aboutsummaryrefslogtreecommitdiffstats
path: root/erts/doc/src/erl_nif.xml
diff options
context:
space:
mode:
Diffstat (limited to 'erts/doc/src/erl_nif.xml')
-rw-r--r--erts/doc/src/erl_nif.xml136
1 files changed, 126 insertions, 10 deletions
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index 864b91946a..3de94be9ff 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="latin1" ?>
+<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE cref SYSTEM "cref.dtd">
<cref>
@@ -168,20 +168,26 @@ ok
<p><marker id="lengthy_work"/>
As mentioned in the <seealso marker="#WARNING">warning</seealso> text at
the beginning of this document it is of vital importance that a native function
- does return relatively fast. It is hard to give an exact maximum amount
+ return relatively quickly. It is hard to give an exact maximum amount
of time that a native function is allowed to work, but as a rule of thumb
- a well behaving native function should return to its caller before a
+ a well-behaving native function should return to its caller before a
millisecond has passed. This can be achieved using different approaches.
- If you have full control over the code that are to execute in the native
+ If you have full control over the code to execute in the native
function, the best approach is to divide the work into multiple chunks of
- work and call the native function multiple times. Function
+ work and call the native function multiple times, either directly from Erlang code
+ or by having a native function schedule a future NIF call via the
+ <seealso marker="#enif_schedule_nif"> enif_schedule_nif</seealso> function. Function
<seealso marker="#enif_consume_timeslice">enif_consume_timeslice</seealso> can be
- used this facilitate such work division. In some cases, however, this might not
- be possible, e.g. when calling third party libraries. Then you typically want
+ used to help with such work division. In some cases, however, this might not
+ be possible, e.g. when calling third-party libraries. Then you typically want
to dispatch the work to another thread, return
from the native function, and wait for the result. The thread can send
the result back to the calling thread using message passing. Information
- about thread primitives can be found below.</p>
+ about thread primitives can be found below. If you have built your system
+ with <em>the currently experimental</em> support for dirty schedulers,
+ you may want to try out this functionality by dispatching the work to a
+ <seealso marker="#dirty_nifs">dirty NIF</seealso>,
+ which does not have the same duration restriction as a normal NIF.</p>
</description>
<section>
<title>FUNCTIONALITY</title>
@@ -312,6 +318,65 @@ ok
<p>The library initialization callbacks <c>load</c>, <c>reload</c> and
<c>upgrade</c> are all thread-safe even for shared state data.</p>
</item>
+
+ <tag><marker id="version_management"/>Version Management</tag>
+ <item><p>
+ When a NIF library is built, information about NIF API version
+ is compiled into the library. When a NIF library is loaded the
+ runtime system verifies that the library is of a compatible version.
+ <c>erl_nif.h</c> defines <c>ERL_NIF_MAJOR_VERSION</c>, and
+ <c>ERL_NIF_MINOR_VERSION</c>. <c>ERL_NIF_MAJOR_VERSION</c> will be
+ incremented when NIF library incompatible changes are made to the
+ Erlang runtime system. Normally it will suffice to recompile the NIF
+ library when the <c>ERL_NIF_MAJOR_VERSION</c> has changed, but it
+ could, under rare circumstances, mean that NIF libraries have to
+ be slightly modified. If so, this will of course be documented.
+ <c>ERL_NIF_MINOR_VERSION</c> will be incremented when
+ new features are added. The runtime system uses the minor version
+ to determine what features to use.
+ </p><p>
+ The runtime system will normally refuse to load a NIF library if
+ the major versions differ, or if the major versions are equal and
+ the minor version used by the NIF library is greater than the one
+ used by the runtime system. Old NIF libraries with lower major
+ versions will however be allowed after a bump of the major version
+ during a transition period of two major releases. Such old NIF
+ libraries might however fail if deprecated features are used.
+ </p></item>
+
+ <tag>Long-running NIFs</tag>
+ <item><p><marker id="dirty_nifs"/>Native functions
+ <seealso marker="#lengthy_work">
+ must normally run quickly</seealso>, as explained earlier in this document. They
+ generally should execute for no more than a millisecond. But not all native functions
+ can execute so quickly; for example, functions that encrypt large blocks of data or
+ perform lengthy file system operations can often run for tens of seconds or more.</p>
+ <p>If the functionality of a long-running NIF can be split so that its work can be
+ achieved through a series of shorter NIF calls, the application can either make that series
+ of NIF calls from the Erlang level, or it can call a NIF that first performs a chunk of the
+ work, then invokes the <seealso marker="#enif_schedule_nif">enif_schedule_nif</seealso>
+ function to schedule another NIF call to perform the next chunk. The final call scheduled
+ in this manner can then return the overall result. Breaking up a long-running function in
+ this manner enables the VM to regain control between calls to the NIFs, thereby avoiding
+ degraded responsiveness, scheduler load balancing problems, and other strange behaviours.</p>
+ <p>A NIF that cannot be split and cannot execute in a millisecond or less is called a "dirty NIF"
+ because it performs work that the Erlang runtime cannot handle cleanly.
+ <em>Note that the dirty NIF functionality described here is experimental</em> and that you have to
+ enable support for dirty schedulers when building OTP in order to try the functionality out.
+ Applications that make use of such functions must indicate to the runtime that the functions are
+ dirty so they can be handled specially. To schedule a dirty NIF for execution, the
+ appropriate flags value can be set for the NIF in its <seealso marker="#ErlNifFunc">ErlNifFunc</seealso>
+ entry, or the application can call <seealso marker="#enif_schedule_nif">enif_schedule_nif</seealso>,
+ passing to it a pointer to the dirty NIF to be executed and indicating with the <c>flags</c>
+ argument whether it expects the operation to be CPU-bound or I/O-bound.</p>
+ <note><p>Dirty NIF support is available only when the emulator is configured with dirty
+ schedulers enabled. This feature is currently disabled by default. To determine whether
+ the dirty NIF API is available, native code can check to see if the C preprocessor macro
+ <c>ERL_NIF_DIRTY_SCHEDULER_SUPPORT</c> is defined. Also, if the Erlang runtime was built
+ without threading support, dirty schedulers are disabled. To check at runtime for the presence
+ of dirty scheduler threads, code can use the <seealso marker="#enif_system_info"><c>
+ enif_system_info()</c></seealso> API function.</p></note>
+ </item>
</taglist>
</section>
<section>
@@ -330,6 +395,8 @@ ok
<c>upgrade</c> will be called to initialize the library.
<c>unload</c> is called to release the library. They are all
described individually below.</p>
+ <p>If compiling a nif for static inclusion via --enable-static-nifs you
+ have to define STATIC_ERLANG_NIF before the ERL_NIF_INIT declaration.</p>
</item>
<tag><marker id="load"/>int (*load)(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)</tag>
@@ -434,6 +501,7 @@ typedef struct {
const char* <em>name</em>;
unsigned <em>arity</em>;
ERL_NIF_TERM (*<em>fptr</em>)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
+ unsigned flags;
} ErlNifFunc;
</code>
<p>Describes a NIF by its name, arity and implementation.
@@ -444,7 +512,17 @@ typedef struct {
will thus denote the Nth argument to the NIF. Note that the
<c>argc</c> argument allows for the same C function to
implement several Erlang functions with different arity (but
- same name probably).</p>
+ same name probably). For a regular NIF, <c>flags</c> is 0 (and
+ so its value can be omitted for statically initialized <c>ErlNifFunc</c>
+ instances), or it can be used to indicate that the NIF is a <seealso
+ marker="#dirty_nifs">dirty NIF</seealso> that should be executed
+ on a dirty scheduler thread (<em>note that the dirty NIF functionality
+ described here is experimental</em> and that you have to enable
+ support for dirty schedulers when building OTP in order to try the
+ functionality out). If the dirty NIF is expected to be
+ CPU-bound, its <c>flags</c> field should be set to
+ <c>ERL_NIF_DIRTY_JOB_CPU_BOUND</c>, or for I/O-bound jobs,
+ <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c>.</p>
</item>
<tag><marker id="ErlNifBinary"/>ErlNifBinary</tag>
<item>
@@ -775,6 +853,20 @@ typedef enum {
Erlang operators <c>=:=</c> and
<c>=/=</c>.</p></desc>
</func>
+ <func><name><ret>int</ret><nametext>enif_is_on_dirty_scheduler(ErlNifEnv* env)</nametext></name>
+ <fsummary>Check to see if executing on a dirty scheduler thread</fsummary>
+ <desc>
+ <p>Check to see if the current NIF is executing on a dirty scheduler thread. If the
+ emulator is built with threading support, calling <c>enif_is_on_dirty_scheduler</c>
+ from within a dirty NIF returns true. It returns false when the calling NIF is a regular
+ NIF running on a normal scheduler thread, or when the emulator is built without threading
+ support.</p>
+ <note><p>This function is available only when the emulator is configured with dirty
+ schedulers enabled. This feature is currently disabled by default. To determine whether
+ the dirty NIF API is available, native code can check to see if the C preprocessor macro
+ <c>ERL_NIF_DIRTY_SCHEDULER_SUPPORT</c> is defined.</p></note>
+ </desc>
+ </func>
<func><name><ret>int</ret><nametext>enif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name>
<fsummary>Determine if a term is a pid</fsummary>
<desc><p>Return true if <c>term</c> is a pid.</p></desc>
@@ -1139,6 +1231,31 @@ typedef enum {
<desc><p>Same as <seealso marker="erl_driver#erl_drv_rwlock_tryrwlock">erl_drv_rwlock_tryrwlock</seealso>.
</p></desc>
</func>
+ <func><name><ret>ERL_NIF_TERM</ret><nametext>enif_schedule_nif(ErlNifEnv* env, const char* fun_name, int flags, ERL_NIF_TERM (*fp)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]), int argc, const ERL_NIF_TERM argv[])</nametext></name>
+ <fsummary>Schedule a NIF for execution</fsummary>
+ <desc>
+ <p>Schedule NIF <c>fp</c> to execute. This function allows an application to break up long-running
+ work into multiple regular NIF calls or to schedule a <seealso marker="#dirty_nifs">dirty NIF</seealso>
+ to execute on a dirty scheduler thread (<em>note that the dirty NIF functionality described here is
+ experimental</em> and that you have to enable support for dirty schedulers when building OTP in
+ order to try the functionality out).</p>
+ <p>The <c>fun_name</c> argument provides a name for the NIF being scheduled for execution. If it cannot
+ be converted to an atom, <c>enif_schedule_nif</c> returns a <c>badarg</c> exception.</p>
+ <p>The <c>flags</c> argument must be set to 0 for a regular NIF, or if the emulator was built the
+ experimental dirty scheduler support enabled, <c>flags</c> can be set to either <c>ERL_NIF_DIRTY_JOB_CPU_BOUND</c>
+ if the job is expected to be primarily CPU-bound, or <c>ERL_NIF_DIRTY_JOB_IO_BOUND</c> for jobs that will
+ be I/O-bound. If dirty scheduler threads are not available in the emulator, a try to schedule such a job
+ will result in a <c>badarg</c> exception.</p>
+
+ <p>The <c>argc</c> and <c>argv</c> arguments can either be the originals passed into the calling NIF, or
+ they can be values created by the calling NIF.</p>
+ <p>The calling NIF must use the return value of <c>enif_schedule_nif</c> as its own return value.</p>
+ <p>Be aware that <c>enif_schedule_nif</c>, as its name implies, only schedules the
+ NIF for future execution. The calling NIF does not block waiting for the scheduled NIF to
+ execute and return, which means that the calling NIF can't expect to receive the scheduled NIF
+ return value and use it for further operations.</p>
+ </desc>
+ </func>
<func><name><ret>ErlNifPid *</ret><nametext>enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)</nametext></name>
<fsummary>Get the pid of the calling process.</fsummary>
<desc><p>Initialize the pid variable <c>*pid</c> to represent the
@@ -1236,4 +1353,3 @@ typedef enum {
<p><seealso marker="erlang#load_nif-2">erlang:load_nif/2</seealso></p>
</section>
</cref>
-