diff options
Diffstat (limited to 'erts/doc/src/erl_driver.xml')
-rw-r--r-- | erts/doc/src/erl_driver.xml | 2465 |
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 < 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, < 0 if no call-back is + provided and > 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 > 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, < 0 if <c>monitor1</c> is less + than <c>monitor2</c> and > 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> + |