aboutsummaryrefslogtreecommitdiffstats
path: root/erts/doc/src/erl_driver.xml
diff options
context:
space:
mode:
authorErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
committerErlang/OTP <[email protected]>2009-11-20 14:54:40 +0000
commit84adefa331c4159d432d22840663c38f155cd4c1 (patch)
treebff9a9c66adda4df2106dfd0e5c053ab182a12bd /erts/doc/src/erl_driver.xml
downloadotp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz
otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2
otp-84adefa331c4159d432d22840663c38f155cd4c1.zip
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/doc/src/erl_driver.xml')
-rw-r--r--erts/doc/src/erl_driver.xml2465
1 files changed, 2465 insertions, 0 deletions
diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml
new file mode 100644
index 0000000000..0b11f4bbcb
--- /dev/null
+++ b/erts/doc/src/erl_driver.xml
@@ -0,0 +1,2465 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE cref SYSTEM "cref.dtd">
+
+<cref>
+ <header>
+ <copyright>
+ <year>2001</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>erl_driver</title>
+ <prepared>Jakob Cederlund</prepared>
+ <responsible>Jakob Cederlund</responsible>
+ <docno>1</docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2000-11-27</date>
+ <rev>PA1</rev>
+ <file>erl_driver.xml</file>
+ </header>
+ <lib>erl_driver</lib>
+ <libsummary>API functions for an Erlang driver</libsummary>
+ <description>
+ <p>As of erts version 5.5.3 the driver interface has been extended
+ (see <seealso marker="driver_entry#extended_marker">extended marker</seealso>).
+ The extended interface introduce
+ <seealso marker="erl_driver#version_management">version management</seealso>,
+ the possibility to pass capability flags
+ (see <seealso marker="driver_entry#driver_flags">driver flags</seealso>)
+ to the runtime system at driver initialization, and some new
+ driver API functions. </p>
+ <note>
+ <p>Old drivers (compiled with an <c>erl_driver.h</c> from an
+ earlier erts version than 5.5.3) have to be recompiled
+ (but does not have to use the extended interface).</p>
+ </note>
+ <p>The driver calls back to the emulator, using the API
+ functions declared in <c>erl_driver.h</c>. They are used for
+ outputting data from the driver, using timers, etc.</p>
+ <p>A driver is a library with a set of function that the emulator
+ calls, in response to Erlang functions and message
+ sending. There may be multiple instances of a driver, each
+ instance is connected to an Erlang port. Every port has a port
+ owner process. Communication with the port is normally done
+ through the port owner process.</p>
+ <p>Most of the functions takes the <c>port</c> handle as an
+ argument. This identifies the driver instance. Note that this
+ port handle must be stored by the driver, it is not given when
+ the driver is called from the emulator (see
+ <seealso marker="driver_entry#emulator">driver_entry</seealso>).</p>
+ <p>Some of the functions takes a parameter of type
+ <c>ErlDrvBinary</c>, a driver binary. It should be both
+ allocated and freed by the caller. Using a binary directly avoid
+ one extra copying of data.</p>
+ <p>Many of the output functions has a "header buffer", with
+ <c>hbuf</c> and <c>hlen</c> parameters. This buffer is sent as a
+ list before the binary (or list, depending on port mode) that is
+ sent. This is convenient when matching on messages received from
+ the port. (Although in the latest versions of Erlang, there is
+ the binary syntax, that enables you to match on the beginning of
+ a binary.)
+ <marker id="smp_support"></marker>
+</p>
+ <p>In the runtime system with SMP support, drivers are locked either
+ on driver level or port level (driver instance level). By default
+ driver level locking will be used, i.e., only one emulator thread
+ will execute code in the driver at a time. If port level locking
+ is used, multiple emulator threads may execute code in the driver
+ at the same time. There will only be one thread at a time calling
+ driver call-backs corresponding to the same port, though. In order
+ to enable port level locking set the <c>ERL_DRV_FLAG_USE_PORT_LOCKING</c>
+ <seealso marker="driver_entry#driver_flags">driver flag</seealso> in
+ the <seealso marker="driver_entry">driver_entry</seealso>
+ used by the driver. When port level locking is used it is the
+ responsibility of the driver writer to synchronize all accesses
+ to data shared by the ports (driver instances).</p>
+ <p>Most drivers written before the runtime system with SMP
+ support existed will be able to run in the runtime system
+ with SMP support without being rewritten if driver
+ level locking is used.</p>
+ <note>
+ <p>It is assumed that drivers does not access other drivers. If
+ drivers should access each other they have to provide their own
+ mechanism for thread safe synchronization. Such "inter driver
+ communication" is strongly discouraged.</p>
+ </note>
+ <p>Previously, in the runtime system without SMP support,
+ specific driver call-backs were always called from the same
+ thread. This is <em>not</em> the case in the runtime system
+ with SMP support. Regardless of locking scheme used, calls
+ to driver call-backs may be made from different threads, e.g.,
+ two consecutive calls to exactly the same call-back for exactly
+ the same port may be made from two different threads. This
+ will for <em>most</em> drivers not be a problem, but it might.
+ Drivers that depend on all call-backs being called in the
+ same thread, <em>have</em> to be rewritten before being used
+ in the runtime system with SMP support.</p>
+ <note>
+ <p>Regardless of locking scheme used, calls to driver
+ call-backs may be made from different threads.</p>
+ </note>
+ <p>Most functions in this API are <em>not</em> thread-safe, i.e.,
+ they may <em>not</em> be called from an arbitrary thread. Function
+ that are not documented as thread-safe may only be called from
+ driver call-backs or function calls descending from a driver
+ call-back call. Note that driver call-backs may be called from
+ different threads. This, however, is not a problem for any
+ functions in this API, since the emulator have control over
+ these threads.</p>
+ <note>
+ <p>Functions not explicitly documented as thread-safe are
+ <em>not</em> thread-safe. Also note that some functions
+ are <em>only</em> thread safe when used in a runtime
+ system with SMP support.</p>
+ </note>
+ </description>
+
+ <section>
+ <title>FUNCTIONALITY</title>
+ <p>All functions that a driver needs to do with Erlang are
+ performed through driver API functions. There are functions
+ for the following functionality:</p>
+ <taglist>
+ <tag>Timer functions</tag>
+ <item>Timer functions are used to control the timer that a driver
+ may use. The timer will have the emulator call the
+ <seealso marker="driver_entry#timeout">timeout</seealso> entry
+ function after a specified time. Only one timer is available
+ for each driver instance.</item>
+ <tag>Queue handling</tag>
+ <item>
+ <p>Every driver instance has an associated queue. This queue is a
+ <c>SysIOVec</c> that works as a buffer. It's mostly used for
+ the driver to buffer data that should be written to a device,
+ it is a byte stream. If the port owner process closes the
+ driver, and the queue is not empty, the driver will not be
+ closed. This enables the driver to flush its buffers before
+ closing.</p>
+ <p>The queue can be manipulated from arbitrary threads if
+ a port data lock is used. See documentation of the
+ <seealso marker="#ErlDrvPDL">ErlDrvPDL</seealso> type for
+ more information.</p>
+ </item>
+ <tag>Output functions</tag>
+ <item>With the output functions, the driver sends data back
+ the emulator. They will be received as messages by the port owner
+ process, see <c>open_port/2</c>. The vector function and the
+ function taking a driver binary is faster, because that avoid
+ copying the data buffer. There is also a fast way of sending
+ terms from the driver, without going through the binary term
+ format.</item>
+ <tag>Failure</tag>
+ <item>The driver can exit and signal errors up to Erlang. This is
+ only for severe errors, when the driver can't possibly keep
+ open.</item>
+ <tag>Asynchronous calls</tag>
+ <item>The latest Erlang versions (R7B and later) has provision for
+ asynchronous function calls, using a thread pool provided by
+ Erlang. There is also a select call, that can be used for
+ asynchronous drivers.</item>
+ <tag>Multi-threading</tag>
+ <item><marker id="multi_threading"></marker>
+ <p>A POSIX thread like API for multi-threading is provided. The
+ Erlang driver thread API only provide a subset of the functionality
+ provided by the POSIX thread API. The subset provided is
+ more or less the basic functionality needed for multi-threaded
+ programming:
+ </p>
+ <list>
+ <item><seealso marker="#ErlDrvTid">Threads</seealso></item>
+ <item><seealso marker="#ErlDrvMutex">Mutexes</seealso></item>
+ <item><seealso marker="#ErlDrvCond">Condition variables</seealso></item>
+ <item><seealso marker="#ErlDrvRWLock">Read/Write locks</seealso></item>
+ <item><seealso marker="#ErlDrvTSDKey">Thread specific data</seealso></item>
+ </list>
+ <p>The Erlang driver thread API can be used in conjunction with
+ the POSIX thread API on UN-ices and with the Windows native thread
+ API on Windows. The Erlang driver thread API has the advantage of
+ being portable, but there might exist situations where you want to
+ use functionality from the POSIX thread API or the Windows
+ native thread API.
+ </p>
+ <p>The Erlang driver thread API only return error codes when it is
+ reasonable to recover from an error condition. If it isn't reasonable
+ to recover from an error condition, the whole runtime system is
+ terminated. For example, if a create mutex operation fails, an error
+ code is returned, but if a lock operation on a mutex fails, the
+ whole runtime system is terminated.
+ </p>
+ <p>Note that there exist no "condition variable wait with timeout" in
+ the Erlang driver thread API. This is due to issues with
+ <c>pthread_cond_timedwait()</c>. When the system clock suddenly
+ is changed, it isn't always guaranteed that you will wake up from
+ the call as expected. An Erlang runtime system has to be able to
+ cope with sudden changes of the system clock. Therefore, we have
+ omitted it from the Erlang driver thread API. In the Erlang driver
+ case, timeouts can and should be handled with the timer functionality
+ of the Erlang driver API.
+ </p>
+ <p>In order for the Erlang driver thread API to function, thread
+ support has to be enabled in the runtime system. An Erlang driver
+ can check if thread support is enabled by use of
+ <seealso marker="#driver_system_info">driver_system_info()</seealso>.
+ Note that some functions in the Erlang driver API are thread-safe
+ only when the runtime system has SMP support, also this
+ information can be retrieved via
+ <seealso marker="#driver_system_info">driver_system_info()</seealso>.
+ Also note that a lot of functions in the Erlang driver API are
+ <em>not</em> thread-safe regardless of whether SMP support is
+ enabled or not. If a function isn't documented as thread-safe it
+ is <em>not</em> thread-safe.
+ </p>
+ <p><em>NOTE</em>: When executing in an emulator thread, it is
+ <em>very important</em> that you unlock <em>all</em> locks you
+ have locked before letting the thread out of your control;
+ otherwise, you are <em>very likely</em> to deadlock the whole
+ emulator. If you need to use thread specific data in an emulator
+ thread, only have the thread specific data set while the thread is
+ under your control, and clear the thread specific data before
+ you let the thread out of your control.
+ </p>
+ <p>In the future there will probably be debug functionality
+ integrated with the Erlang driver thread API. All functions
+ that create entities take a <c>name</c> argument. Currently
+ the <c>name</c> argument is unused, but it will be used when
+ the debug functionality has been implemented. If you name all
+ entities created well, the debug functionality will be able
+ to give you better error reports.
+ </p>
+ </item>
+ <tag>Adding / remove drivers</tag>
+ <item>A driver can add and later remove drivers.</item>
+ <tag>Monitoring processes</tag>
+ <item>A driver can monitor a process that does not own a port.</item>
+ <tag>Version management</tag>
+ <item>
+ <marker id="version_management"></marker>
+ <p>Version management is enabled for drivers that have set the
+ <seealso marker="driver_entry#extended_marker">extended_marker</seealso>
+ field of their
+ <seealso marker="driver_entry">driver_entry</seealso>
+ to <c>ERL_DRV_EXTENDED_MARKER</c>. <c>erl_driver.h</c> defines
+ <c>ERL_DRV_EXTENDED_MARKER</c>,
+ <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c>, and
+ <c>ERL_DRV_EXTENDED_MINOR_VERSION</c>.
+ <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> will be incremented when
+ driver incompatible changes are made to the Erlang runtime
+ system. Normally it will suffice to recompile drivers when the
+ <c>ERL_DRV_EXTENDED_MAJOR_VERSION</c> has changed, but it
+ could, under rare circumstances, mean that drivers have to
+ be slightly modified. If so, this will of course be documented.
+ <c>ERL_DRV_EXTENDED_MINOR_VERSION</c> will be incremented when
+ new features are added. The runtime system use the minor version
+ of the driver to determine what features to use.
+ The runtime system will refuse to load a driver if the major
+ 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.</p>
+ <p>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 use 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.</p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>DATA TYPES</title>
+
+ <taglist>
+ <tag><marker id="ErlDrvSysInfo"/>ErlDrvSysInfo</tag>
+ <item>
+ <p/>
+ <code type="none">
+typedef struct ErlDrvSysInfo {
+ int driver_major_version;
+ int driver_minor_version;
+ char *erts_version;
+ char *otp_release;
+ int thread_support;
+ int smp_support;
+ int async_threads;
+ int scheduler_threads;
+} ErlDrvSysInfo;
+ </code>
+
+ <p>
+ The <c>ErlDrvSysInfo</c> structure is used for storage of
+ information about the Erlang runtime system.
+ <seealso marker="#driver_system_info">driver_system_info()</seealso>
+ will write the system information when passed a reference to
+ a <c>ErlDrvSysInfo</c> structure. A description of the
+ fields in the structure follow:
+ </p>
+ <taglist>
+ <tag><c>driver_major_version</c></tag>
+ <item>The value of
+ <seealso marker="#version_management">ERL_DRV_EXTENDED_MAJOR_VERSION</seealso>
+ when the runtime system was compiled. This value is the same
+ as the value of
+ <seealso marker="#version_management">ERL_DRV_EXTENDED_MAJOR_VERSION</seealso>
+ used when compiling the driver; otherwise, the runtime system
+ would have refused to load the driver.
+ </item>
+ <tag><c>driver_minor_version</c></tag>
+ <item>The value of
+ <seealso marker="#version_management">ERL_DRV_EXTENDED_MINOR_VERSION</seealso>
+ when the runtime system was compiled. This value might differ
+ from the value of
+ <seealso marker="#version_management">ERL_DRV_EXTENDED_MINOR_VERSION</seealso>
+ used when compiling the driver.
+ </item>
+ <tag><c>erts_version</c></tag>
+ <item>A string containing the version number of the runtime system
+ (the same as returned by
+ <seealso marker="erts:erlang#system_info_version">erlang:system_info(version)</seealso>).
+ </item>
+ <tag><c>otp_release</c></tag>
+ <item>A string containing the OTP release number
+ (the same as returned by
+ <seealso marker="erts:erlang#system_info_otp_release">erlang:system_info(otp_release)</seealso>).
+ </item>
+ <tag><c>thread_support</c></tag>
+ <item>A value <c>!= 0</c> if the runtime system has thread support;
+ otherwise, <c>0</c>.
+ </item>
+ <tag><c>smp_support</c></tag>
+ <item>A value <c>!= 0</c> if the runtime system has SMP support;
+ otherwise, <c>0</c>.
+ </item>
+ <tag><c>thread_support</c></tag>
+ <item>A value <c>!= 0</c> if the runtime system has thread support;
+ otherwise, <c>0</c>.
+ </item>
+ <tag><c>smp_support</c></tag>
+ <item>A value <c>!= 0</c> if the runtime system has SMP support;
+ otherwise, <c>0</c>.
+ </item>
+ <tag><c>async_threads</c></tag>
+ <item>The number of async threads in the async thread pool used
+ by <seealso marker="#driver_async">driver_async()</seealso>
+ (the same as returned by
+ <seealso marker="erts:erlang#system_info_thread_pool_size">erlang:system_info(thread_pool_size)</seealso>).
+ </item>
+ <tag><c>scheduler_threads</c></tag>
+ <item>The number of scheduler threads used by the runtime system
+ (the same as returned by
+ <seealso marker="erts:erlang#system_info_schedulers">erlang:system_info(schedulers)</seealso>).
+ </item>
+ </taglist>
+ </item>
+
+ <tag><marker id="ErlDrvBinary"/>ErlDrvBinary</tag>
+ <item>
+ <p/>
+ <code type="none">
+typedef struct ErlDrvBinary {
+ int orig_size;
+ char orig_bytes[];
+} ErlDrvBinary;
+</code>
+ <p>The <c>ErlDrvBinary</c> structure is a binary, as sent
+ between the emulator and the driver. All binaries are
+ reference counted; when <c>driver_binary_free</c> is called,
+ the reference count is decremented, when it reaches zero,
+ the binary is deallocated. The <c>orig_size</c> is the size
+ of the binary, and <c>orig_bytes</c> is the buffer. The
+ <c>ErlDrvBinary</c> does not have a fixed size, its size is
+ <c>orig_size + 2 * sizeof(int)</c>.</p>
+ <note>
+ <p>The <c>refc</c> field has been removed. The reference count of
+ an <c>ErlDrvBinary</c> is now stored elsewhere. The
+ reference count of an <c>ErlDrvBinary</c> can be accessed via
+ <seealso marker="#driver_binary_get_refc">driver_binary_get_refc()</seealso>,
+ <seealso marker="#driver_binary_inc_refc">driver_binary_inc_refc()</seealso>,
+ and
+ <seealso marker="#driver_binary_dec_refc">driver_binary_dec_refc()</seealso>.</p>
+ </note>
+ <p>Some driver calls, such as <c>driver_enq_binary</c>,
+ increments the driver reference count, and others, such as
+ <c>driver_deq</c> decrements it.</p>
+ <p>Using a driver binary instead of a normal buffer, is often
+ faster, since the emulator doesn't need to copy the data,
+ only the pointer is used.</p>
+ <p>A driver binary allocated in the driver, with
+ <c>driver_alloc_binary</c>, should be freed in the driver (unless otherwise stated),
+ with <c>driver_free_binary</c>. (Note that this doesn't
+ necessarily deallocate it, if the driver is still referred
+ in the emulator, the ref-count will not go to zero.)</p>
+ <p>Driver binaries are used in the <c>driver_output2</c> and
+ <c>driver_outputv</c> calls, and in the queue. Also the
+ driver call-back <seealso marker="driver_entry#outputv">outputv</seealso> uses driver
+ binaries.</p>
+ <p>If the driver of some reason or another, wants to keep a
+ driver binary around, in a static variable for instance, the
+ reference count should be incremented,
+ and the binary can later be freed in the <seealso marker="driver_entry#stop">stop</seealso> call-back, with
+ <c>driver_free_binary</c>.</p>
+ <p>Note that since a driver binary is shared by the driver and
+ the emulator, a binary received from the emulator or sent to
+ the emulator, must not be changed by the driver.</p>
+ <p>From erts version 5.5 (OTP release R11B), orig_bytes is
+ guaranteed to be properly aligned for storage of an array of
+ doubles (usually 8-byte aligned).</p>
+ </item>
+ <tag>ErlDrvData</tag>
+ <item>
+ <p>The <c>ErlDrvData</c> 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.</p>
+ </item>
+ <tag>SysIOVec</tag>
+ <item>
+ <p>This is a system I/O vector, as used by <c>writev</c> on
+ unix and <c>WSASend</c> on Win32. It is used in
+ <c>ErlIOVec</c>.</p>
+ </item>
+ <tag><marker id="ErlIOVec"/>ErlIOVec</tag>
+ <item>
+ <p/>
+ <code type="none">
+typedef struct ErlIOVec {
+ int vsize;
+ int size;
+ SysIOVec* iov;
+ >ErlDrvBinary** binv;
+} ErlIOVec;
+</code>
+ <p>The I/O vector used by the emulator and drivers, is a list
+ of binaries, with a <c>SysIOVec</c> pointing to the buffers
+ of the binaries. It is used in <c>driver_outputv</c> and the
+ <seealso marker="driver_entry#outputv">outputv</seealso>
+ driver call-back. Also, the driver queue is an
+ <c>ErlIOVec</c>.</p>
+ </item>
+
+ <tag>ErlDrvMonitor</tag>
+ <item>
+ <p>When a driver creates a monitor for a process, a
+ <c>ErlDrvMonitor</c> is filled in. This is an opaque
+ data-type which can be assigned to but not compared without
+ using the supplied compare function (i.e. it behaves like a struct).</p>
+ <p>The driver writer should provide the memory for storing the
+ monitor when calling <seealso marker="#driver_monitor_process">driver_monitor_process</seealso>. The
+ address of the data is not stored outside of the driver, so
+ the <c>ErlDrvMonitor</c> can be used as any other datum, it
+ can be copied, moved in memory, forgotten etc.</p>
+ </item>
+ <tag><marker id="ErlDrvNowData"/>ErlDrvNowData</tag>
+ <item>
+ <p>The <c>ErlDrvNowData</c> structure holds a timestamp
+ consisting of three values measured from some arbitrary
+ point in the past. The three structure members are:</p>
+ <taglist>
+ <tag>megasecs</tag>
+ <item>The number of whole megaseconds elapsed since the arbitrary
+ point in time</item>
+ <tag>secs</tag>
+ <item>The number of whole seconds elapsed since the arbitrary
+ point in time</item>
+ <tag>microsecs</tag>
+ <item>The number of whole microseconds elapsed since the arbitrary
+ point in time</item>
+ </taglist>
+ </item>
+ <tag><marker id="ErlDrvPDL"/>ErlDrvPDL</tag>
+ <item>
+ <p>If certain port specific data have to be accessed from other
+ threads than those calling the driver call-backs, a port data lock
+ can be used in order to synchronize the operations on the data.
+ Currently, the only port specific data that the emulator
+ associates with the port data lock is the driver queue.</p>
+ <p>Normally a driver instance does not have a port data lock. If
+ the driver instance want to use a port data lock, it has to
+ create the port data lock by calling
+ <seealso marker="#driver_pdl_create">driver_pdl_create()</seealso>.
+ <em>NOTE</em>: Once the port data lock has been created, every
+ access to data associated with the port data lock have to be done
+ while having the port data lock locked. The port data lock is
+ locked, and unlocked, respectively, by use of
+ <seealso marker="#driver_pdl_lock">driver_pdl_lock()</seealso>, and
+ <seealso marker="#driver_pdl_unlock">driver_pdl_unlock()</seealso>.</p>
+ <p>A port data lock is reference counted, and when the reference
+ count reach zero, it will be destroyed. The emulator will at
+ least increment the reference count once when the lock is
+ created and decrement it once when the port associated with
+ the lock terminates. The emulator will also increment the
+ reference count when an async job is enqueued and decrement
+ it after an async job has been invoked, or canceled. Besides
+ this, it is the responsibility of the driver to ensure that
+ the reference count does not reach zero before the last use
+ of the lock by the driver has been made. The reference count
+ can be read, incremented, and decremented, respectively, by
+ use of
+ <seealso marker="#driver_pdl_get_refc">driver_pdl_get_refc()</seealso>,
+ <seealso marker="#driver_pdl_inc_refc">driver_pdl_inc_refc()</seealso>, and
+ <seealso marker="#driver_pdl_dec_refc">driver_pdl_dec_refc()</seealso>.</p>
+ </item>
+
+ <tag><marker id="ErlDrvTid"/>ErlDrvTid</tag>
+ <item>
+ <p>Thread identifier.</p>
+ <p>See also:
+ <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>,
+ <seealso marker="#erl_drv_thread_exit">erl_drv_thread_exit()</seealso>,
+ <seealso marker="#erl_drv_thread_join">erl_drv_thread_join()</seealso>,
+ <seealso marker="#erl_drv_thread_self">erl_drv_thread_self()</seealso>,
+ and
+ <seealso marker="#erl_drv_equal_tids">erl_drv_equal_tids()</seealso>.
+ </p>
+ </item>
+ <tag><marker id="ErlDrvThreadOpts"/>ErlDrvThreadOpts</tag>
+ <item>
+ <p/>
+ <code type="none">
+ int suggested_stack_size;
+ </code>
+ <p>Thread options structure passed to
+ <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>.
+ Currently the following fields exist:
+ </p>
+ <taglist>
+ <tag>suggested_stack_size</tag>
+ <item>A suggestion, in kilo-words, on how large stack to use. A value less
+ than zero means default size.
+ </item>
+ </taglist>
+ <p>See also:
+ <seealso marker="#erl_drv_thread_opts_create">erl_drv_thread_opts_create()</seealso>,
+ <seealso marker="#erl_drv_thread_opts_destroy">erl_drv_thread_opts_destroy()</seealso>,
+ and
+ <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>.
+ </p>
+ </item>
+
+ <tag><marker id="ErlDrvMutex"/>ErlDrvMutex</tag>
+ <item>
+ <p>Mutual exclusion lock. Used for synchronizing access to shared data.
+ Only one thread at a time can lock a mutex.
+ </p>
+ <p>See also:
+ <seealso marker="#erl_drv_mutex_create">erl_drv_mutex_create()</seealso>,
+ <seealso marker="#erl_drv_mutex_destroy">erl_drv_mutex_destroy()</seealso>,
+ <seealso marker="#erl_drv_mutex_lock">erl_drv_mutex_lock()</seealso>,
+ <seealso marker="#erl_drv_mutex_trylock">erl_drv_mutex_trylock()</seealso>,
+ and
+ <seealso marker="#erl_drv_mutex_unlock">erl_drv_mutex_unlock()</seealso>.
+ </p>
+ </item>
+ <tag><marker id="ErlDrvCond"/>ErlDrvCond</tag>
+ <item>
+ <p>Condition variable. Used when threads need to wait for a specific
+ condition to appear before continuing execution. Condition variables
+ need to be used with associated mutexes.
+ </p>
+ <p>See also:
+ <seealso marker="#erl_drv_cond_create">erl_drv_cond_create()</seealso>,
+ <seealso marker="#erl_drv_cond_destroy">erl_drv_cond_destroy()</seealso>,
+ <seealso marker="#erl_drv_cond_signal">erl_drv_cond_signal()</seealso>,
+ <seealso marker="#erl_drv_cond_broadcast">erl_drv_cond_broadcast()</seealso>,
+ and
+ <seealso marker="#erl_drv_cond_wait">erl_drv_cond_wait()</seealso>.
+ </p>
+ </item>
+ <tag><marker id="ErlDrvRWLock"/>ErlDrvRWLock</tag>
+ <item>
+ <p>Read/write lock. Used to allow multiple threads to read shared data
+ while only allowing one thread to write the same data. Multiple threads
+ can read lock an rwlock at the same time, while only one thread can
+ read/write lock an rwlock at a time.
+ </p>
+ <p>See also:
+ <seealso marker="#erl_drv_rwlock_create">erl_drv_rwlock_create()</seealso>,
+ <seealso marker="#erl_drv_rwlock_destroy">erl_drv_rwlock_destroy()</seealso>,
+ <seealso marker="#erl_drv_rwlock_rlock">erl_drv_rwlock_rlock()</seealso>,
+ <seealso marker="#erl_drv_rwlock_tryrlock">erl_drv_rwlock_tryrlock()</seealso>,
+ <seealso marker="#erl_drv_rwlock_runlock">erl_drv_rwlock_runlock()</seealso>,
+ <seealso marker="#erl_drv_rwlock_rwlock">erl_drv_rwlock_rwlock()</seealso>,
+ <seealso marker="#erl_drv_rwlock_tryrwlock">erl_drv_rwlock_tryrwlock()</seealso>,
+ and
+ <seealso marker="#erl_drv_rwlock_rwunlock">erl_drv_rwlock_rwunlock()</seealso>.
+ </p>
+ </item>
+ <tag><marker id="ErlDrvTSDKey"/>ErlDrvTSDKey</tag>
+ <item>
+ <p>Key which thread specific data can be associated with.</p>
+ <p>See also:
+ <seealso marker="#erl_drv_tsd_key_create">erl_drv_tsd_key_create()</seealso>,
+ <seealso marker="#erl_drv_tsd_key_destroy">erl_drv_tsd_key_destroy()</seealso>,
+ <seealso marker="#erl_drv_tsd_set">erl_drv_tsd_set()</seealso>,
+ and
+ <seealso marker="#erl_drv_tsd_get">erl_drv_tsd_get()</seealso>.
+ </p>
+ </item>
+ </taglist>
+ </section>
+
+ <funcs>
+ <func>
+ <name><ret>void</ret><nametext>driver_system_info(ErlDrvSysInfo *sys_info_ptr, size_t size)</nametext></name>
+ <fsummary>Get information about the Erlang runtime system</fsummary>
+ <desc>
+ <marker id="driver_system_info"></marker>
+ <p>This function will write information about the Erlang runtime
+ system into the
+ <seealso marker="#ErlDrvSysInfo">ErlDrvSysInfo</seealso>
+ structure referred to by the first argument. The second
+ argument should be the size of the
+ <seealso marker="#ErlDrvSysInfo">ErlDrvSysInfo</seealso>
+ structure, i.e., <c>sizeof(ErlDrvSysInfo)</c>.</p>
+ <p>See the documentation of the
+ <seealso marker="#ErlDrvSysInfo">ErlDrvSysInfo</seealso>
+ structure for information about specific fields.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_output(ErlDrvPort port, char *buf, int len)</nametext></name>
+ <fsummary>Send data from driver to port owner</fsummary>
+ <desc>
+ <marker id="driver_output"></marker>
+ <p>The <c>driver_output</c> function is used to send data from
+ the driver up to the emulator. The data will be received as
+ terms or binary data, depending on how the driver port was
+ opened.</p>
+ <p>The data is queued in the port owner process' message
+ queue. Note that this does not yield to the emulator. (Since
+ the driver and the emulator runs in the same thread.)</p>
+ <p>The parameter <c>buf</c> points to the data to send, and
+ <c>len</c> is the number of bytes.</p>
+ <p>The return value for all output functions is 0. (Unless the
+ driver is used for distribution, in which case it can fail
+ and return -1. For normal use, the output function always
+ returns 0.)</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_output2(ErlDrvPort port, char *hbuf, int hlen, char *buf, int len)</nametext></name>
+ <fsummary>Send data and binary data to port owner</fsummary>
+ <desc>
+ <marker id="driver_output2"></marker>
+ <p>The <c>driver_output2</c> function first sends <c>hbuf</c>
+ (length in <c>hlen</c>) data as a list, regardless of port
+ settings. Then <c>buf</c> is sent as a binary or list.
+ E.g. if <c>hlen</c> is 3 then the port owner process will
+ receive <c>[H1, H2, H3 | T]</c>.</p>
+ <p>The point of sending data as a list header, is to facilitate
+ matching on the data received.</p>
+ <p>The return value is 0 for normal use.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_output_binary(ErlDrvPort port, char *hbuf, int hlen, ErlDrvBinary* bin, int offset, int len)</nametext></name>
+ <fsummary>Send data from a driver binary to port owner</fsummary>
+ <desc>
+ <marker id="driver_output_binary"></marker>
+ <p>This function sends data to port owner process from a
+ driver binary, it has a header buffer (<c>hbuf</c>
+ and <c>hlen</c>) just like <c>driver_output2</c>. The
+ <c>hbuf</c> parameter can be <c>NULL</c>.</p>
+ <p>The parameter <c>offset</c> is an offset into the binary and
+ <c>len</c> is the number of bytes to send.</p>
+ <p>Driver binaries are created with <c>driver_alloc_binary</c>.</p>
+ <p>The data in the header is sent as a list and the binary as
+ an Erlang binary in the tail of the list.</p>
+ <p>E.g. if <c>hlen</c> is 2, then the port owner process will
+ receive <c><![CDATA[[H1, H2 | <<T>>]]]></c>.</p>
+ <p>The return value is 0 for normal use.</p>
+ <p>Note that, using the binary syntax in Erlang, the driver
+ application can match the header directly from the binary,
+ so the header can be put in the binary, and hlen can be set
+ to 0.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_outputv(ErlDrvPort port, char* hbuf, int hlen, ErlIOVec *ev, int skip)</nametext></name>
+ <fsummary>Send vectorized data to port owner</fsummary>
+ <desc>
+ <marker id="driver_outputv"></marker>
+ <p>This function sends data from an IO vector, <c>ev</c>, to
+ the port owner process. It has a header buffer (<c>hbuf</c>
+ and <c>hlen</c>), just like <c>driver_output2</c>.</p>
+ <p>The <c>skip</c> parameter is a number of bytes to skip of
+ the <c>ev</c> vector from the head.</p>
+ <p>You get vectors of <c>ErlIOVec</c> type from the driver
+ queue (see below), and the <seealso marker="driver_entry#outputv">outputv</seealso> driver entry
+ function. You can also make them yourself, if you want to
+ send several <c>ErlDrvBinary</c> buffers at once. Often
+ it is faster to use <c>driver_output</c> or
+ <c>driver_output_binary</c>.</p>
+ <p>E.g. if <c>hlen</c> is 2 and <c>ev</c> points to an array of
+ three binaries, the port owner process will receive <c><![CDATA[[H1, H2, <<B1>>, <<B2>> | <<B3>>]]]></c>.</p>
+ <p>The return value is 0 for normal use.</p>
+ <p>The comment for <c>driver_output_binary</c> applies for
+ <c>driver_outputv</c> too.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_vec_to_buf(ErlIOVec *ev, char *buf, int len)</nametext></name>
+ <fsummary>Collect data segments into a buffer</fsummary>
+ <desc>
+ <marker id="driver_vec_to_buf"></marker>
+ <p>This function collects several segments of data, referenced
+ by <c>ev</c>, by copying them in order to the buffer
+ <c>buf</c>, of the size <c>len</c>.</p>
+ <p>If the data is to be sent from the driver to the port owner
+ process, it is faster to use <c>driver_outputv</c>.</p>
+ <p>The return value is the space left in the buffer, i.e. if
+ the <c>ev</c> contains less than <c>len</c> bytes it's the
+ difference, and if <c>ev</c> contains <c>len</c> bytes or
+ more, it's 0. This is faster if there is more than one header byte,
+ since the binary syntax can construct integers directly from
+ the binary.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_set_timer(ErlDrvPort port, unsigned long time)</nametext></name>
+ <fsummary>Set a timer to call the driver</fsummary>
+ <desc>
+ <marker id="driver_set_timer"></marker>
+ <p>This function sets a timer on the driver, which will count
+ down and call the driver when it is timed out. The
+ <c>time</c> parameter is the time in milliseconds before the
+ timer expires.</p>
+ <p>When the timer reaches 0 and expires, the driver entry
+ function <seealso marker="driver_entry#emulator">timeout</seealso> is called.</p>
+ <p>Note that there is only one timer on each driver instance;
+ setting a new timer will replace an older one.</p>
+ <p>Return value i 0 (-1 only when the <c>timeout</c> driver
+ function is <c>NULL</c>).</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_cancel_timer(ErlDrvPort port)</nametext></name>
+ <fsummary>Cancel a previously set timer</fsummary>
+ <desc>
+ <marker id="driver_cancel_timer"></marker>
+ <p>This function cancels a timer set with
+ <c>driver_set_timer</c>.</p>
+ <p>The return value is 0.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_read_timer(ErlDrvPort port, unsigned long *time_left)</nametext></name>
+ <fsummary>Read the time left before timeout</fsummary>
+ <desc>
+ <marker id="driver_read_timer"></marker>
+ <p>This function reads the current time of a timer, and places
+ the result in <c>time_left</c>. This is the time in
+ milliseconds, before the timeout will occur.</p>
+ <p>The return value is 0.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_get_now(ErlDrvNowData *now)</nametext></name>
+ <fsummary>Read a system timestamp</fsummary>
+ <desc>
+ <marker id="driver_get_now"></marker>
+ <p>This function reads a timestamp into the memory pointed to by
+ the parameter <c>now</c>. See the description of <seealso marker="#ErlDrvNowData">ErlDrvNowData</seealso> for
+ specification of it's fields. </p>
+ <p>The return value is 0 unless the <c>now</c> pointer is not
+ valid, in which case it is &lt; 0. </p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_select(ErlDrvPort port, ErlDrvEvent event, int mode, int on)</nametext></name>
+ <fsummary>Provide an event for having the emulator call the driver</fsummary>
+ <desc>
+ <marker id="driver_select"></marker>
+ <p>This function is used by drivers to provide the emulator with
+ events to check for. This enables the emulator to call the driver
+ when something has happened asynchronously.</p>
+ <p>The <c>event</c> argument identifies an OS-specific event object.
+ On Unix systems, the functions <c>select</c>/<c>poll</c> are used. The
+ event object must be a socket or pipe (or other object that
+ <c>select</c>/<c>poll</c> can use).
+ On windows, the Win32 API function <c>WaitForMultipleObjects</c>
+ is used. This places other restriction on the event object.
+ Refer to the Win32 SDK documentation.</p>
+ <p>The <c>on</c> parameter should be <c>1</c> for setting events
+ and <c>0</c> for clearing them.</p>
+ <p>The <c>mode</c> argument is bitwise-or combination of
+ <c>ERL_DRV_READ</c>, <c>ERL_DRV_WRITE</c> and <c>ERL_DRV_USE</c>.
+ The first two specifies whether to wait for read events and/or write
+ events. A fired read event will call
+ <seealso marker="driver_entry#ready_input">ready_input</seealso>
+ while a fired write event will call
+ <seealso marker="driver_entry#ready_output">ready_output</seealso>.
+ </p>
+ <note>
+ <p>Some OS (Windows) does not differ between read and write events.
+ The call-back for a fired event then only depends on the value of <c>mode</c>.</p>
+ </note>
+ <p><c>ERL_DRV_USE</c> specifies if we are using the event object or if we want to close it.
+ On an emulator with SMP support, it is not safe to clear all events
+ and then close the event object after <c>driver_select</c> has
+ returned. Another thread may still be using the event object
+ internally. To safely close an event object call
+ <c>driver_select</c> with <c>ERL_DRV_USE</c> and <c>on==0</c>. That
+ will clear all events and then call
+ <seealso marker="driver_entry#stop_select">stop_select</seealso>
+ when it is safe to close the event object.
+ <c>ERL_DRV_USE</c> should be set together with the first event
+ for an event object. It is harmless to set <c>ERL_DRV_USE</c>
+ even though it already has been done. Clearing all events but keeping
+ <c>ERL_DRV_USE</c> set will indicate that we are using the event
+ object and probably will set events for it again.</p>
+ <note>
+ <p>ERL_DRV_USE was added in OTP release R13. Old drivers will still work
+ as before. But it is recommended to update them to use <c>ERL_DRV_USE</c> and
+ <c>stop_select</c> to make sure that event objects are closed in a safe way.</p>
+ </note>
+ <p>The return value is 0 (Failure, -1, only if the
+ <c>ready_input</c>/<c>ready_output</c> is
+ <c>NULL</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>void *</ret><nametext>driver_alloc(size_t size)</nametext></name>
+ <fsummary>Allocate memory</fsummary>
+ <desc>
+ <marker id="driver_alloc"></marker>
+ <p>This function allocates a memory block of the size specified
+ in <c>size</c>, and returns it. This only fails on out of
+ memory, in that case <c>NULL</c> is returned. (This is most
+ often a wrapper for <c>malloc</c>).</p>
+ <p>Memory allocated must be explicitly freed with a corresponding
+ call to <c>driver_free</c> (unless otherwise stated).</p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>void *</ret><nametext>driver_realloc(void *ptr, size_t size)</nametext></name>
+ <fsummary>Resize an allocated memory block</fsummary>
+ <desc>
+ <marker id="driver_realloc"></marker>
+ <p>This function resizes a memory block, either in place, or by
+ allocating a new block, copying the data and freeing the old
+ block. A pointer is returned to the reallocated memory. On
+ failure (out of memory), <c>NULL</c> is returned. (This is
+ most often a wrapper for <c>realloc</c>.)</p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>void</ret><nametext>driver_free(void *ptr)</nametext></name>
+ <fsummary>Free an allocated memory block</fsummary>
+ <desc>
+ <marker id="driver_free"></marker>
+ <p>This function frees the memory pointed to by <c>ptr</c>. The
+ memory should have been allocated with
+ <c>driver_alloc</c>. All allocated memory should be
+ deallocated, just once. There is no garbage collection in
+ drivers.</p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>ErlDrvBinary*</ret><nametext>driver_alloc_binary(int size)</nametext></name>
+ <fsummary>Allocate a driver binary</fsummary>
+ <desc>
+ <marker id="driver_alloc_binary"></marker>
+ <p>This function allocates a driver binary with a memory block
+ of at least <c>size</c> bytes, and returns a pointer to it,
+ or NULL on failure (out of memory). When a driver binary has
+ been sent to the emulator, it must not be altered. Every
+ allocated binary should be freed by a corresponding call to
+ <c>driver_free_binary</c> (unless otherwise stated).</p>
+ <p>Note that a driver binary has an internal reference counter,
+ this means that calling <c>driver_free_binary</c> it may not
+ actually dispose of it. If it's sent to the emulator, it may
+ be referenced there.</p>
+ <p>The driver binary has a field, <c>orig_bytes</c>, which
+ marks the start of the data in the binary.</p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>ErlDrvBinary*</ret><nametext>driver_realloc_binary(ErlDrvBinary *bin, int size)</nametext></name>
+ <fsummary>Resize a driver binary</fsummary>
+ <desc>
+ <marker id="driver_realloc_binary"></marker>
+ <p>This function resizes a driver binary, while keeping the
+ data. The resized driver binary is returned. On failure (out
+ of memory), <c>NULL</c> is returned.</p>
+ <p>This function is only thread-safe when the emulator with SMP
+ support is used.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>void</ret><nametext>driver_free_binary(ErlDrvBinary *bin)</nametext></name>
+ <fsummary>Free a driver binary</fsummary>
+ <desc>
+ <marker id="driver_free_binary"></marker>
+ <p>This function frees a driver binary <c>bin</c>, allocated
+ previously with <c>driver_alloc_binary</c>. Since binaries
+ in Erlang are reference counted, the binary may still be
+ around.</p>
+ <p>This function is only thread-safe when the emulator with SMP
+ support is used.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>long</ret><nametext>driver_binary_get_refc(ErlDrvBinary *bin)</nametext></name>
+ <fsummary>Get the reference count of a driver binary</fsummary>
+ <desc>
+ <marker id="driver_binary_get_refc"></marker>
+ <p>Returns current reference count on <c>bin</c>.</p>
+ <p>This function is only thread-safe when the emulator with SMP
+ support is used.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>long</ret><nametext>driver_binary_inc_refc(ErlDrvBinary *bin)</nametext></name>
+ <fsummary>Increment the reference count of a driver binary</fsummary>
+ <desc>
+ <marker id="driver_binary_inc_refc"></marker>
+ <p>Increments the reference count on <c>bin</c> and returns
+ the reference count reached after the increment.</p>
+ <p>This function is only thread-safe when the emulator with SMP
+ support is used.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>long</ret><nametext>driver_binary_dec_refc(ErlDrvBinary *bin)</nametext></name>
+ <fsummary>Decrement the reference count of a driver binary</fsummary>
+ <desc>
+ <marker id="driver_binary_dec_refc"></marker>
+ <p>Decrements the reference count on <c>bin</c> and returns
+ the reference count reached after the decrement.</p>
+ <p>This function is only thread-safe when the emulator with SMP
+ support is used.</p>
+ <note>
+ <p>You should normally decrement the reference count of a
+ driver binary by calling
+ <seealso marker="#driver_free_binary">driver_free_binary()</seealso>.
+ <c>driver_binary_dec_refc()</c> does <em>not</em> free
+ the binary if the reference count reaches zero. <em>Only</em>
+ use <c>driver_binary_dec_refc()</c> when you are sure
+ <em>not</em> to reach a reference count of zero.</p>
+ </note>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_enq(ErlDrvPort port, char* buf, int len)</nametext></name>
+ <fsummary>Enqueue data in the driver queue</fsummary>
+ <desc>
+ <marker id="driver_enq"></marker>
+ <p>This function enqueues data in the driver queue. The data in
+ <c>buf</c> is copied (<c>len</c> bytes) and placed at the
+ end of the driver queue. The driver queue is normally used
+ in a FIFO way.</p>
+ <p>The driver queue is available to queue output from the
+ emulator to the driver (data from the driver to the emulator
+ is queued by the emulator in normal erlang message
+ queues). This can be useful if the driver has to wait for
+ slow devices etc, and wants to yield back to the
+ emulator. The driver queue is implemented as an ErlIOVec.</p>
+ <p>When the queue contains data, the driver won't close, until
+ the queue is empty.</p>
+ <p>The return value is 0.</p>
+ <p>This function can be called from an arbitrary thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_pushq(ErlDrvPort port, char* buf, int len)</nametext></name>
+ <fsummary>Push data at the head of the driver queue</fsummary>
+ <desc>
+ <marker id="driver_pushq"></marker>
+ <p>This function puts data at the head of the driver queue. The
+ data in <c>buf</c> is copied (<c>len</c> bytes) and placed
+ at the beginning of the queue.</p>
+ <p>The return value is 0.</p>
+ <p>This function can be called from an arbitrary thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_deq(ErlDrvPort port, int size)</nametext></name>
+ <fsummary>Dequeue data from the head of the driver queue</fsummary>
+ <desc>
+ <marker id="driver_deq"></marker>
+ <p>This function dequeues data by moving the head pointer
+ forward in the driver queue by <c>size</c> bytes. The data
+ in the queue will be deallocated.</p>
+ <p>The return value is the number of bytes remaining in the queue
+ or -1 on failure.</p>
+ <p>This function can be called from an arbitrary thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_sizeq(ErlDrvPort port)</nametext></name>
+ <fsummary>Return the size of the driver queue</fsummary>
+ <desc>
+ <marker id="driver_sizeq"></marker>
+ <p>This function returns the number of bytes currently in the
+ driver queue.</p>
+ <p>This function can be called from an arbitrary thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_enq_bin(ErlDrvPort port, ErlDrvBinary *bin, int offset, int len)</nametext></name>
+ <fsummary>Enqueue binary in the driver queue</fsummary>
+ <desc>
+ <marker id="driver_enq_bin"></marker>
+ <p>This function enqueues a driver binary in the driver
+ queue. The data in <c>bin</c> at <c>offset</c> with length
+ <c>len</c> is placed at the end of the queue. This function
+ is most often faster than <c>driver_enq</c>, because the
+ data doesn't have to be copied.</p>
+ <p>This function can be called from an arbitrary thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
+ <p>The return value is 0.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_pushq_bin(ErlDrvPort port, ErlDrvBinary *bin, int offset, int len)</nametext></name>
+ <fsummary>Push binary at the head of the driver queue</fsummary>
+ <desc>
+ <marker id="driver_pushq_bin"></marker>
+ <p>This function puts data in the binary <c>bin</c>, at
+ <c>offset</c> with length <c>len</c> at the head of the
+ driver queue. It is most often faster than
+ <c>driver_pushq</c>, because the data doesn't have to be
+ copied.</p>
+ <p>This function can be called from an arbitrary thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
+ <p>The return value is 0.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>SysIOVec*</ret><nametext>driver_peekq(ErlDrvPort port, int *vlen)</nametext></name>
+ <fsummary>Get the driver queue as a vector</fsummary>
+ <desc>
+ <marker id="driver_peekq"></marker>
+ <p>This function retrieves the driver queue as a pointer to an
+ array of <c>SysIOVec</c>s. It also returns the number of
+ elements in <c>vlen</c>. This is the only way to get data
+ out of the queue.</p>
+ <p>Nothing is remove from the queue by this function, that must be done
+ with <c>driver_deq</c>.</p>
+ <p>The returned array is suitable to use with the Unix system
+ call <c>writev</c>.</p>
+ <p>This function can be called from an arbitrary thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_enqv(ErlDrvPort port, ErlIOVec *ev, int skip)</nametext></name>
+ <fsummary>Enqueue vector in the driver queue</fsummary>
+ <desc>
+ <marker id="driver_enqv"></marker>
+ <p>This function enqueues the data in <c>ev</c>, skipping the
+ first <c>skip</c> bytes of it, at the end of the driver
+ queue. It is faster than <c>driver_enq</c>, because the data
+ doesn't have to be copied.</p>
+ <p>The return value is 0.</p>
+ <p>This function can be called from an arbitrary thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_pushqv(ErlDrvPort port, ErlIOVec *ev, int skip)</nametext></name>
+ <fsummary>Push vector at the head of the driver queue</fsummary>
+ <desc>
+ <marker id="driver_pushqv"></marker>
+ <p>This function puts the data in <c>ev</c>, skipping the first
+ <c>skip</c> bytes of it, at the head of the driver queue.
+ It is faster than <c>driver_pushq</c>, because the data
+ doesn't have to be copied.</p>
+ <p>The return value is 0.</p>
+ <p>This function can be called from an arbitrary thread if a
+ <seealso marker="#ErlDrvPDL">port data lock</seealso>
+ associated with the <c>port</c> is locked by the calling
+ thread during the call.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>ErlDrvPDL</ret><nametext>driver_pdl_create(ErlDrvPort port)</nametext></name>
+ <fsummary>Create a port data lock</fsummary>
+ <desc>
+ <marker id="driver_pdl_create"></marker>
+ <p>This function creates a port data lock associated with
+ the <c>port</c>. <em>NOTE</em>: Once a port data lock has
+ been created, it has to be locked during all operations
+ on the driver queue of the <c>port</c>.</p>
+ <p>On success a newly created port data lock is returned. On
+ failure <c>NULL</c> is returned. <c>driver_pdl_create()</c> will
+ fail if <c>port</c> is invalid or if a port data lock already has
+ been associated with the <c>port</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>void</ret><nametext>driver_pdl_lock(ErlDrvPDL pdl)</nametext></name>
+ <fsummary>Lock port data lock</fsummary>
+ <desc>
+ <marker id="driver_pdl_lock"></marker>
+ <p>This function locks the port data lock passed as argument
+ (<c>pdl</c>).</p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>void</ret><nametext>driver_pdl_unlock(ErlDrvPDL pdl)</nametext></name>
+ <fsummary>Unlock port data lock</fsummary>
+ <desc>
+ <marker id="driver_pdl_unlock"></marker>
+ <p>This function unlocks the port data lock passed as argument
+ (<c>pdl</c>).</p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>long</ret><nametext>driver_pdl_get_refc(ErlDrvPDL pdl)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <marker id="driver_pdl_get_refc"></marker>
+ <p>This function returns the current reference count of
+ the port data lock passed as argument (<c>pdl</c>).</p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>long</ret><nametext>driver_pdl_inc_refc(ErlDrvPDL pdl)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <marker id="driver_pdl_inc_refc"></marker>
+ <p>This function increments the reference count of
+ the port data lock passed as argument (<c>pdl</c>).</p>
+ <p>The current reference count after the increment has
+ been performed is returned.</p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>long</ret><nametext>driver_pdl_dec_refc(ErlDrvPDL pdl)</nametext></name>
+ <fsummary></fsummary>
+ <desc>
+ <marker id="driver_pdl_dec_refc"></marker>
+ <p>This function decrements the reference count of
+ the port data lock passed as argument (<c>pdl</c>).</p>
+ <p>The current reference count after the decrement has
+ been performed is returned.</p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_monitor_process(ErlDrvPort port, ErlDrvTermData process, ErlDrvMonitor *monitor)</nametext></name>
+ <fsummary>Monitor a process from a driver</fsummary>
+ <desc>
+ <marker id="driver_monitor_process"></marker>
+ <p>Start monitoring a process from a driver. When a process is
+ monitored, a process exit will result in a call to the
+ provided <seealso marker="driver_entry#process_exit">process_exit</seealso> call-back
+ in the <seealso marker="driver_entry">ErlDrvEntry</seealso>
+ structure. The <c>ErlDrvMonitor</c> structure is filled in, for later
+ removal or compare.</p>
+ <p>The <c>process</c> parameter should be the return value of an
+ earlier call to <seealso marker="#driver_caller">driver_caller</seealso> or <seealso marker="#driver_connected">driver_connected</seealso> call.</p>
+ <p>The function returns 0 on success, &lt; 0 if no call-back is
+ provided and &gt; 0 if the process is no longer alive.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_demonitor_process(ErlDrvPort port, const ErlDrvMonitor *monitor)</nametext></name>
+ <fsummary>Stop monitoring a process from a driver</fsummary>
+ <desc>
+ <marker id="driver_demonitor_process"></marker>
+ <p>This function cancels an monitor created earlier. </p>
+ <p>The function returns 0 if a monitor was removed and &gt; 0
+ if the monitor did no longer exist.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>ErlDrvTermData</ret><nametext>driver_get_monitored_process(ErlDrvPort port, const ErlDrvMonitor *monitor)</nametext></name>
+ <fsummary>Retrieve the process id from a monitor</fsummary>
+ <desc>
+ <marker id="driver_get_monitored_process"></marker>
+ <p>The function returns the process id associated with a living
+ monitor. It can be used in the <c>process_exit</c> call-back to
+ get the process identification for the exiting process.</p>
+ <p>The function returns <c>driver_term_nil</c> if the monitor
+ no longer exists.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_compare_monitors(const ErlDrvMonitor *monitor1, const ErlDrvMonitor *monitor2)</nametext></name>
+ <fsummary>Compare two monitors</fsummary>
+ <desc>
+ <marker id="driver_compare_monitors"></marker>
+ <p>This function is used to compare two <c>ErlDrvMonitor</c>s. It
+ can also be used to imply some artificial order on monitors,
+ for whatever reason.</p>
+ <p>The function returns 0 if <c>monitor1</c> and
+ <c>monitor2</c> are equal, &lt; 0 if <c>monitor1</c> is less
+ than <c>monitor2</c> and &gt; 0 if <c>monitor1</c> is greater
+ than <c>monitor2</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>void</ret><nametext>add_driver_entry(ErlDrvEntry *de)</nametext></name>
+ <fsummary>Add a driver entry</fsummary>
+ <desc>
+ <marker id="add_driver_entry"></marker>
+ <p>This function adds a driver entry to the list of drivers
+ known by Erlang. The <seealso marker="driver_entry#init">init</seealso> function of the <c>de</c>
+ parameter is called.</p>
+ <note>
+ <p>To use this function for adding drivers residing in
+ dynamically loaded code is dangerous. If the driver code
+ for the added driver resides in the same dynamically
+ loaded module (i.e. <c>.so</c> file) as a normal
+ dynamically loaded driver (loaded with the <c>erl_ddll</c>
+ interface), the caller should call <seealso marker="#driver_lock_driver">driver_lock_driver</seealso> before
+ adding driver entries.</p>
+ <p>Use of this function is generally deprecated.</p>
+ </note>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>remove_driver_entry(ErlDrvEntry *de)</nametext></name>
+ <fsummary>Remove a driver entry</fsummary>
+ <desc>
+ <marker id="remove_driver_entry"></marker>
+ <p>This function removes a driver entry <c>de</c> previously
+ added with <c>add_driver_entry</c>.</p>
+ <p>Driver entries added by the <c>erl_ddll</c> erlang interface can
+ not be removed by using this interface.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>char*</ret><nametext>erl_errno_id(int error)</nametext></name>
+ <fsummary>Get erlang error atom name from error number</fsummary>
+ <desc>
+ <marker id="erl_errno_id"></marker>
+ <p>This function returns the atom name of the erlang error,
+ given the error number in <c>error</c>. Error atoms are:
+ <c>einval</c>, <c>enoent</c>, etc. It can be used to make
+ error terms from the driver.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>void</ret><nametext>set_busy_port(ErlDrvPort port, int on)</nametext></name>
+ <fsummary>Signal or unsignal port as busy</fsummary>
+ <desc>
+ <marker id="set_busy_port"></marker>
+ <p>This function set and resets the busy status of the port. If
+ <c>on</c> is 1, the port is set to busy, if it's 0 the port
+ is set to not busy.</p>
+ <p>When the port is busy, sending to it with <c>Port ! Data</c>
+ or <c>port_command/2</c>, will block the port owner process,
+ until the port is signaled as not busy.</p>
+ <p>If the
+ <seealso marker="driver_entry#driver_flags"><![CDATA[ERL_DRV_FLAG_SOFT_BUSY]]></seealso>
+ has been set in the
+ <seealso marker="driver_entry">driver_entry</seealso>,
+ data can be forced into the driver via
+ <seealso marker="erlang#erlang:port_command/3">port_command(Port, Data, [force])</seealso>
+ even though the driver has signaled that it is busy.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>void</ret><nametext>set_port_control_flags(ErlDrvPort port, int flags)</nametext></name>
+ <fsummary>Set flags on how to handle control entry function</fsummary>
+ <desc>
+ <marker id="set_port_control_flags"></marker>
+ <p>This function sets flags for how the <seealso marker="driver_entry#control">control</seealso> driver entry
+ function will return data to the port owner process. (The
+ <c>control</c> function is called from <c>port_control/3</c>
+ in erlang.)</p>
+ <p>Currently there are only two meaningful values for
+ <c>flags</c>: 0 means that data is returned in a list, and
+ <c>PORT_CONTROL_FLAG_BINARY</c> means data is returned as
+ a binary from <c>control</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_failure_eof(ErlDrvPort port)</nametext></name>
+ <fsummary>Fail with EOF</fsummary>
+ <desc>
+ <marker id="driver_failure_eof"></marker>
+ <p>This function signals to erlang that the driver has
+ encountered an EOF and should be closed, unless the port was
+ opened with the <c>eof</c> option, in that case eof is sent
+ to the port. Otherwise, the port is close and an
+ <c>'EXIT'</c> message is sent to the port owner process.</p>
+ <p>The return value is 0.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_failure_atom(ErlDrvPort port, char *string)</nametext></name>
+ <name><ret>int</ret><nametext>driver_failure_posix(ErlDrvPort port, int error)</nametext></name>
+ <name><ret>int</ret><nametext>driver_failure(ErlDrvPort port, int error)</nametext></name>
+ <fsummary>Fail with error</fsummary>
+ <desc>
+ <marker id="driver_failure_atom"></marker>
+ <marker id="driver_failure_posix"></marker>
+ <marker id="driver_failure"></marker>
+ <p>These functions signal to Erlang that the driver has
+ encountered an error and should be closed. The port is
+ closed and the tuple <c>{'EXIT', error, Err}</c>, is sent to
+ the port owner process, where error is an error atom
+ (<c>driver_failure_atom</c> and
+ <c>driver_failure_posix</c>), or an integer
+ (<c>driver_failure</c>).</p>
+ <p>The driver should fail only when in severe error situations,
+ when the driver cannot possibly keep open, for instance
+ buffer allocation gets out of memory. Normal errors is more
+ appropriate to handle with sending error codes with
+ <c>driver_output</c>.</p>
+ <p>The return value is 0.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>ErlDrvTermData</ret><nametext>driver_connected(ErlDrvPort port)</nametext></name>
+ <fsummary>Return the port owner process</fsummary>
+ <desc>
+ <marker id="driver_connected"></marker>
+ <p>This function returns the port owner process.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>ErlDrvTermData</ret><nametext>driver_caller(ErlDrvPort port)</nametext></name>
+ <fsummary>Return the process making the driver call</fsummary>
+ <desc>
+ <marker id="driver_caller"></marker>
+ <p>This function returns the process id of the process that
+ made the current call to the driver. The process id can be
+ used with <c>driver_send_term</c> to send back data to the
+ caller. <c>driver_caller()</c> only return valid data
+ when currently executing in one of the following driver
+ callbacks:</p>
+ <taglist>
+ <tag><seealso marker="driver_entry#start">start</seealso></tag>
+ <item>Called from <c>open_port/2</c>.</item>
+ <tag><seealso marker="driver_entry#output">output</seealso></tag>
+ <item>Called from <c>erlang:send/2</c>, and
+ <c>erlang:port_command/2</c></item>
+ <tag><seealso marker="driver_entry#outputv">outputv</seealso></tag>
+ <item>Called from <c>erlang:send/2</c>, and
+ <c>erlang:port_command/2</c></item>
+ <tag><seealso marker="driver_entry#control">control</seealso></tag>
+ <item>Called from <c>erlang:port_control/3</c></item>
+ <tag><seealso marker="driver_entry#call">call</seealso></tag>
+ <item>Called from <c>erlang:port_call/3</c></item>
+ </taglist>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_output_term(ErlDrvPort port, ErlDrvTermData* term, int n)</nametext></name>
+ <fsummary>Send term data from driver to port owner</fsummary>
+ <desc>
+ <marker id="driver_output_term"></marker>
+ <p>This functions sends data in the special driver term
+ format. This is a fast way to deliver term data from a
+ driver. It also needs no binary conversion, so the port
+ owner process receives data as normal Erlang terms.</p>
+ <p>The <c>term</c> parameter points to an array of
+ <c>ErlDrvTermData</c>, with <c>n</c> elements. This array
+ contains terms described in the driver term format. Every
+ term consists of one to four elements in the array. The
+ term first has a term type, and then arguments.</p>
+ <p>Tuple and lists (with the exception of strings, see below),
+ are built in reverse polish notation, so that to build a
+ tuple, the elements are given first, and then the tuple
+ term, with a count. Likewise for lists.</p>
+ <p>A tuple must be specified with the number of elements. (The
+ elements precedes the <c>ERL_DRV_TUPLE</c> term.)</p>
+ <p>A list must be specified with the number of elements,
+ including the tail, which is the last term preceding
+ <c>ERL_DRV_LIST</c>.</p>
+ <p>The special term <c>ERL_DRV_STRING_CONS</c> is used to
+ "splice" in a string in a list, a string given this way is
+ not a list per se, but the elements are elements of the
+ surrounding list.</p>
+ <pre>
+Term type Argument(s)
+===========================================
+ERL_DRV_NIL
+ERL_DRV_ATOM ErlDrvTermData atom (from driver_mk_atom(char *string))
+ERL_DRV_INT ErlDrvSInt integer
+ERL_DRV_UINT ErlDrvUInt integer
+ERL_DRV_INT64 ErlDrvSInt64 *integer_ptr
+ERL_DRV_UINT64 ErlDrvUInt64 *integer_ptr
+ERL_DRV_PORT ErlDrvTermData port (from driver_mk_port(ErlDrvPort port))
+ERL_DRV_BINARY ErlDrvBinary *bin, ErlDrvUInt len, ErlDrvUInt offset
+ERL_DRV_BUF2BINARY char *buf, ErlDrvUInt len
+ERL_DRV_STRING char *str, int len
+ERL_DRV_TUPLE int sz
+ERL_DRV_LIST int sz
+ERL_DRV_PID ErlDrvTermData pid (from driver_connected(ErlDrvPort port) or driver_caller(ErlDrvPort port))
+ERL_DRV_STRING_CONS char *str, int len
+ERL_DRV_FLOAT double *dbl
+ERL_DRV_EXT2TERM char *buf, ErlDrvUInt len
+ </pre>
+ <p>The unsigned integer data type <c>ErlDrvUInt</c> and the
+ signed integer data type <c>ErlDrvSInt</c> are 64 bits wide
+ on a 64 bit runtime system and 32 bits wide on a 32 bit
+ runtime system. They were introduced in erts version 5.6,
+ and replaced some of the <c>int</c> arguments in the list above.
+ </p>
+ <p>The unsigned integer data type <c>ErlDrvUInt64</c> and the
+ signed integer data type <c>ErlDrvSInt64</c> are always 64 bits
+ wide. They were introduced in erts version 5.7.4.
+ </p>
+
+ <p>To build the tuple <c>{tcp, Port, [100 | Binary]}</c>, the
+ following call could be made.</p>
+ <code type="none"><![CDATA[
+ ErlDrvBinary* bin = ...
+ ErlDrvPort port = ...
+ ErlDrvTermData spec[] = {
+ ERL_DRV_ATOM, driver_mk_atom("tcp"),
+ ERL_DRV_PORT, driver_mk_port(port),
+ ERL_DRV_INT, 100,
+ ERL_DRV_BINARY, bin, 50, 0,
+ ERL_DRV_LIST, 2,
+ ERL_DRV_TUPLE, 3,
+ };
+ driver_output_term(port, spec, sizeof(spec) / sizeof(spec[0]));
+ ]]>
+ </code>
+ <p>Where <c>bin</c> is a driver binary of length at least 50
+ and <c>port</c> is a port handle. Note that the <c>ERL_DRV_LIST</c>
+ comes after the elements of the list, likewise the
+ <c>ERL_DRV_TUPLE</c>.</p>
+ <p>The term <c>ERL_DRV_STRING_CONS</c> is a way to construct
+ strings. It works differently from how <c>ERL_DRV_STRING</c>
+ works. <c>ERL_DRV_STRING_CONS</c> builds a string list in
+ reverse order, (as opposed to how <c>ERL_DRV_LIST</c>
+ works), concatenating the strings added to a list. The tail
+ must be given before <c>ERL_DRV_STRING_CONS</c>.</p>
+ <p>The <c>ERL_DRV_STRING</c> constructs a string, and ends
+ it. (So it's the same as <c>ERL_DRV_NIL</c> followed by
+ <c>ERL_DRV_STRING_CONS</c>.)</p>
+ <code type="none"><![CDATA[
+ /* to send [x, "abc", y] to the port: */
+ ErlDrvTermData spec[] = {
+ ERL_DRV_ATOM, driver_mk_atom("x"),
+ ERL_DRV_STRING, (ErlDrvTermData)"abc", 3,
+ ERL_DRV_ATOM, driver_mk_atom("y"),
+ ERL_DRV_NIL,
+ ERL_DRV_LIST, 4
+ };
+ driver_output_term(port, spec, sizeof(spec) / sizeof(spec[0]));
+ ]]></code>
+ <p></p>
+ <code type="none"><![CDATA[
+ /* to send "abc123" to the port: */
+ ErlDrvTermData spec[] = {
+ ERL_DRV_NIL, /* with STRING_CONS, the tail comes first */
+ ERL_DRV_STRING_CONS, (ErlDrvTermData)"123", 3,
+ ERL_DRV_STRING_CONS, (ErlDrvTermData)"abc", 3,
+ };
+ driver_output_term(port, spec, sizeof(spec) / sizeof(spec[0]));
+ ]]></code>
+ <p>The <c>ERL_DRV_EXT2TERM</c> term type is used for passing a
+ term encoded with the
+ <seealso marker="erl_ext_dist">external format</seealso>,
+ i.e., a term that has been encoded by
+ <seealso marker="erts:erlang#term_to_binary/2">erlang:term_to_binary</seealso>,
+ <seealso marker="erl_interface:ei">erl_interface</seealso>, etc.
+ For example, if <c>binp</c> is a pointer to an <c>ErlDrvBinary</c>
+ that contains the term <c>{17, 4711}</c> encoded with the
+ <seealso marker="erl_ext_dist">external format</seealso>
+ and you want to wrap it in a two tuple with the tag <c>my_tag</c>,
+ i.e., <c>{my_tag, {17, 4711}}</c>, you can do as follows:
+ </p>
+ <code type="none"><![CDATA[
+ ErlDrvTermData spec[] = {
+ ERL_DRV_ATOM, driver_mk_atom("my_tag"),
+ ERL_DRV_EXT2TERM, (ErlDrvTermData) binp->orig_bytes, binp->orig_size
+ ERL_DRV_TUPLE, 2,
+ };
+ driver_output_term(port, spec, sizeof(spec) / sizeof(spec[0]));
+ ]]></code>
+ <p>If you want to pass a binary and doesn't already have the content
+ of the binary in an <c>ErlDrvBinary</c>, you can benefit from using
+ <c>ERL_DRV_BUF2BINARY</c> instead of creating an <c>ErlDrvBinary</c>
+ via <c>driver_alloc_binary()</c> and then pass the binary via
+ <c>ERL_DRV_BINARY</c>. The runtime system will often allocate
+ binaries smarter if <c>ERL_DRV_BUF2BINARY</c> is used.
+ However, if the content of the binary to pass already resides in
+ an <c>ErlDrvBinary</c>, it is normally better to pass the binary
+ using <c>ERL_DRV_BINARY</c> and the <c>ErlDrvBinary</c> in question.
+ </p>
+ <p>The <c>ERL_DRV_UINT</c>, <c>ERL_DRV_BUF2BINARY</c>, and
+ <c>ERL_DRV_EXT2TERM</c> term types were introduced in the 5.6
+ version of erts.
+ </p>
+ <p>Note that this function is <em>not</em> thread-safe, not
+ even when the emulator with SMP support is used.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>ErlDrvTermData</ret><nametext>driver_mk_atom(char* string)</nametext></name>
+ <fsummary>Make an atom from a name</fsummary>
+ <desc>
+ <marker id="driver_mk_atom"></marker>
+ <p>This function returns an atom given a name
+ <c>string</c>. The atom is created and won't change, so the
+ return value may be saved and reused, which is faster than
+ looking up the atom several times.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>ErlDrvTermData</ret><nametext>driver_mk_port(ErlDrvPort port)</nametext></name>
+ <fsummary>Make a erlang term port from a port</fsummary>
+ <desc>
+ <marker id="driver_mk_port"></marker>
+ <p>This function converts a port handle to the erlang term
+ format, usable in the <c>driver_output_send</c> function.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_send_term(ErlDrvPort port, ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name>
+ <fsummary>Send term data to other process than port owner process</fsummary>
+ <desc>
+ <marker id="driver_send_term"></marker>
+ <p>This function is the only way for a driver to send data to
+ <em>other</em> processes than the port owner process. The
+ <c>receiver</c> parameter specifies the process to receive
+ the data.</p>
+ <p>The parameters <c>term</c> and <c>n</c> does the same thing
+ as in <seealso marker="#driver_output_term">driver_output_term</seealso>.</p>
+ <p>This function is only thread-safe when the emulator with SMP
+ support is used.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>long</ret><nametext>driver_async (ErlDrvPort port, unsigned int* key, void (*async_invoke)(void*), void* async_data, void (*async_free)(void*))</nametext></name>
+ <fsummary>Perform an asynchronous call within a driver</fsummary>
+ <desc>
+ <marker id="driver_async"></marker>
+ <p>This function performs an asynchronous call. The function
+ <c>async_invoke</c> is invoked in a thread separate from the
+ emulator thread. This enables the driver to perform
+ time-consuming, blocking operations without blocking the
+ emulator.</p>
+ <p>Erlang is by default started without an async thread pool. The
+ number of async threads that the runtime system should use
+ is specified by the
+ <seealso marker="erl#async_thread_pool_size">+A</seealso>
+ command line argument of <seealso marker="erl">erl(1)</seealso>.
+ If no async thread pool is available, the call is made
+ synchronously in the thread calling <c>driver_async()</c>. The
+ current number of async threads in the async thread pool can be
+ retrieved via
+ <seealso marker="#driver_system_info">driver_system_info()</seealso>.</p>
+ <p>If there is a thread pool available, a thread will be
+ used. If the <c>key</c> argument is null, the threads from the
+ pool are used in a round-robin way, each call to
+ <c>driver_async</c> uses the next thread in the pool. With the
+ <c>key</c> argument set, this behaviour is changed. The two
+ same values of <c>*key</c> always get the same thread.</p>
+ <p>To make sure that a driver instance always uses the same
+ thread, the following call can be used:</p>
+ <p></p>
+ <code type="none"><![CDATA[
+ unsigned int myKey = (unsigned int) myPort;
+
+ r = driver_async(myPort, &myKey, myData, myFunc);
+ ]]></code>
+ <p>It is enough to initialize <c>myKey</c> once for each
+ driver instance.</p>
+ <p>If a thread is already working, the calls will be
+ queued up and executed in order. Using the same thread for
+ each driver instance ensures that the calls will be made in
+ sequence.</p>
+ <p>The <c>async_data</c> is the argument to the functions
+ <c>async_invoke</c> and <c>async_free</c>. It's typically a
+ pointer to a structure that contains a pipe or event that
+ can be used to signal that the async operation completed.
+ The data should be freed in <c>async_free</c>, because it's
+ called if <c>driver_async_cancel</c> is called.</p>
+ <p>When the async operation is done, <seealso marker="driver_entry#ready_async">ready_async</seealso> driver
+ entry function is called. If <c>async_ready</c> is null in
+ the driver entry, the <c>async_free</c> function is called
+ instead.</p>
+ <p>The return value is a handle to the asynchronous task, which
+ can be used as argument to <c>driver_async_cancel</c>.</p>
+ <note>
+ <p>As of erts version 5.5.4.3 the default stack size for
+ threads in the async-thread pool is 16 kilowords,
+ i.e., 64 kilobyte on 32-bit architectures.
+ This small default size has been chosen since the
+ amount of async-threads might be quite large. The
+ default stack size is enough for drivers delivered
+ with Erlang/OTP, but might not be sufficiently large
+ for other dynamically linked in drivers that use the
+ driver_async() functionality. A suggested stack size
+ for threads in the async-thread pool can be configured
+ via the
+ <seealso marker="erl#async_thread_stack_size">+a</seealso>
+ command line argument of
+ <seealso marker="erl">erl(1)</seealso>.</p>
+ </note>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_async_cancel(long id)</nametext></name>
+ <fsummary>Cancel an asynchronous call</fsummary>
+ <desc>
+ <marker id="driver_async_cancel"></marker>
+ <p>This function cancels an asynchronous operation, by removing
+ it from the queue. Only functions in the queue can be
+ cancelled; if a function is executing, it's too late to
+ cancel it. The <c>async_free</c> function is also called.</p>
+ <p>The return value is 1 if the operation was removed from the
+ queue, otherwise 0.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>int</ret><nametext>driver_lock_driver(ErlDrvPort port)</nametext></name>
+ <fsummary>Make sure the driver is never unloaded</fsummary>
+ <desc>
+ <marker id="driver_lock_driver"></marker>
+ <p>This function locks the driver used by the port <c>port</c>
+ in memory for the rest of the emulator process
+ lifetime. After this call, the driver behaves as one of Erlang's
+ statically linked in drivers.</p>
+ </desc>
+ </func>
+ <func>
+ <name><ret>ErlDrvPort</ret><nametext>driver_create_port(ErlDrvPort port, ErlDrvTermData owner_pid, char* name, ErlDrvData drv_data)</nametext></name>
+ <fsummary>Create a new port (driver instance)</fsummary>
+ <desc>
+ <p>This function creates a new port executing the same driver
+ code as the port creating the new port.
+ A short description of the arguments:</p>
+ <taglist>
+ <tag><c>port</c></tag>
+ <item>The port handle of the port (driver instance) creating
+ the new port.</item>
+ <tag><c>owner_pid</c></tag>
+ <item>The process id of the Erlang process which will be
+ owner of the new port. This process will be linked
+ to the new port. You usually want to use
+ <c>driver_caller(port)</c> as <c>owner_pid</c>.</item>
+ <tag><c>name</c></tag>
+ <item>The port name of the new port. You usually want to
+ use the same port name as the driver name
+ (<seealso marker="driver_entry#driver_name">driver_name</seealso>
+ field of the
+ <seealso marker="driver_entry">driver_entry</seealso>).</item>
+ <tag><c>drv_data</c></tag>
+ <item>The driver defined handle that will be passed in subsequent
+ calls to driver call-backs. Note, that the
+ <seealso marker="driver_entry#start">driver start call-back</seealso>
+ will not be called for this new driver instance.
+ The driver defined handle is normally created in the
+ <seealso marker="driver_entry#start">driver start call-back</seealso>
+ when a port is created via
+ <seealso marker="erts:erlang#open_port/2">erlang:open_port/2</seealso>. </item>
+ </taglist>
+ <p>The caller of <c>driver_create_port()</c> is allowed to
+ manipulate the newly created port when <c>driver_create_port()</c>
+ has returned. When
+ <seealso marker="#smp_support">port level locking</seealso>
+ is used, the creating port is, however, only allowed to
+ manipulate the newly created port until the current driver
+ call-back that was called by the emulator returns.</p>
+ <note>
+ <p>When
+ <seealso marker="#smp_support">port level locking</seealso>
+ is used, the creating port is only allowed to manipulate
+ the newly created port until the current driver call-back
+ returns.</p>
+ </note>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_drv_thread_create(char *name,
+ ErlDrvTid *tid,
+ void * (*func)(void *),
+ void *arg,
+ ErlDrvThreadOpts *opts)</nametext></name>
+ <fsummary>Create a thread</fsummary>
+ <desc>
+ <marker id="erl_drv_thread_create"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>name</c></tag>
+ <item>A string identifying the created thread. It will be used
+ to identify the thread in planned future debug
+ functionality.
+ </item>
+ <tag><c>tid</c></tag>
+ <item>A pointer to a thread identifier variable.</item>
+ <tag><c>func</c></tag>
+ <item>A pointer to a function to execute in the created thread.</item>
+ <tag><c>arg</c></tag>
+ <item>A pointer to argument to the <c>func</c> function.</item>
+ <tag><c>opts</c></tag>
+ <item>A pointer to thread options to use or <c>NULL</c>.</item>
+ </taglist>
+ <p>This function creates a new thread. On success <c>0</c> is returned;
+ otherwise, an <c>errno</c> value is returned to indicate the error.
+ The newly created thread will begin executing in the function pointed
+ to by <c>func</c>, and <c>func</c> will be passed <c>arg</c> as
+ argument. When <c>erl_drv_thread_create()</c> returns the thread
+ identifier of the newly created thread will be available in
+ <c>*tid</c>. <c>opts</c> can be either a <c>NULL</c> pointer, or a
+ pointer to an
+ <seealso marker="#ErlDrvThreadOpts">ErlDrvThreadOpts</seealso>
+ structure. If <c>opts</c> is a <c>NULL</c> pointer, default options
+ will be used; otherwise, the passed options will be used.
+ </p>
+ <warning><p>You are not allowed to allocate the
+ <seealso marker="#ErlDrvThreadOpts">ErlDrvThreadOpts</seealso>
+ structure by yourself. It has to be allocated and
+ initialized by
+ <seealso marker="#erl_drv_thread_opts_create">erl_drv_thread_opts_create()</seealso>.
+ </p></warning>
+ <p>The created thread will terminate either when <c>func</c> returns
+ or if
+ <seealso marker="#erl_drv_thread_exit">erl_drv_thread_exit()</seealso>
+ is called by the thread. The exit value of the thread is either
+ returned from <c>func</c> or passed as argument to
+ <seealso marker="#erl_drv_thread_exit">erl_drv_thread_exit()</seealso>.
+ The driver creating the thread has the responsibility of joining the
+ thread, via
+ <seealso marker="#erl_drv_thread_join">erl_drv_thread_join()</seealso>,
+ before the driver is unloaded. It is not possible to create
+ "detached" threads, i.e., threads that don't need to be joined.
+ </p>
+ <warning><p>All created threads need to be joined by the driver before
+ it is unloaded. If the driver fails to join all threads
+ created before it is unloaded, the runtime system will
+ most likely crash when the code of the driver is unloaded.
+ </p></warning>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlDrvThreadOpts *</ret><nametext>erl_drv_thread_opts_create(char *name)</nametext></name>
+ <fsummary>Create thread options</fsummary>
+ <desc>
+ <marker id="erl_drv_thread_opts_create"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>name</c></tag>
+ <item>A string identifying the created thread options. It will be used
+ to identify the thread options in planned future debug
+ functionality.
+ </item>
+ </taglist>
+ <p>This function allocates and initialize a thread option
+ structure. On failure <c>NULL</c> is returned. A thread option
+ structure is used for passing options to
+ <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>.
+ If the structure isn't modified before it is passed to
+ <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>,
+ the default values will be used.
+ </p>
+ <warning><p>You are not allowed to allocate the
+ <seealso marker="#ErlDrvThreadOpts">ErlDrvThreadOpts</seealso>
+ structure by yourself. It has to be allocated and
+ initialized by <c>erl_drv_thread_opts_create()</c>.
+ </p></warning>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_thread_opts_destroy(ErlDrvThreadOpts *opts)</nametext></name>
+ <fsummary>Destroy thread options</fsummary>
+ <desc>
+ <marker id="erl_drv_thread_opts_destroy"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>opts</c></tag>
+ <item>A pointer to thread options to destroy.</item>
+ </taglist>
+ <p>This function destroys thread options previously created by
+ <seealso marker="#erl_drv_thread_opts_create">erl_drv_thread_opts_create()</seealso>.
+ </p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_thread_exit(void *exit_value)</nametext></name>
+ <fsummary>Terminate calling thread</fsummary>
+ <desc>
+ <marker id="erl_drv_thread_exit"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>exit_value</c></tag>
+ <item>A pointer to an exit value or <c>NULL</c>.</item>
+ </taglist>
+ <p>This function terminates the calling thread with the exit
+ value passed as argument. You are only allowed to terminate
+ threads created with
+ <seealso marker="#erl_drv_thread_create">erl_drv_thread_create()</seealso>.
+ The exit value can later be retrieved by another thread via
+ <seealso marker="#erl_drv_thread_join">erl_drv_thread_join()</seealso>.
+ </p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_drv_thread_join(ErlDrvTid tid, void **exit_value)</nametext></name>
+ <fsummary>Join with another thread</fsummary>
+ <desc>
+ <marker id="erl_drv_thread_join"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>tid</c></tag>
+ <item>The thread identifier of the thread to join.</item>
+ <tag><c>exit_value</c></tag>
+ <item>A pointer to a pointer to an exit value, or <c>NULL</c>.</item>
+ </taglist>
+ <p>This function joins the calling thread with another thread, i.e.,
+ the calling thread is blocked until the thread identified by
+ <c>tid</c> has terminated. On success <c>0</c> is returned;
+ otherwise, an <c>errno</c> value is returned to indicate the error.
+ A thread can only be joined once. The behavior of joining
+ more than once is undefined, an emulator crash is likely. If
+ <c>exit_value == NULL</c>, the exit value of the terminated thread
+ will be ignored; otherwise, the exit value of the terminated thread
+ will be stored at <c>*exit_value</c>.
+ </p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlDrvTid</ret><nametext>erl_drv_thread_self(void)</nametext></name>
+ <fsummary>Get the thread identifier of the current thread</fsummary>
+ <desc>
+ <marker id="erl_drv_thread_self"></marker>
+ <p>This function returns the thread identifier of the
+ calling thread.
+ </p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_drv_equal_tids(ErlDrvTid tid1, ErlDrvTid tid2)</nametext></name>
+ <fsummary>Compare thread identifiers for equality</fsummary>
+ <desc>
+ <marker id="erl_drv_equal_tids"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>tid1</c></tag>
+ <item>A thread identifier.</item>
+ <tag><c>tid2</c></tag>
+ <item>A thread identifier.</item>
+ </taglist>
+ <p>This function compares two thread identifiers for equality,
+ and returns <c>0</c> it they aren't equal, and
+ a value not equal to <c>0</c> if they are equal.</p>
+ <note><p>A Thread identifier may be reused very quickly after
+ a thread has terminated. Therefore, if a thread
+ corresponding to one of the involved thread identifiers
+ has terminated since the thread identifier was saved,
+ the result of <c>erl_drv_equal_tids()</c> might not give
+ expected result.
+ </p></note>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlDrvMutex *</ret><nametext>erl_drv_mutex_create(char *name)</nametext></name>
+ <fsummary>Create a mutex</fsummary>
+ <desc>
+ <marker id="erl_drv_mutex_create"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>name</c></tag>
+ <item>A string identifying the created mutex. It will be used
+ to identify the mutex in planned future debug functionality.
+ </item>
+ </taglist>
+ <p>This function creates a mutex and returns a pointer to it. On
+ failure <c>NULL</c> is returned. The driver creating the mutex
+ has the responsibility of destroying it before the driver is
+ unloaded.
+ </p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_mutex_destroy(ErlDrvMutex *mtx)</nametext></name>
+ <fsummary>Destroy a mutex</fsummary>
+ <desc>
+ <marker id="erl_drv_mutex_destroy"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>mtx</c></tag>
+ <item>A pointer to a mutex to destroy.</item>
+ </taglist>
+ <p>This function destroys a mutex previously created by
+ <seealso marker="#erl_drv_mutex_create">erl_drv_mutex_create()</seealso>.
+ The mutex has to be in an unlocked state before being
+ destroyed.
+ </p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_mutex_lock(ErlDrvMutex *mtx)</nametext></name>
+ <fsummary>Lock a mutex</fsummary>
+ <desc>
+ <marker id="erl_drv_mutex_lock"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>mtx</c></tag>
+ <item>A pointer to a mutex to lock.</item>
+ </taglist>
+ <p>This function locks a mutex. The calling thread will be
+ blocked until the mutex has been locked. A thread
+ which currently has locked the mutex may <em>not</em> lock
+ the same mutex again.
+ </p>
+ <warning><p>If you leave a mutex locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.
+ </p></warning>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_drv_mutex_trylock(ErlDrvMutex *mtx)</nametext></name>
+ <fsummary>Try lock a mutex</fsummary>
+ <desc>
+ <marker id="erl_drv_mutex_trylock"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>mtx</c></tag>
+ <item>A pointer to a mutex to try to lock.</item>
+ </taglist>
+ <p>This function tries to lock a mutex. If successful <c>0</c>,
+ is returned; otherwise, <c>EBUSY</c> is returned. A thread
+ which currently has locked the mutex may <em>not</em> try to
+ lock the same mutex again.
+ </p>
+ <warning><p>If you leave a mutex locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.
+ </p></warning>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_mutex_unlock(ErlDrvMutex *mtx)</nametext></name>
+ <fsummary>Unlock a mutex</fsummary>
+ <desc>
+ <marker id="erl_drv_mutex_unlock"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>mtx</c></tag>
+ <item>A pointer to a mutex to unlock.</item>
+ </taglist>
+ <p>This function unlocks a mutex. The mutex currently has to be
+ locked by the calling thread.
+ </p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlDrvCond *</ret><nametext>erl_drv_cond_create(char *name)</nametext></name>
+ <fsummary>Create a condition variable</fsummary>
+ <desc>
+ <marker id="erl_drv_cond_create"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>name</c></tag>
+ <item>A string identifying the created condition variable. It
+ will be used to identify the condition variable in planned
+ future debug functionality.
+ </item>
+ </taglist>
+ <p>This function creates a condition variable and returns a
+ pointer to it. On failure <c>NULL</c> is returned. The driver
+ creating the condition variable has the responsibility of
+ destroying it before the driver is unloaded.</p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_cond_destroy(ErlDrvCond *cnd)</nametext></name>
+ <fsummary>Destroy a condition variable</fsummary>
+ <desc>
+ <marker id="erl_drv_cond_destroy"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>cnd</c></tag>
+ <item>A pointer to a condition variable to destroy.</item>
+ </taglist>
+ <p>This function destroys a condition variable previously
+ created by
+ <seealso marker="#erl_drv_cond_create">erl_drv_cond_create()</seealso>.
+ </p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_cond_signal(ErlDrvCond *cnd)</nametext></name>
+ <fsummary>Signal on a condition variable</fsummary>
+ <desc>
+ <marker id="erl_drv_cond_signal"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>cnd</c></tag>
+ <item>A pointer to a condition variable to signal on.</item>
+ </taglist>
+ <p>This function signals on a condition variable. That is, if
+ other threads are waiting on the condition variable being
+ signaled, <em>one</em> of them will be woken.
+ </p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_cond_broadcast(ErlDrvCond *cnd)</nametext></name>
+ <fsummary>Broadcast on a condition variable</fsummary>
+ <desc>
+ <marker id="erl_drv_cond_broadcast"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>cnd</c></tag>
+ <item>A pointer to a condition variable to broadcast on.</item>
+ </taglist>
+ <p>This function broadcasts on a condition variable. That is, if
+ other threads are waiting on the condition variable being
+ broadcasted on, <em>all</em> of them will be woken.
+ </p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_cond_wait(ErlDrvCond *cnd, ErlDrvMutex *mtx)</nametext></name>
+ <fsummary>Wait on a condition variable</fsummary>
+ <desc>
+ <marker id="erl_drv_cond_wait"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>cnd</c></tag>
+ <item>A pointer to a condition variable to wait on.</item>
+ <tag><c>mtx</c></tag>
+ <item>A pointer to a mutex to unlock while waiting.</item>
+ <tag><c></c></tag>
+ <item></item>
+ </taglist>
+ <p>This function waits on a condition variable. The calling
+ thread is blocked until another thread wakes it by signaling
+ or broadcasting on the condition variable. Before the calling
+ thread is blocked it unlocks the mutex passed as argument, and
+ when the calling thread is woken it locks the same mutex before
+ returning. That is, the mutex currently has to be locked by
+ the calling thread when calling this function.
+ </p>
+ <note><p><c>erl_drv_cond_wait()</c> might return even though
+ no-one has signaled or broadcasted on the condition
+ variable. Code calling <c>erl_drv_cond_wait()</c> should
+ always be prepared for <c>erl_drv_cond_wait()</c>
+ returning even though the condition that the thread was
+ waiting for hasn't occurred. That is, when returning from
+ <c>erl_drv_cond_wait()</c> always check if the condition
+ has occurred, and if not call <c>erl_drv_cond_wait()</c>
+ again.
+ </p></note>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>ErlDrvRWLock *</ret><nametext>erl_drv_rwlock_create(char *name)</nametext></name>
+ <fsummary>Create an rwlock</fsummary>
+ <desc>
+ <marker id="erl_drv_rwlock_create"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>name</c></tag>
+ <item>A string identifying the created rwlock. It will be used to
+ identify the rwlock in planned future debug functionality.
+ </item>
+ </taglist>
+ <p>This function creates an rwlock and returns a pointer to it. On
+ failure <c>NULL</c> is returned. The driver creating the rwlock
+ has the responsibility of destroying it before the driver is
+ unloaded.
+ </p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_rwlock_destroy(ErlDrvRWLock *rwlck)</nametext></name>
+ <fsummary>Destroy an rwlock</fsummary>
+ <desc>
+ <marker id="erl_drv_rwlock_destroy"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>rwlck</c></tag>
+ <item>A pointer to an rwlock to destroy.</item>
+ </taglist>
+ <p>This function destroys an rwlock previously created by
+ <seealso marker="#erl_drv_rwlock_create">erl_drv_rwlock_create()</seealso>.
+ The rwlock has to be in an unlocked state before being destroyed.
+ </p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_rwlock_rlock(ErlDrvRWLock *rwlck)</nametext></name>
+ <fsummary>Read lock an rwlock</fsummary>
+ <desc>
+ <marker id="erl_drv_rwlock_rlock"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>rwlck</c></tag>
+ <item>A pointer to an rwlock to read lock.</item>
+ </taglist>
+ <p>This function read locks an rwlock. The calling thread will be
+ blocked until the rwlock has been read locked. A thread
+ which currently has read or read/write locked the rwlock may
+ <em>not</em> lock the same rwlock again.
+ </p>
+ <warning><p>If you leave an rwlock locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.
+ </p></warning>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_drv_rwlock_tryrlock(ErlDrvRWLock *rwlck)</nametext></name>
+ <fsummary>Try to read lock an rwlock</fsummary>
+ <desc>
+ <marker id="erl_drv_rwlock_tryrlock"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>rwlck</c></tag>
+ <item>A pointer to an rwlock to try to read lock.</item>
+ </taglist>
+ <p>This function tries to read lock an rwlock. If successful
+ <c>0</c>, is returned; otherwise, <c>EBUSY</c> is returned.
+ A thread which currently has read or read/write locked the
+ rwlock may <em>not</em> try to lock the same rwlock again.
+ </p>
+ <warning><p>If you leave an rwlock locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.
+ </p></warning>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_rwlock_runlock(ErlDrvRWLock *rwlck)</nametext></name>
+ <fsummary>Read unlock an rwlock</fsummary>
+ <desc>
+ <marker id="erl_drv_rwlock_runlock"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>rwlck</c></tag>
+ <item>A pointer to an rwlock to read unlock.</item>
+ </taglist>
+ <p>This function read unlocks an rwlock. The rwlock currently
+ has to be read locked by the calling thread.
+ </p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_rwlock_rwlock(ErlDrvRWLock *rwlck)</nametext></name>
+ <fsummary>Read/Write lock an rwlock</fsummary>
+ <desc>
+ <marker id="erl_drv_rwlock_rwlock"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>rwlck</c></tag>
+ <item>A pointer to an rwlock to read/write lock.</item>
+ </taglist>
+ <p>This function read/write locks an rwlock. The calling thread
+ will be blocked until the rwlock has been read/write locked.
+ A thread which currently has read or read/write locked the
+ rwlock may <em>not</em> lock the same rwlock again.
+ </p>
+ <warning><p>If you leave an rwlock locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.
+ </p></warning>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_drv_rwlock_tryrwlock(ErlDrvRWLock *rwlck)</nametext></name>
+ <fsummary>Try to read/write lock an rwlock</fsummary>
+ <desc>
+ <marker id="erl_drv_rwlock_tryrwlock"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>rwlck</c></tag>
+ <item>A pointer to an rwlock to try to read/write lock.</item>
+ </taglist>
+ <p>This function tries to read/write lock an rwlock. If successful
+ <c>0</c>, is returned; otherwise, <c>EBUSY</c> is returned.
+ A thread which currently has read or read/write locked the
+ rwlock may <em>not</em> try to lock the same rwlock again.
+ </p>
+ <warning><p>If you leave an rwlock locked in an emulator thread
+ when you let the thread out of your control, you will
+ <em>very likely</em> deadlock the whole emulator.
+ </p></warning>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_rwlock_rwunlock(ErlDrvRWLock *rwlck)</nametext></name>
+ <fsummary>Read/Write unlock an rwlock</fsummary>
+ <desc>
+ <marker id="erl_drv_rwlock_rwunlock"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>rwlck</c></tag>
+ <item>A pointer to an rwlock to read/write unlock.</item>
+ </taglist>
+ <p>This function read/write unlocks an rwlock. The rwlock
+ currently has to be read/write locked by the calling thread.
+ </p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_drv_tsd_key_create(char *name, ErlDrvTSDKey *key)</nametext></name>
+ <fsummary>Create a thread specific data key</fsummary>
+ <desc>
+ <marker id="erl_drv_tsd_key_create"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>name</c></tag>
+ <item>A string identifying the created key. It will be used
+ to identify the key in planned future debug
+ functionality.
+ </item>
+ <tag><c>key</c></tag>
+ <item>A pointer to a thread specific data key variable.</item>
+ </taglist>
+ <p>This function creates a thread specific data key. On success
+ <c>0</c> is returned; otherwise, an <c>errno</c> value is returned
+ to indicate the error. The driver creating the key has the
+ responsibility of destroying it before the driver is unloaded.
+ </p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_tsd_key_destroy(ErlDrvTSDKey key)</nametext></name>
+ <fsummary>Destroy a thread specific data key</fsummary>
+ <desc>
+ <marker id="erl_drv_tsd_key_destroy"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>key</c></tag>
+ <item>A thread specific data key to destroy.</item>
+ </taglist>
+ <p>This function destroys a thread specific data key
+ previously created by
+ <seealso marker="#erl_drv_tsd_key_create">erl_drv_tsd_key_create()</seealso>.
+ All thread specific data using this key in all threads
+ have to be cleared (see
+ <seealso marker="#erl_drv_tsd_set">erl_drv_tsd_set()</seealso>)
+ prior to the call to <c>erl_drv_tsd_key_destroy()</c>.
+ </p>
+ <warning><p>A destroyed key is very likely to be reused soon.
+ Therefore, if you fail to clear the thread specific
+ data using this key in a thread prior to destroying
+ the key, you will <em>very likely</em> get unexpected
+ errors in other parts of the system.
+ </p></warning>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void</ret><nametext>erl_drv_tsd_set(ErlDrvTSDKey key, void *data)</nametext></name>
+ <fsummary>Set thread specific data</fsummary>
+ <desc>
+ <marker id="erl_drv_tsd_set"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>key</c></tag>
+ <item>A thread specific data key.</item>
+ <tag><c>data</c></tag>
+ <item>A pointer to data to associate with <c>key</c>
+ in calling thread.
+ </item>
+ </taglist>
+ <p>This function sets thread specific data associated with
+ <c>key</c> for the calling thread. You are only allowed to set
+ thread specific data for threads while they are fully under your
+ control. For example, if you set thread specific data in a thread
+ calling a driver call-back function, it has to be cleared, i.e.
+ set to <c>NULL</c>, before returning from the driver call-back
+ function.
+ </p>
+ <warning><p>If you fail to clear thread specific data in an
+ emulator thread before letting it out of your control,
+ you might not ever be able to clear this data with
+ later unexpected errors in other parts of the system as
+ a result.
+ </p></warning>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>void *</ret><nametext>erl_drv_tsd_get(ErlDrvTSDKey key)</nametext></name>
+ <fsummary>Get thread specific data</fsummary>
+ <desc>
+ <marker id="erl_drv_tsd_get"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>key</c></tag>
+ <item>A thread specific data key.</item>
+ </taglist>
+ <p>This function returns the thread specific data
+ associated with <c>key</c> for the calling thread.
+ If no data has been associated with <c>key</c> for
+ the calling thread, <c>NULL</c> is returned.
+ </p>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_drv_putenv(char *key, char *value)</nametext></name>
+ <fsummary>Set the value of an environment variable</fsummary>
+ <desc>
+ <marker id="erl_drv_putenv"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>key</c></tag>
+ <item>A null terminated string containing the
+ name of the environment variable.</item>
+ <tag><c>value</c></tag>
+ <item>A null terminated string containing the
+ new value of the environment variable.</item>
+ </taglist>
+ <p>This function sets the value of an environment variable.
+ It returns <c>0</c> on success, and a value <c>!= 0</c> on
+ failure.
+ </p>
+ <note><p>The result of passing the empty string ("") as a value
+ is platform dependent. On some platforms the value of the
+ variable is set to the empty string, on others, the
+ environment variable is removed.</p>
+ </note>
+ <warning><p>Do <em>not</em> use libc's <c>putenv</c> or similar
+ C library interfaces from a driver.
+ </p></warning>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name><ret>int</ret><nametext>erl_drv_getenv(char *key, char *value, size_t *value_size)</nametext></name>
+ <fsummary>Get the value of an environment variable</fsummary>
+ <desc>
+ <marker id="erl_drv_getenv"></marker>
+ <p>Arguments:</p>
+ <taglist>
+ <tag><c>key</c></tag>
+ <item>A null terminated string containing the
+ name of the environment variable.</item>
+ <tag><c>value</c></tag>
+ <item>A pointer to an output buffer.</item>
+ <tag><c>value_size</c></tag>
+ <item>A pointer to an integer. The integer is both used for
+ passing input and output sizes (see below).
+ </item>
+ </taglist>
+ <p>This function retrieves the value of an environment variable.
+ When called, <c>*value_size</c> should contain the size of
+ the <c>value</c> buffer. On success <c>0</c> is returned,
+ the value of the environment variable has been written to
+ the <c>value</c> buffer, and <c>*value_size</c> contains the
+ string length (excluding the terminating null character) of
+ the value written to the <c>value</c> buffer. On failure,
+ i.e., no such environment variable was found, a value less than
+ <c>0</c> is returned. When the size of the <c>value</c>
+ buffer is too small, a value greater than <c>0</c> is returned
+ and <c>*value_size</c> has been set to the buffer size needed.
+ </p>
+ <warning><p>Do <em>not</em> use libc's <c>getenv</c> or similar
+ C library interfaces from a driver.
+ </p></warning>
+ <p>This function is thread-safe.</p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>SEE ALSO</title>
+ <p><seealso marker="driver_entry">driver_entry(3)</seealso>,
+ <seealso marker="kernel:erl_ddll">erl_ddll(3)</seealso>,
+ <seealso marker="erts:erlang">erlang(3)</seealso></p>
+ <p>An Alternative Distribution Driver (ERTS User's
+ Guide Ch. 3)</p>
+ </section>
+</cref>
+