driver_entry
C library
driver_entry
Library summary
The driver-entry structure used by erlang drivers.
Description
Use this functionality with extreme care!
A driver callback is executed as a direct extension of the native code of the VM. Execution is not made in a safe environment. The VM can not provide the same services as provided when executing Erlang code, such as preemptive scheduling or memory protection. If the driver callback function doesn't behave well, the whole VM will misbehave.
A driver callback that crash will crash the whole VM.
An erroneously implemented driver callback might cause a VM internal state inconsistency which may cause a crash of the VM, or miscellaneous misbehaviors of the VM at any point after the call to the driver callback.
A driver callback that do
lengthy work
before returning will degrade responsiveness of the VM, and may cause miscellaneous strange behaviors. Such strange behaviors include, but are not limited to, extreme memory usage, and bad load balancing between schedulers. Strange behaviors that might occur due to lengthy work may also vary between OTP releases.
As of erts version 5.9 (OTP release R15B) the driver interface has been changed with larger types for the callbacks output
, control
and call
. See driver version management
in erl_driver
.
Old drivers (compiled with an erl_driver.h
from an earlier erts version than 5.9) have to be updated and have to use the extended interface (with version management
).
The driver_entry
structure is a C struct that all erlang drivers define. It contains entry points for the erlang driver that are called by the erlang emulator when erlang code accesses the driver.
The erl_driver
driver API functions need a port handle that identifies the driver instance (and the port in the emulator). This is only passed to the start
function, but not to the other functions. The start
function returns a driver-defined handle that is passed to the other functions. A common practice is to have the start
function allocate some application-defined structure and stash the port
handle in it, to use it later with the driver API functions.
The driver call-back functions are called synchronously from the erlang emulator. If they take too long before completing, they can cause timeouts in the emulator. Use the queue or asynchronous calls if necessary, since the emulator must be responsive.
The driver structure contains the name of the driver and some 15 function pointers. These pointers are called at different times by the emulator.
The only exported function from the driver is driver_init
. This function returns the driver_entry
structure that points to the other functions in the driver. The driver_init
function is declared with a macro DRIVER_INIT(drivername)
. (This is because different OS's have different names for it.)
When writing a driver in C++, the driver entry should be of "C"
linkage. One way to do this is to put this line somewhere before the driver entry: extern "C" DRIVER_INIT(drivername);
.
When the driver has passed the driver_entry
over to the emulator, the driver is not allowed to modify the driver_entry
.
If compiling a driver for static inclusion via --enable-static-drivers you have to define STATIC_ERLANG_DRIVER before the DRIVER_INIT declaration.
Do not declare the driver_entry
const
. This since the emulator needs to modify the handle
, and the handle2
fields. A statically allocated, and const
declared driver_entry
may be located in read only memory which will cause the emulator to crash.
Data types
- ErlDrvEntry
-
typedef struct erl_drv_entry { int (*init)(void); /* called at system start up for statically linked drivers, and after loading for dynamically loaded drivers */ #ifndef ERL_SYS_DRV ErlDrvData (*start)(ErlDrvPort port, char *command); /* called when open_port/2 is invoked. return value -1 means failure. */ #else ErlDrvData (*start)(ErlDrvPort port, char *command, SysDriverOpts* opts); /* special options, only for system driver */ #endif void (*stop)(ErlDrvData drv_data); /* called when port is closed, and when the emulator is halted. */ void (*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT len); /* called when we have output from erlang to the port */ void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event); /* called when we have input from one of the driver's handles */ void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event); /* called when output is possible to one of the driver's handles */ char *driver_name; /* name supplied as command in open_port XXX ? */ void (*finish)(void); /* called before unloading the driver - DYNAMIC DRIVERS ONLY */ void *handle; /* Reserved -- Used by emulator internally */ ErlDrvSSizeT (*control)(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen); /* "ioctl" for drivers - invoked by port_control/3 */ void (*timeout)(ErlDrvData drv_data); /* Handling of timeout in driver */ void (*outputv)(ErlDrvData drv_data, ErlIOVec *ev); /* called when we have output from erlang to the port */ void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data); void (*flush)(ErlDrvData drv_data); /* called when the port is about to be closed, and there is data in the driver queue that needs to be flushed before 'stop' can be called */ ErlDrvSSizeT (*call)(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen, unsigned int *flags); /* Works mostly like 'control', a synchronous call into the driver. */ void (*event)(ErlDrvData drv_data, ErlDrvEvent event, ErlDrvEventData event_data); /* Called when an event selected by driver_event() has occurred */ int extended_marker; /* ERL_DRV_EXTENDED_MARKER */ int major_version; /* ERL_DRV_EXTENDED_MAJOR_VERSION */ int minor_version; /* ERL_DRV_EXTENDED_MINOR_VERSION */ int driver_flags; /* ERL_DRV_FLAGs */ void *handle2; /* Reserved -- Used by emulator internally */ void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor); /* Called when a process monitor fires */ void (*stop_select)(ErlDrvEvent event, void* reserved); /* Called to close an event object */ } ErlDrvEntry;
- int (*init)(void)
-
This is called directly after the driver has been loaded by
erl_ddll:load_driver/2
. (Actually when the driver is added to the driver list.) The driver should return 0, or if the driver can't initialize, -1. - ErlDrvData (*start)(ErlDrvPort port, char* command)
-
This is called when the driver is instantiated, when
open_port/2
is called. The driver should return a number >= 0 or a pointer, or if the driver can't be started, one of three error codes should be returned:ERL_DRV_ERROR_GENERAL - general error, no error code
ERL_DRV_ERROR_ERRNO - error with error code in
errno
ERL_DRV_ERROR_BADARG - error, badarg
If an error code is returned, the port isn't started.
- void (*stop)(ErlDrvData drv_data)
-
This is called when the port is closed, with
port_close/1
orPort ! {self(), close}
. Note that terminating the port owner process also closes the port. Ifdrv_data
is a pointer to memory allocated instart
, thenstop
is the place to deallocate that memory. - void (*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)
-
This is called when an erlang process has sent data to the port. The data is pointed to by
buf
, and islen
bytes. Data is sent to the port withPort ! {self(), {command, Data}}
, or withport_command/2
. Depending on how the port was opened, it should be either a list of integers 0...255 or a binary. Seeopen_port/3
andport_command/2
. - void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event)
- void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event)
-
This is called when a driver event (given in the
event
parameter) is signaled. This is used to help asynchronous drivers "wake up" when something happens.On unix the
event
is a pipe or socket handle (or something that theselect
system call understands).On Windows the
event
is an Event or Semaphore (or something that theWaitForMultipleObjects
API function understands). (Some trickery in the emulator allows more than the built-in limit of 64Events
to be used.)On Enea OSE the
event
is one or more signals that can be retrieved usingerl_drv_ose_get_signal
.To use this with threads and asynchronous routines, create a pipe on unix, an Event on Windows or a unique signal number on Enea OSE. When the routine completes, write to the pipe (use
SetEvent
on Windows or send a message to the emulator process on Enea OSE), this will make the emulator callready_input
orready_output
.Spurious events may happen. That is, calls to
ready_input
orready_output
even though no real events are signaled. In reality it should be rare (and OS dependant), but a robust driver must nevertheless be able to handle such cases. - char *driver_name
-
This is the name of the driver, it must correspond to the atom used in
open_port
, and the name of the driver library file (without the extension). - void (*finish)(void)
-
This function is called by the
erl_ddll
driver when the driver is unloaded. (It is only called in dynamic drivers.)The driver is only unloaded as a result of calling
unload_driver/1
, or when the emulator halts. - void *handle
-
This field is reserved for the emulator's internal use. The emulator will modify this field; therefore, it is important that the
driver_entry
isn't declaredconst
. - ErlDrvSSizeT (*control)(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen)
-
This is a special routine invoked with the erlang function
port_control/3
. It works a little like an "ioctl" for erlang drivers. The data given toport_control/3
arrives inbuf
andlen
. The driver may send data back, using*rbuf
andrlen
.This is the fastest way of calling a driver and get a response. It won't make any context switch in the erlang emulator, and requires no message passing. It is suitable for calling C function to get faster execution, when erlang is too slow.
If the driver wants to return data, it should return it in
rbuf
. Whencontrol
is called,*rbuf
points to a default buffer ofrlen
bytes, which can be used to return data. Data is returned different depending on the port control flags (those that are set withset_port_control_flags
).If the flag is set to
PORT_CONTROL_FLAG_BINARY
, a binary will be returned. Small binaries can be returned by writing the raw data into the default buffer. A binary can also be returned by setting*rbuf
to point to a binary allocated withdriver_alloc_binary
. This binary will be freed automatically aftercontrol
has returned. The driver can retain the binary for read only access withdriver_binary_inc_refc
to be freed later withdriver_free_binary
. It is never allowed to alter the binary aftercontrol
has returned. If*rbuf
is set to NULL, an empty list will be returned.If the flag is set to
0
, data is returned as a list of integers. Either use the default buffer or set*rbuf
to point to a larger buffer allocated withdriver_alloc
. The buffer will be freed automatically aftercontrol
has returned.Using binaries is faster if more than a few bytes are returned.
The return value is the number of bytes returned in
*rbuf
. - void (*timeout)(ErlDrvData drv_data)
-
This function is called any time after the driver's timer reaches 0. The timer is activated with
driver_set_timer
. There are no priorities or ordering among drivers, so if several drivers time out at the same time, any one of them is called first. - void (*outputv)(ErlDrvData drv_data, ErlIOVec *ev)
-
This function is called whenever the port is written to. If it is
NULL
, theoutput
function is called instead. This function is faster thanoutput
, because it takes anErlIOVec
directly, which requires no copying of the data. The port should be in binary mode, seeopen_port/2
.The
ErlIOVec
contains both aSysIOVec
, suitable forwritev
, and one or more binaries. If these binaries should be retained, when the driver returns fromoutputv
, they can be queued (usingdriver_enq_bin
for instance), or if they are kept in a static or global variable, the reference counter can be incremented. - void (*ready_async)(ErlDrvData drv_data, ErlDrvThreadData thread_data)
-
This function is called after an asynchronous call has completed. The asynchronous call is started with
driver_async
. This function is called from the erlang emulator thread, as opposed to the asynchronous function, which is called in some thread (if multithreading is enabled). - ErlDrvSSizeT (*call)(ErlDrvData drv_data, unsigned int command, char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen, unsigned int *flags)
-
This function is called from
erlang:port_call/3
. It works a lot like thecontrol
call-back, but uses the external term format for input and output.command
is an integer, obtained from the call from erlang (the second argument toerlang:port_call/3
).buf
andlen
provide the arguments to the call (the third argument toerlang:port_call/3
). They can be decoded usingei
functions.rbuf
points to a return buffer,rlen
bytes long. The return data should be a valid erlang term in the external (binary) format. This is converted to an erlang term and returned byerlang:port_call/3
to the caller. If more space thanrlen
bytes is needed to return data,*rbuf
can be set to memory allocated withdriver_alloc
. This memory will be freed automatically aftercall
has returned.The return value is the number of bytes returned in
*rbuf
. IfERL_DRV_ERROR_GENERAL
is returned (or in fact, anything < 0),erlang:port_call/3
will throw aBAD_ARG
. - void (*event)(ErlDrvData drv_data, ErlDrvEvent event, ErlDrvEventData event_data)
-
Intentionally left undocumented.
- int extended_marker
-
This field should either be equal to
ERL_DRV_EXTENDED_MARKER
or0
. An old driver (not aware of the extended driver interface) should set this field to0
. If this field is equal to0
, all the fields following this field also have to be0
, orNULL
in case it is a pointer field. - int major_version
-
This field should equal
ERL_DRV_EXTENDED_MAJOR_VERSION
if theextended_marker
field equalsERL_DRV_EXTENDED_MARKER
. - int minor_version
-
This field should equal
ERL_DRV_EXTENDED_MINOR_VERSION
if theextended_marker
field equalsERL_DRV_EXTENDED_MARKER
. - int driver_flags
-
This field is used to pass driver capability and other information to the runtime system. If the
extended_marker
field equalsERL_DRV_EXTENDED_MARKER
, it should contain0
or driver flags (ERL_DRV_FLAG_*
) ored bitwise. Currently the following driver flags exist:ERL_DRV_FLAG_USE_PORT_LOCKING
- The runtime system will use port level locking on all ports executing this driver instead of driver level locking when the driver is run in a runtime system with SMP support. For more information see the
erl_driver
documentation. ERL_DRV_FLAG_SOFT_BUSY
- Marks that driver instances can handle being called in the
output
and/oroutputv
callbacks even though a driver instance has marked itself as busy (seeset_busy_port()
). Since erts version 5.7.4 this flag is required for drivers used by the Erlang distribution (the behaviour has always been required by drivers used by the distribution). ERL_DRV_FLAG_NO_BUSY_MSGQ
- Disable busy port message queue functionality. For more information, see the documentation of the
erl_drv_busy_msgq_limits()
function.
- void *handle2
-
This field is reserved for the emulator's internal use. The emulator will modify this field; therefore, it is important that the
driver_entry
isn't declaredconst
. - void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor)
-
This callback is called when a monitored process exits. The
drv_data
is the data associated with the port for which the process is monitored (usingdriver_monitor_process
) and themonitor
corresponds to theErlDrvMonitor
structure filled in when creating the monitor. The driver interface functiondriver_get_monitored_process
can be used to retrieve the process id of the exiting process as anErlDrvTermData
. - void (*stop_select)(ErlDrvEvent event, void* reserved)
-
This function is called on behalf of
driver_select
when it is safe to close an event object.A typical implementation on Unix is to do
close((int)event)
.Argument
reserved
is intended for future use and should be ignored.In contrast to most of the other call-back functions,
stop_select
is called independent of any port. NoErlDrvData
argument is passed to the function. No driver lock or port lock is guaranteed to be held. The port that calleddriver_select
might even be closed at the timestop_select
is called. But it could also be the case thatstop_select
is called directly bydriver_select
.It is not allowed to call any functions in the
driver API
fromstop_select
. This strict limitation is due to the volatile context thatstop_select
may be called.
See also
erl_driver(3)
, erl_ddll(3)
, erlang(3)
, kernel(3)
© 2010–2017 Ericsson AB
Licensed under the Apache License, Version 2.0.