driver_entry
Warning!
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 cannot provide the same services as provided when executing Erlang code, such as pre-emptive scheduling or memory protection. If the driver callback function does not behave well, the whole VM will misbehave.
-
A driver callback that crash will crash the whole VM.
-
An erroneously implemented driver callback can cause a VM internal state inconsistency, which can 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 doing lengthy work before returning degrades responsiveness of the VM, and can 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 can occur because of lengthy work can also vary between Erlang/OTP releases.
As from ERTS 5.9 (Erlang/OTP R15B) the driver interface
has been changed with larger types for the callbacks
output
,
control
, and
call
.
See driver
version management in
erl_driver
.
Note!
Old drivers (compiled with an erl_driver.h
from an
ERTS version earlier than 5.9) must 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,
which 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 callback functions are called synchronously from the Erlang emulator. If they take too long before completing, they can cause time-outs in the emulator. Use the queue or asynchronous calls if necessary, as the emulator must be responsive.
The driver structure contains the driver name and some 15 function pointers, which 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
operating systems have different names for it.)
When writing a driver in C++, the driver entry is to be of
"C"
linkage. One way to do this is to put the
following 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 through
--enable-static-drivers
, you must define
STATIC_ERLANG_DRIVER
before the DRIVER_INIT
declaration.
Note!
Do not declare the driver_entry
const
.
This because the emulator must
modify the handle
and the handle2
fields. A statically allocated, and const
-declared
driver_entry
can be located in
read-only memory, which causes the emulator to crash.
Data Types
ErlDrvEntry
typedef struct erl_drv_entry { int (*init)(void); /* Called at system startup 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 erlang:open_port/2 */ 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 time-out 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 must 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* unused_event_callback; 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)
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 is to return 0
, or, if
the driver cannot initialize, -1
.
ErlDrvData (*start)(ErlDrvPort port, char* command)
Called when the driver is instantiated, when
erlang:open_port/2
is called.
The driver is to return a number >= 0 or a pointer, or, if the
driver cannot be started, one of three error codes:
ERL_DRV_ERROR_GENERAL
ERL_DRV_ERROR_ERRNO
errno
ERL_DRV_ERROR_BADARG
badarg
If an error code is returned, the port is not started.
void (*stop)(ErlDrvData drv_data)
Called when the port is closed, with
erlang:port_close/1
or Port ! {self(), close}
.
Notice that terminating the port owner process also closes the
port. If drv_data
is a pointer to memory allocated in
start
, then stop
is the place to deallocate that
memory.
void (*output)(ErlDrvData drv_data, char *buf, ErlDrvSizeT len)
Called when an Erlang process has sent data to the port. The data is
pointed to by buf
, and is len
bytes. Data is sent to
the port with Port ! {self(), {command, Data}}
or with
erlang:port_command/2
. Depending on how the port was
opened, it is to be either a list of integers 0...255
or a
binary. See
erlang:open_port/2
and
erlang:port_command/2
.
void (*ready_input)(ErlDrvData drv_data, ErlDrvEvent event)
void (*ready_output)(ErlDrvData drv_data, ErlDrvEvent event)
Called when a driver event (specified in parameter
event
) is signaled. This is used to help
asynchronous drivers "wake up" when something occurs.
On Unix the event
is a pipe or socket handle (or
something that the select
system call understands).
On Windows the event
is an Event
or Semaphore
(or something that the WaitForMultipleObjects
API
function understands). (Some trickery in the emulator allows
more than the built-in limit of 64 Events
to be used.)
To use this with threads and asynchronous routines, create a
pipe on Unix and an Event
on Windows. When the routine
completes, write to the pipe (use SetEvent
on
Windows), this makes the emulator call
ready_input
or ready_output
.
False events can occur. That is, calls to ready_input
or ready_output
although no real events are signaled. In
reality, it is rare (and OS-dependant), but a robust driver
must nevertheless be able to handle such cases.
char *driver_name
The driver name. It must correspond to the atom used in
erlang:open_port/2
, and the name of the driver
library file (without the extension).
void (*finish)(void)
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
erl_ddll: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, so it is important
that the driver_entry
is not declared const
.
ErlDrvSSizeT (*control)(ErlDrvData drv_data, unsigned int command,
char *buf, ErlDrvSizeT len, char **rbuf, ErlDrvSizeT rlen)
A special routine invoked with
erlang:port_control/3
.
It works a little like an "ioctl" for
Erlang drivers. The data specified to port_control/3
arrives in buf
and len
. The driver can send
data back, using *rbuf
and rlen
.
This is the fastest way of calling a driver and get a response. It makes no 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 is to return it in
rbuf
. When control
is called,
*rbuf
points to a default buffer of rlen
bytes, which
can be used to return data. Data is returned differently depending on
the port control flags (those that are set with
erl_driver:set_port_control_flags
).
If the flag is set to PORT_CONTROL_FLAG_BINARY
,
a binary is 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 with
erl_driver:driver_alloc_binary
.
This binary is freed automatically after control
has returned.
The driver can retain the binary for read only access with
erl_driver:driver_binary_inc_refc
to be freed later
with
erl_driver:driver_free_binary
.
It is never allowed to change the binary after control
has
returned. If *rbuf
is set to NULL
, an empty list is
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 with
erl_driver:driver_alloc
. The
buffer is freed automatically after control
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)
Called any time after the driver's timer reaches 0
.
The timer is activated with
erl_driver:driver_set_timer
. No priorities or
ordering exist among drivers, so if several drivers time out at
the same time, anyone of them is called first.
void (*outputv)(ErlDrvData drv_data, ErlIOVec *ev)
Called whenever the port is written to. If
it is NULL
, the output
function is called
instead. This function is faster than output
, as
it takes an ErlIOVec
directly, which requires no
copying of the data. The port is to be in binary mode, see
erlang:open_port/2
.
ErlIOVec
contains both a SysIOVec
,
suitable for writev
, and one or more binaries. If
these binaries are to be retained when the driver returns
from outputv
, they can be queued (using, for example,
erl_driver:driver_enq_bin
)
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)
Called after an asynchronous call has completed.
The asynchronous call is started with
erl_driver:driver_async
.
This function is called from the Erlang emulator thread, as
opposed to the asynchronous function, which is called in
some thread (if multi-threading is enabled).
void (*flush)(ErlDrvData drv_data)
Called when the port is about to be closed, and there is data in the driver queue that must 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)
Called from
erlang:port_call/3
.
It works a lot like the control
callback, but uses the
external term format for input and output.
command
is an integer, obtained from the call from
Erlang (the second argument to erlang:port_call/3
).
buf
and len
provide the arguments to the call
(the third argument to erlang:port_call/3
). They can
be decoded using ei
functions.
rbuf
points to a return buffer, rlen
bytes
long. The return data is to be a valid Erlang term in the
external (binary) format. This is converted to an Erlang
term and returned by erlang:port_call/3
to the
caller. If more space than rlen
bytes is needed to
return data, *rbuf
can be set to memory allocated with
erl_driver:driver_alloc
.
This memory is freed automatically after call
has returned.
The return value is the number of bytes returned in
*rbuf
. If ERL_DRV_ERROR_GENERAL
is returned
(or in fact, anything < 0), erlang:port_call/3
throws a BAD_ARG
.
void (*event)(ErlDrvData drv_data, ErlDrvEvent event,
ErlDrvEventData event_data)
Intentionally left undocumented.
int extended_marker
This field is either to be equal to ERL_DRV_EXTENDED_MARKER
or 0
. An old driver (not aware of the extended driver
interface) is to set this field to 0
. If this field is
0
, all the following fields must also be 0
,
or NULL
if it is a pointer field.
int major_version
This field is to equal ERL_DRV_EXTENDED_MAJOR_VERSION
if
field extended_marker
equals
ERL_DRV_EXTENDED_MARKER
.
int minor_version
This field is to equal ERL_DRV_EXTENDED_MINOR_VERSION
if
field extended_marker
equals
ERL_DRV_EXTENDED_MARKER
.
int driver_flags
This field is used to pass driver capability and other
information to the runtime system. If
field extended_marker
equals ERL_DRV_EXTENDED_MARKER
,
it is to contain 0
or driver flags (ERL_DRV_FLAG_*
)
OR'ed bitwise. The following driver flags exist:
ERL_DRV_FLAG_USE_PORT_LOCKING
The runtime system uses 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
erl_driver
.
ERL_DRV_FLAG_SOFT_BUSY
Marks that driver instances can handle being called
in the output
and/or
outputv
callbacks
although a driver instance has marked itself as busy (see
erl_driver:set_busy_port
).
As from ERTS 5.7.4 this flag is required for drivers used
by the Erlang distribution (the behavior has always been
required by drivers used by the distribution).
ERL_DRV_FLAG_NO_BUSY_MSGQ
Disables busy port message queue functionality. For
more information, see
erl_driver:erl_drv_busy_msgq_limits
.
ERL_DRV_FLAG_USE_INIT_ACK
When this flag is specified, the linked-in driver must manually
acknowledge that the port has been successfully started using
erl_driver:erl_drv_init_ack()
.
This allows the implementor to make the
erlang:open_port
exit with badarg
after some
initial asynchronous initialization has been done.
void *handle2
This field is reserved for the emulator's internal use. The
emulator modifies this field, so it is important
that the driver_entry
is not declared const
.
void (*process_exit)(ErlDrvData drv_data, ErlDrvMonitor *monitor)
Called when a monitored process exits. The
drv_data
is the data associated with the port for which
the process is monitored (using
erl_driver:driver_monitor_process
)
and the monitor
corresponds to the ErlDrvMonitor
structure filled
in when creating the monitor. The driver interface function
erl_driver:driver_get_monitored_process
can be used to retrieve the process ID of the exiting process as
an ErlDrvTermData
.
void (*stop_select)(ErlDrvEvent event, void* reserved)
Called on behalf of
erl_driver: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 is to be
ignored.
In contrast to most of the other callback functions,
stop_select
is called independent of any port. No
ErlDrvData
argument is passed to the function. No
driver lock or port lock is guaranteed to be held. The port that
called driver_select
can even be closed at the
time stop_select
is called. But it can also be
the case that stop_select
is called directly by
erl_driver:driver_select
.
It is not allowed to call any functions in the
driver API from
stop_select
. This strict limitation is because the
volatile context that stop_select
can be called.