driver_entry(3erl) C Library Functions driver_entry(3erl)
NAME
driver_entry - The driver-entry structure used by Erlang drivers.
DESCRIPTION
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 miscel-
laneous misbehaviors of the VM at any point after the call to the
driver callback.
* A driver callback doing lengthy work before returning degrades re-
sponsiveness of the VM, and can cause miscellaneous strange behav-
iors. Such strange behaviors include, but are not limited to, ex-
treme 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 de-
fine. 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 func-
tion returns a driver-defined handle that is passed to the other func-
tions. 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 func-
tion returns the driver_entry structure that points to the other func-
tions 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:
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 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_com-
mand/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 sig-
naled. 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 trick-
ery 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_out-
put 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 er-
lang: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:un-
load_driver/1, or when the emulator halts.
void *handle:
This field is reserved for the emulator's internal use. The emula-
tor will modify this field, so it is important that the driver_en-
try 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 mes-
sage 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 differ-
ently 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 re-
turned. 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 order-
ing 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 asynchro-
nous 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 sec-
ond 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), er-
lang: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 ex-
tended_marker equals ERL_DRV_EXTENDED_MARKER.
int minor_version:
This field is to equal ERL_DRV_EXTENDED_MINOR_VERSION if field ex-
tended_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_EX-
TENDED_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 informa-
tion, 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 emula-
tor 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 as-
sociated 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_moni-
tored_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.
SEE ALSO
erl_driver(3erl), erlang(3erl), erl_ddll(3erl)
Ericsson AB erts 11.0.2 driver_entry(3erl)