From b507cbfdee5a553c6b24d8d69198786cfe11e361 Mon Sep 17 00:00:00 2001
From: Raimo Niskanen This document was written a long time ago. A lot of it is still
- valid, but some things have changed since it was first written.
- Updates of this document are planned for the future. The reader
- is encouraged to also read the
- As of erts version 5.5.3 the driver interface has been extended
- (see
+ As of erts version 5.9 (OTP release R15B) the driver interface
+ has been changed with larger types for the callbacks
+ Old drivers (compiled with an
The
Do not declare the
Do not declare the
This is called when an erlang process has sent data to the
port. The data is pointed to by
This is a special routine invoked with the erlang function
This function is called from
As of erts version 5.5.3 the driver interface has been extended
(see
Old drivers (compiled with an
As of erts version 5.9 old drivers has to be recompiled
+ and has to use the extended interface. They also has to be
+ adjusted to the
+
The driver calls back to the emulator, using the API
functions declared in
A driver can add and later remove drivers.
A driver can monitor a process that does not own a port.
Version management is enabled for drivers that have set the @@ -268,15 +271,203 @@ versions differ, or if the major versions are equal and the minor version used by the driver is greater than the one used by the runtime system.
-The emulator tries to check that a driver that doesn't use the - extended driver interface isn't incompatible when loading it. - It can, however, not make sure that it isn't incompatible. Therefore, - when loading a driver that doesn't use the extended driver - interface, there is a risk that it will be loaded also when - the driver is incompatible. When the driver uses the extended driver - interface, the emulator can verify that it isn't of an incompatible - driver version. You are therefore advised to use the extended driver - interface.
+The emulator will refuse to load a driver that does not use
+ the extended driver interface since,
+ to allow for 64-bit capable drivers,
+ incompatible type changes for the callbacks
+
Therefore it is not enough to just recompile drivers written with + version management for pre-R15B types; the types has to be changed + in the driver suggesting other rewrites especially regarding + size variables. Investigate all warnings when recompiling!
+Also, the API driver functions
+ For erts-5.9 two new integer types
+
+ To not update a driver and just recompile it probably works + when building for a 32-bit machine creating a false sense of security. + Hopefully that will generate many important warnings. + But when recompiling the same driver later on for a 64-bit machine + there will be warnings and almost certainly crashes. + So it is a BAD idea to postpone updating the driver and + not fixing the warnings! +
+
+ When recompiling with
+ Here follows a checklist for rewriting a pre erts-5.9 driver, + most important first. +
+
+ Rewrite driver callback
+
+ Rewrite driver callback
+
+ These changes are essential to not crash the emulator + or worse cause malfunction. + Without them a driver may return garbage in the high 32 bits + to the emulator causing it to build a huge result from random + bytes either crashing on memory allocation or succeeding with + a random result from the driver call. +
+
+ Driver callback
+
+ Driver callback
+
+ Driver callback
+
+ Sane compiler's calling conventions probably make these changes
+ necessary only for a driver to handle data chunks that require
+ 64-bit size fields (mostly larger than 2 GB since that is what
+ an
+ The argument type change is from signed to unsigned which + may cause problems for e.g. loop termination conditions or + error conditions if you just change the types all over the place. +
+
+ The
+ Automatic type casting probably makes these changes necessary only + for a driver that encounters sizes larger than 32 bits. +
+
+ The
+ Many driver API functions has changed argument type
+ and/or return value to
+ This is a change from signed to unsigned which + may cause problems for e.g. loop termination conditions and + error conditions if you just change the types all over the place. +
+An unsigned integer type to be used as
A signed integer type the size of
@@ -332,12 +527,12 @@ typedef struct ErlDrvSysInfo {
erts_version
- A string containing the version number of the runtime system
(the same as returned by
-
erlang:system_info(version) ).
+ erlang:system_info(version) ).
otp_release
- A string containing the OTP release number
(the same as returned by
-
erlang:system_info(otp_release) ).
+ erlang:system_info(otp_release) ).
thread_support
- A value
!= 0 if the runtime system has thread support;
@@ -351,12 +546,12 @@ typedef struct ErlDrvSysInfo {
- The number of async threads in the async thread pool used
by
driver_async()
(the same as returned by
- erlang:system_info(thread_pool_size) ).
+ erlang:system_info(thread_pool_size) ).
scheduler_threads
- The number of scheduler threads used by the runtime system
(the same as returned by
-
erlang:system_info(schedulers) ).
+ erlang:system_info(schedulers) ).
nif_major_version
- The value of
ERL_NIF_MAJOR_VERSION when the runtime system was compiled.
@@ -371,7 +566,7 @@ typedef struct ErlDrvSysInfo {
typedef struct ErlDrvBinary {
- int orig_size;
+ ErlDrvSint orig_size;
char orig_bytes[];
} ErlDrvBinary;
@@ -423,7 +618,7 @@ typedef struct ErlDrvBinary {
-
The ErlDrvData is a handle to driver-specific data,
passed to the driver call-backs. It is a pointer, and is
- most often casted to a specific pointer in the driver.
+ most often type casted to a specific pointer in the driver.
SysIOVec
-
@@ -437,7 +632,7 @@ typedef struct ErlDrvBinary {
typedef struct ErlIOVec {
int vsize;
- int size;
+ ErlDrvSizeT size;
SysIOVec* iov;
ErlDrvBinary** binv;
} ErlIOVec;
@@ -630,7 +825,7 @@ typedef struct ErlIOVec {
- int driver_output(ErlDrvPort port, char *buf, int len)
+ int driver_output(ErlDrvPort port, char *buf, ErlDrvSizeT len)
Send data from driver to port owner
@@ -650,7 +845,7 @@ typedef struct ErlIOVec {
- int driver_output2(ErlDrvPort port, char *hbuf, int hlen, char *buf, int len)
+ int driver_output2(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, char *buf, ErlDrvSizeT len)
Send data and binary data to port owner
@@ -665,7 +860,7 @@ typedef struct ErlIOVec {
- int driver_output_binary(ErlDrvPort port, char *hbuf, int hlen, ErlDrvBinary* bin, int offset, int len)
+ int driver_output_binary(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, ErlDrvBinary* bin, ErlDrvSizeT offset, ErlDrvSizeT len)
Send data from a driver binary to port owner
@@ -688,7 +883,7 @@ typedef struct ErlIOVec {
- int driver_outputv(ErlDrvPort port, char* hbuf, int hlen, ErlIOVec *ev, int skip)
+ int driver_outputv(ErlDrvPort port, char* hbuf, ErlDrvSizeT hlen, ErlIOVec *ev, ErlDrvSizeT skip)
Send vectorized data to port owner
@@ -711,7 +906,7 @@ typedef struct ErlIOVec {
- int driver_vec_to_buf(ErlIOVec *ev, char *buf, int len)
+ ErlDrvSizeT driver_vec_to_buf(ErlIOVec *ev, char *buf, ErlDrvSizeT len)
Collect data segments into a buffer
@@ -738,7 +933,7 @@ typedef struct ErlIOVec {
time parameter is the time in milliseconds before the
timer expires.
When the timer reaches 0 and expires, the driver entry
- function timeout is called.
+ function timeout is called.
Note that there is only one timer on each driver instance;
setting a new timer will replace an older one.
Return value is 0 (-1 only when the timeout driver
@@ -832,7 +1027,7 @@ typedef struct ErlIOVec {
- void * driver_alloc(size_t size)
+ void * driver_alloc(ErlDrvSizeT size)
Allocate memory
@@ -846,7 +1041,7 @@ typedef struct ErlIOVec {
- void * driver_realloc(void *ptr, size_t size)
+ void * driver_realloc(void *ptr, ErlDrvSizeT size)
Resize an allocated memory block
@@ -872,7 +1067,7 @@ typedef struct ErlIOVec {
- ErlDrvBinary* driver_alloc_binary(int size)
+ ErlDrvBinary* driver_alloc_binary(ErlDrvSizeT size)
Allocate a driver binary
@@ -892,7 +1087,7 @@ typedef struct ErlIOVec {
- ErlDrvBinary* driver_realloc_binary(ErlDrvBinary *bin, int size)
+ ErlDrvBinary* driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size)
Resize a driver binary
@@ -958,7 +1153,7 @@ typedef struct ErlIOVec {
- int driver_enq(ErlDrvPort port, char* buf, int len)
+ int driver_enq(ErlDrvPort port, char* buf, ErlDrvSizeT len)
Enqueue data in the driver queue
@@ -982,7 +1177,7 @@ typedef struct ErlIOVec {
- int driver_pushq(ErlDrvPort port, char* buf, int len)
+ int driver_pushq(ErlDrvPort port, char* buf, ErlDrvSizeT len)
Push data at the head of the driver queue
@@ -997,7 +1192,7 @@ typedef struct ErlIOVec {
- int driver_deq(ErlDrvPort port, int size)
+ ErlDrvSizeT driver_deq(ErlDrvPort port, ErlDrvSizeT size)
Dequeue data from the head of the driver queue
@@ -1013,7 +1208,7 @@ typedef struct ErlIOVec {
- int driver_sizeq(ErlDrvPort port)
+ ErlDrvSizeT driver_sizeq(ErlDrvPort port)
Return the size of the driver queue
@@ -1026,7 +1221,7 @@ typedef struct ErlIOVec {
- int driver_enq_bin(ErlDrvPort port, ErlDrvBinary *bin, int offset, int len)
+ int driver_enq_bin(ErlDrvPort port, ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)
Enqueue binary in the driver queue
@@ -1043,7 +1238,7 @@ typedef struct ErlIOVec {
- int driver_pushq_bin(ErlDrvPort port, ErlDrvBinary *bin, int offset, int len)
+ int driver_pushq_bin(ErlDrvPort port, ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)
Push binary at the head of the driver queue
@@ -1059,6 +1254,28 @@ typedef struct ErlIOVec {
The return value is 0.
+
+ ErlDrvSizeT driver_peekqv(ErlDrvPort port, ErlIOVec *ev)
+ Get the driver queue as an IO vector
+
+
+
+ This function retrieves the driver queue into a supplied
+ ErlIOVec ev . It also returns the queue size.
+ This is one of two ways to get data out of the queue.
+
+
+ If ev is NULL all ones i.e. -1 type cast to
+ ErlDrvSizeT is returned.
+
+ Nothing is removed from the queue by this function, that must be done
+ with driver_deq .
+ This function can be called from an arbitrary thread if a
+ port data lock
+ associated with the port is locked by the calling
+ thread during the call.
+
+
SysIOVec* driver_peekq(ErlDrvPort port, int *vlen)
Get the driver queue as a vector
@@ -1066,7 +1283,7 @@ typedef struct ErlIOVec {
This function retrieves the driver queue as a pointer to an
array of SysIOVec s. It also returns the number of
- elements in vlen . This is the only way to get data
+ elements in vlen . This is one of two ways to get data
out of the queue.
Nothing is removed from the queue by this function, that must be done
with driver_deq .
@@ -1079,7 +1296,7 @@ typedef struct ErlIOVec {
- int driver_enqv(ErlDrvPort port, ErlIOVec *ev, int skip)
+ int driver_enqv(ErlDrvPort port, ErlIOVec *ev, ErlDrvSizeT skip)
Enqueue vector in the driver queue
@@ -1095,7 +1312,7 @@ typedef struct ErlIOVec {
- int driver_pushqv(ErlDrvPort port, ErlIOVec *ev, int skip)
+ int driver_pushqv(ErlDrvPort port, ErlIOVec *ev, ErlDrvSizeT skip)
Push vector at the head of the driver queue
@@ -1494,7 +1711,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
term encoded with the
external format ,
i.e., a term that has been encoded by
- erlang:term_to_binary ,
+ erlang:term_to_binary ,
erl_interface , etc.
For example, if binp is a pointer to an ErlDrvBinary
that contains the term {17, 4711} encoded with the
@@ -1694,7 +1911,7 @@ ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
The driver defined handle is normally created in the
driver start call-back
when a port is created via
- erlang:open_port/2 .
+ erlang:open_port/2 .
The caller of
An Alternative Distribution Driver (ERTS User's Guide Ch. 3)
-- cgit v1.2.3