Maxim Sokhatsky
[email protected]
Synrc Research Center, s.r.o.


LING — the Erlang/OTP compatible virtual machine is able to boot in 50ms under no OS within Xen hypervisor or raw hardware. This chapter will guide you through C interfaces of the LING internals. The purpose of this document is to deep inside LING as fast as possible.


The reference to the process that created the timer if msg is a pointer term or 0 if msg is not. The reference is needed to update pending_timers count of the sending process and and destroy it if is a zombie already. The fire function that is called when the timer expire. It is introduced to use the same code for TCP/IP and potentially other timers. When enveloped is true — send {timeout,TRef,Msg} message, false — send just Msg.


int etimer_add(uint64_t ref_id, uint64_t timeout, term_t dst, term_t msg, proc_t *sender, int enveloped); uint64_t etimer_closest_timeout(void); void etimer_expired(uint64_t now); int etimer_cancel(uint64_t ref_id, int64_t *left_ns); void etimer_cancel_by_receiver(term_t dst); int etimer_read(uint64_t ref_id, int64_t *left_ns); void etimer_fill_root_regs(proc_t *sender, region_t *regs, int nr_expected);

Message Queue

Message Queue is an essential part of message-passing reactive systems. As soon as Erlang virual machine is built upon message-passing framework, the concept of a message and a queue are central for the system. All messages are being added to the head of the queue atomically. The node is driven by single processor.


term_t msg_queue_current(msg_queue_t *mq); int msg_queue_len(msg_queue_t *mq); void msg_queue_next(msg_queue_t *mq); void msg_queue_reset(msg_queue_t *mq); void msg_queue_drop(msg_queue_t *mq); void msg_queue_done(msg_queue_t *mq); void msg_queue_mark(msg_queue_t *mq,uint32_t *mark); void msg_queue_restore(msg_queue_t *mq,uint32_t *mark); int msg_queue_push_N(msg_queue_t *mq,term_t t); void msg_queue_fill_root_regs(msg_queue_t *mq, region_t *r); void msg_queue_init(msg_queue_t *mq, uint32_t *buf_starts, uint32_t *buf_ends);


Monitors — are the unidirectional way of tracking processes. It can be stacked. The other primitives of the Erlang VM are links which are unstackable bidirectional form built on top of two monitors.


int monitor(uint64_t ref_id,term_t pid1,term_t pid2); int demonitor(uint64_t ref_id,term_t pid1); int notify_monitors_N(term_t late,term_t reason); term_t list_monitored_by(term_t pid2,heap_t *hp); term_t list_monitors(term_t pid1, heap_t *hp);


Console, Disk, UDP, TCP, DNS and other drivers are implement Outlet async interface. Outlet gives you an extensibility power to LING. It is more like BEAM ports interface from the client point of view, but from the other hand it is more like NIF due to C linkage.


typedef outlet_t *(*outlet_factory_func_t) (proc_t *cont_proc, uint32_t bit_opts); outlet_factory_func_t outlet_resolve_driver(term_t name); outlet_t *outlet_make_N(outlet_vtab_t *vtab, proc_t *cont_proc, int32_t bit_opts, uint32_t extra); void outlet_new_data(outlet_t *ol, uint8_t *data, int dlen); term_t outlet_control(outlet_t *ol, uint32_t op, uint8_t *data, int dlen, term_t reply_to, heap_t *hp); int outlet_attach(outlet_t *ol); void outlet_detach(outlet_t *ol); void outlet_destroy_private(outlet_t *ol); outlet_t *outlet_lookup(term_t oid); outlet_t *outlet_lookup_by_name(term_t name); uint8_t *outlet_get_send_buffer(outlet_t *ol,int len); int outlet_send(outlet_t *ol,int len,term_t reply_to); term_t outlet_all(heap_t *hp); void outlet_register(outlet_t *ol, term_t name); void outlet_unregister(outlet_t *ol); void outlet_pass_new_data(outlet_t *ol, uint8_t *data, int dlen); int outlet_signal_exit_N(outlet_t *ol, term_t src, term_t reason); void outlet_close(outlet_t *ol, term_t reason); void outlet_destroy(outlet_t *ol); int outlet_notify_owner(outlet_t *ol, term_t what);