driver_entry(3erl) | C Library Functions | driver_entry(3erl) |
driver_entry - The driver-entry structure used by Erlang drivers.
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.
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.
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.
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;
If an error code is returned, the port is not started.
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.
The driver is only unloaded as a result of calling erl_ddll:unload_driver/1, or when the emulator halts.
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.
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.
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.
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.
erts 10.2.4 | Ericsson AB |