A NIF library contains native implementation of some functions of an Erlang module. The native implemented functions (NIFs) are called like any other functions without any difference to the caller. Each NIF must have an implementation in Erlang that is invoked if the function is called before the NIF library is successfully loaded. A typical such stub implementation is to throw an exception. But it can also be used as a fallback implementation if the NIF library is not implemented for some architecture.
Use this functionality with extreme care.
A native function is executed as a direct extension of the native code of the VM. Execution is not made in a safe environment. The VM cannot provide the same services as provided when executing Erlang code, such as pre-emptive scheduling or memory protection. If the native function does not behave well, the whole VM will misbehave.
A native function that crash will crash the whole VM.
An erroneously implemented native function can cause a VM internal state inconsistency, which can cause a crash of the VM, or miscellaneous misbehaviors of the VM at any point after the call to the native function.
A native function doing
A minimal example of a NIF library can look as follows:
/* niftest.c */
#include <erl_nif.h>
static ERL_NIF_TERM hello(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
return enif_make_string(env, "Hello world!", ERL_NIF_LATIN1);
}
static ErlNifFunc nif_funcs[] =
{
{"hello", 0, hello}
};
ERL_NIF_INIT(niftest,nif_funcs,NULL,NULL,NULL,NULL)
The Erlang module can look as follows:
-module(niftest).
-export([init/0, hello/0]).
init() ->
erlang:load_nif("./niftest", 0).
hello() ->
"NIF library not loaded".
Compile and test can look as follows (on Linux):
$> gcc -fPIC -shared -o niftest.so niftest.c -I $ERL_ROOT/usr/include/
$> erl
1> c(niftest).
{ok,niftest}
2> niftest:hello().
"NIF library not loaded"
3> niftest:init().
ok
4> niftest:hello().
"Hello world!"
A better solution for a real module is to take advantage of the new
directive
A NIF does not have to be exported, it can be local to the module. However, unused local stub functions will be optimized away by the compiler, causing loading of the NIF library to fail.
Once loaded, a NIF library is persistent. It will not be unloaded until the module code version that it belongs to is purged.
All interaction between NIF code and the Erlang runtime system is performed by calling NIF API functions. Functions exist for the following functionality:
Any Erlang terms can be passed to a NIF as function arguments and
be returned as function return values. The terms are of C-type
All terms of type
Terms of type binary are accessed with the help of struct type
The raw data pointed to by
Binaries are sequences of whole bytes. Bitstrings with an arbitrary bit length have no support yet.
The use of resource objects is a safe way to return pointers to
native data structures from a NIF. A resource object is
only a block of memory allocated with
All resource objects are created as instances of some resource
type. This makes resources from different modules to be
distinguishable. A resource type is created by calling
The following is a template example of how to create and return a resource object.
ERL_NIF_TERM term;
MyStruct* obj = enif_alloc_resource(my_resource_type, sizeof(MyStruct));
/* initialize struct ... */
term = enif_make_resource(env, obj);
if (keep_a_reference_of_our_own) {
/* store 'obj' in static variable, private data or other resource object */
}
else {
enif_release_resource(obj);
/* resource now only owned by "Erlang" */
}
return term;
Notice that once
Another use of resource objects is to create binary terms with
user-defined memory management.
Resource types support upgrade in runtime by allowing a loaded NIF library to take over an already existing resource type and by that "inherit" all existing objects of that type. The destructor of the new library is thereafter called for the inherited objects and the library with the old destructor function can be safely unloaded. Existing resource objects, of a module that is upgraded, must either be deleted or taken over by the new NIF library. The unloading of a library is postponed as long as there exist resource objects with a destructor function in the library.
A loaded NIF library is tied to the Erlang module instance
that loaded it. If the module is upgraded, the new module instance
needs to load its own NIF library (or maybe choose not to). The new
module instance can, however, choose to load the exact same NIF library
as the old code if it wants to. Sharing the dynamic library means that
static data defined by the library is shared as well. To avoid
unintentionally shared static data between module instances, each Erlang
module version can keep its own private data. This private data can be
set when the NIF library is loaded and later retrieved by calling
A NIF is thread-safe without any explicit synchronization as
long as it acts as a pure function and only reads the supplied
arguments. When you write to a shared state either through
static variables or
The library initialization callbacks
When a NIF library is built, information about the 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.
Incremented when NIF library incompatible changes are made to the
Erlang runtime system. Normally it suffices to recompile the NIF
library when the
Incremented when new features are added. The runtime system uses the minor version to determine what features to use.
The runtime system normally refuses 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 are, however, allowed after a bump of the major version during a transition period of two major releases. Such old NIF libraries can however fail if deprecated features are used.
Support for time measurement in NIF libraries:
The Erlang nif library contains function for easily working
with I/O vectors as used by the unix system call
Typical usage when writing to a file descriptor looks like this:
0) {
/* If the I/O queue contains data we enqueue the iovec and
then peek the data to write out of the queue. */
if (!enif_ioq_enqv(q, iovec, 0))
return -3;
sysiovec = enif_ioq_peek(q, &iovcnt);
} else {
/* If the I/O queue is empty we skip the trip through it. */
iovcnt = iovec->iovcnt;
sysiovec = iovec->iov;
}
/* Attempt to write the data */
n = writev(fd, sysiovec, iovcnt);
saved_errno = errno;
if (enif_ioq_size(q) == 0) {
/* If the I/O queue was initially empty we enqueue any
remaining data into the queue for writing later. */
if (n >= 0 && !enif_ioq_enqv(q, iovec, n))
return -3;
} else {
/* Dequeue any data that was written from the queue. */
if (n > 0 && !enif_ioq_deq(q, n, NULL))
return -4;
}
/* return n, which is either number of bytes written or -1 if
some error happened */
errno = saved_errno;
return n;
}]]>
As mentioned in the
The
If the NIF call is too lengthy, this must be handled in one of the following ways to avoid degraded responsiveness, scheduler load balancing problems, and other strange behaviors:
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 has two options:
Make that series of NIF calls from the Erlang level.
Call a NIF that first performs a chunk of the work, then
invokes the
Breaking up a long-running function in this manner enables the VM to regain control between calls to the NIFs.
This approach is always preferred over the other alternatives described below. This both from a performance perspective and a system characteristics perspective.
This is accomplished by dispatching the work to another thread
managed by the NIF library, return from the NIF, and wait for
the result. The thread can send the result back to the Erlang
process using
Dirty NIF support is available only when the emulator is
configured with dirty scheduler support. As of ERTS version
9.0, dirty scheduler support is enabled by default on the
runtime system with SMP support. The Erlang runtime without
SMP support does not support dirty schedulers even
when the dirty scheduler support is explicitly enabled. To
check at runtime for the presence of dirty scheduler threads,
code can use the
A NIF that cannot be split and cannot execute in a millisecond or less is called a "dirty NIF", as it performs work that the ordinary schedulers of the Erlang runtime system cannot handle cleanly. Applications that make use of such functions must indicate to the runtime that the functions are dirty so they can be handled specially. This is handled by executing dirty jobs on a separate set of schedulers called dirty schedulers. A dirty NIF executing on a dirty scheduler does not have the same duration restriction as a normal NIF.
It is important to classify the dirty job correct. An I/O bound job should be classified as such, and a CPU bound job should be classified as such. If you should classify CPU bound jobs as I/O bound jobs, dirty I/O schedulers might starve ordinary schedulers. I/O bound jobs are expected to either block waiting for I/O, and/or spend a limited amount of time moving data.
To schedule a dirty NIF for execution, the application has two options:
Set the appropriate flags value for the dirty NIF in its
Call
A job that alternates between I/O bound and CPU bound can be
reclassified and rescheduled using
While a process executes a dirty NIF, some operations that
communicate with it can take a very long time to complete.
Suspend or garbage collection of a process executing a dirty
NIF cannot be done until the dirty NIF has returned. Thus, other
processes waiting for such operations to complete might
have to wait for a very long time. Blocking multi-scheduling, that
is, calling
Many operations communicating with a process executing a
dirty NIF can, however, complete while it executes the
dirty NIF. For example, retrieving information about it through
Termination of a process executing a dirty NIF can only be
completed up to a certain point while it executes the dirty NIF.
All Erlang resources, such as its registered name and its ETS
tables, are released. All links and monitors are triggered. The
execution of the NIF is, however, not stopped. The NIF
can safely continue execution, allocate heap memory, and so on,
but it is of course better to stop executing as soon as possible.
The NIF can check whether a current process is alive using
This is the magic macro to initialize a NIF library. It is to be evaluated in global file scope.
The fourth argument
If compiling a NIF for static inclusion through
The library fails to load if
Works as
The library fails to load if
Variables of type
Passed as the first argument to all NIFs. All function arguments passed to a NIF belong to that environment. The return value from a NIF must also be a term belonging to the same environment.
A process-bound environment contains transient information about the calling Erlang process. The environment is only valid in the thread where it was supplied as argument until the NIF returns. It is thus useless and dangerous to store pointers to process-bound environments between NIF calls.
Created by calling
All contained terms of a list/tuple/map must belong to the same
environment as the list/tuple/map itself. Terms can be copied between
environments with
typedef struct {
const char* name;
unsigned arity;
ERL_NIF_TERM (*fptr)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]);
unsigned flags;
} ErlNifFunc;
Describes a NIF by its name, arity, and implementation.
A pointer to the function that implements the NIF.
Contains the function arguments passed to the NIF.
The array length, that is, the function arity.
Is
If the dirty NIF is expected to be CPU-bound, its
If one of the
typedef struct {
unsigned size;
unsigned char* data;
} ErlNifBinary;
Notice that
An enumeration of the options that can be specified to
When receiving data from untrusted sources, use option
This is an opaque data type that identifies a monitor.
The nif writer is to provide the memory for storing the
monitor when calling
A process identifier (pid). In contrast to pid terms (instances of
A port identifier. In contrast to port ID terms (instances of
Each instance of
typedef struct {
ErlNifResourceDtor* dtor;
ErlNifResourceStop* stop;
ErlNifResourceDown* down;
} ErlNifResourceTypeInit;
Initialization structure read by
typedef void ErlNifResourceDtor(ErlNifEnv* env, void* obj);
The function prototype of a resource destructor function.
The
typedef void ErlNifResourceDown(ErlNifEnv* env, void* obj, const ErlNifPid* pid, const ErlNifMonitor* mon);
The function prototype of a resource down function,
called on the behalf of
typedef void ErlNifResourceStop(ErlNifEnv* env, void* obj, ErlNifEvent event, int is_direct_call);
The function prototype of a resource stop function,
called on the behalf of
typedef enum {
ERL_NIF_LATIN1
}ErlNifCharEncoding;
The character encoding used in strings and atoms. The only
supported encoding is
Used by
A native signed 64-bit integer type.
A native unsigned 64-bit integer type.
A signed 64-bit integer type for representation of time.
An enumeration of time units supported by the NIF API:
An enumeration of the properties that can be requested from
Return only positive integers.
Return only
An enumeration of the supported hash types that can be generated
using
Non-portable hash function that only guarantees the same hash for the same term within one Erlang VM instance.
It takes 32-bit salt values and generates hashes within
Portable hash function that gives the same hash for the same Erlang term regardless of machine architecture and ERTS version.
It ignores salt values and generates hashes within
Slower than
A system I/O vector, as used by
typedef struct {
int iovcnt;
size_t size;
SysIOVec* iov;
} ErlNifIOVec;
An I/O vector containing
Create a normal I/O Queue
Allocates memory of
Returns
Allocates a new binary of size
Returns
Allocates a new process-independent environment. The environment can
be used to hold terms that are not bound to any process. Such terms
can later be copied to a process environment with
Returns pointer to the new environment.
Allocates a memory-managed resource object of type
Creates a term that is the result of decoding the binary data at
On success, stores the resulting term at
See also
Frees all terms in an environment and clears it for reuse.
The environment must have been allocated with
Returns an integer <
Compares two
Returns
Same as
Same as
Same as
Same as
Same as
Gives the runtime system a hint about how much CPU time the current NIF call has consumed since the last hint, or since the start of the NIF if no previous hint has been specified. The time is specified as a percent of the timeslice that a process is allowed to execute Erlang code until it can be suspended to give time for other runnable processes. The scheduling timeslice is not an exact entity, but can usually be approximated to about 1 millisecond.
Notice that it is up to the runtime system to determine if and how
to use this information. Implementations on some platforms can use
other means to determine consumed CPU time. Lengthy NIFs should
regardless of this frequently call
Argument
Returns
This function is provided to better support co-operative scheduling,
improve system responsiveness, and make it easier to prevent
misbehaviors of the VM because of a NIF monopolizing a scheduler
thread. It can be used to divide
See also the
Converts the
Returns
See also
Returns the CPU time in the same format as
Cancels a monitor created earlier with
Returns
This function is only thread-safe when the emulator with SMP support is used. It can only be used in a non-SMP emulator from a NIF-calling thread.
Same as
Frees memory allocated by
Frees an environment allocated with
Frees an io vector returned from
Writes a
Returns the number of bytes written (including terminating
Sets
Returns
Sets
Returns
Sets
Returns
Sets
Returns
If
If
Sets
Returns
Sets
Returns
Sets
Returns
Sets
Returns
Sets
Returns
Sets
Returns
Writes a
Returns one of the following:
The written string is always
If
Returns
Sets
Returns
Sets
Returns
Sets
Returns
Same as
Returns
See also
Hashes
Ranges of taken salt (if any) and returned value depend on the hash type.
Initializes the structure pointed to by
Returns
Initializes the structure pointed to by
Returns
Fills
To create a list of binaries from an arbitrary iolist, use
When calling this function,
/* Don't use a pre-allocated structure */
ErlNifIOVec *iovec = NULL;
enif_inspect_iovec(env, max_elements, term, &tail, &iovec);
/* Use a stack-allocated vector as an optimization for vectors with few elements */
ErlNifIOVec vec, *iovec = &vec;
enif_inspect_iovec(env, max_elements, term, &tail, &iovec);
The contents of the
Returns
Create a new I/O Queue that can be used to store data.
Destroy the I/O queue and free all of it's contents
Dequeue
Returns
Enqueue the
Returns
Enqueue the
Returns
Get the I/O queue as a pointer to an array of
Nothing is removed from the queue by this function, that must be done
with
The returned array is suitable to use with the Unix system
call
Get the size of
Returns
Returns
Returns
This function can only be used from a NIF-calling thread, and with an environment corresponding to currently executing processes.
Returns
Return true if
Returns
Returns
Returns
Returns
Returns
Returns
Returns
Returns
This function is only thread-safe when the emulator with SMP support is used. It can only be used in a non-SMP emulator from a NIF-calling thread.
Returns
This function is only thread-safe when the emulator with SMP support is used. It can only be used in a non-SMP emulator from a NIF-calling thread.
Returns
Returns
Adds a reference to resource object
Creates an atom term from the
Create an atom term from the string
Makes a
The return value from
See also
Before ERTS 7.0 (Erlang/OTP 18), the return value
from
Makes a binary term from
Makes a copy of term
Creates a floating-point term from a
Tries to create the term of an already existing atom from
the
If the atom already exists, this function stores the term in
Tries to create the term of an already existing atom from the
string
If the atom already exists, this function stores the term in
Creates an integer term.
Creates an integer term from a signed 64-bit integer.
Creates an ordinary list term of length
Returns an empty list if
Creates an ordinary list term with length indicated by the
function name. Prefer these functions (macros) over the variadic
Creates a list cell
Creates an ordinary list containing the elements of array
Returns an empty list if
Creates an integer term from a
Makes a copy of map
If successful, this function sets
The
If map
Returns
The
Makes a copy of map
If successful, this function sets
The
Allocates a binary of size
Returns a pointer to the raw binary data and sets
Makes an empty map term.
Makes a pid term from
Creates a reference like
Creates an opaque handle to a memory-managed resource object
obtained by
Notice that the only defined behavior of using a resource term in
an Erlang program is to store it and send it between processes on the
same node. Other operations, such as matching or
Creates a binary term that is memory-managed by a resource object
Several binary terms can be managed by the same resource object. The destructor is not called until the last binary is garbage collected. This can be useful to return different parts of a larger binary buffer.
As with
Sets
This function is only to be used on short lists, as a copy is created of the list, which is not released until after the NIF returns.
The
Creates a list containing the characters of the
Creates a list containing the characters of the string
Makes a subbinary of binary
Creates a tuple term of arity
Creates a tuple term with length indicated by the
function name. Prefer these functions (macros) over the variadic
Creates a tuple containing the elements of array
Creates an integer term from an
Creates an integer term from an unsigned 64-bit integer.
Creates an integer term from an
Returns a unique integer with the same properties as specified by
See also
Creates an iterator for the map
Returns
A map iterator is only useful during the lifetime of environment
ERL_NIF_TERM key, value;
ErlNifMapIterator iter;
enif_map_iterator_create(env, my_map, &iter, ERL_NIF_MAP_ITERATOR_FIRST);
while (enif_map_iterator_get_pair(env, &iter, &key, &value)) {
do_something(key,value);
enif_map_iterator_next(env, &iter);
}
enif_map_iterator_destroy(env, &iter);
The key-value pairs of a map have no defined iteration order. The only guarantee is that the iteration order of a single map instance is preserved during the lifetime of the environment that the map belongs to.
Destroys a map iterator created by
Gets key and value terms at the current map iterator position.
On success, sets
Returns
Returns
Increments map iterator to point to the next key-value entry.
Returns
Decrements map iterator to point to the previous key-value entry.
Returns
Starts monitoring a process from a resource. When a process is
monitored, a process exit results in a call to the provided
Argument
If
Returns
This function is only thread-safe when the emulator with SMP support is used. It can only be used in a non-SMP emulator from a NIF-calling thread.
Returns the current
Returns
See also
Same as
Same as
Same as
Same as
Same as
Returns an
This function is deprecated.
Creates or takes over a resource type identified by the string
The two flag values can be combined with bitwise OR. The resource
type name is local to the calling module. Argument
On success, the function returns a pointer to the resource type and
Notice that
See also
Same as
Argument
Works as
Using a
Returns
See also
Returns the pointer to the private data that was set by
Creates an error exception with the term
The return value from
See also
Changes the size of a binary
Returns
Releases a binary obtained from
Removes a reference to resource object
Same as
Same as
Same as
Same as
Same as
Same as
Same as
Same as
Schedules NIF
Provides a name for the NIF that is scheduled for execution.
If it cannot be converted to an atom,
Must be set to
Can either be the originals passed into the calling NIF, or can be values created by the calling NIF.
The calling NIF must use the return value of
Be aware that
This function can be used to receive asynchronous notifications when OS-specific event objects become ready for either read or write operations.
Argument
Argument
{select, Obj, Ref, ready_input | ready_output}
Argument
Argument
Argument
The notifications are one-shot only. To receive further notifications of the same
type (read or write), repeated calls to
Use
The first call to
Use
Returns a non-negative value on success where the following bits can be set:
Returns a negative value if the call failed where the follwing bits can be set:
Use bitwise AND to test for specific bits in the return vaue.
New significant bits may be added in future releases to give more detailed
information for both failed and successful calls. Do NOT use equallity tests
like
Example:
retval = enif_select(env, fd, ERL_NIF_SELECT_STOP, resource, ref);
if (retval < 0) {
/* handle error */
}
/* Success! */
if (retval & ERL_NIF_SELECT_STOP_CALLED) {
/* ... */
}
Initializes the pid variable
Returns
Sends a message to a process.
Returns
The message environment
If
This function is only thread-safe when the emulator with SMP support is used. It can only be used in a non-SMP emulator from a NIF-calling thread.
Passing
Gets the byte size of resource object
Similar to
Same as
Allocates a new binary with
Returns
See also
Same as
Same as
Same as
Same as
Same as
Same as
Determine the type of currently executing thread. A positive value indicates a scheduler thread while a negative value or zero indicates another type of thread. Currently the following specific types exist (which may be extended in the future):
Undefined thread that is not a scheduler thread.
A normal scheduler thread.
A dirty CPU scheduler thread.
A dirty I/O scheduler thread.
Returns the current time offset between
Returns
See also
Same as
Same as
Same as
Same as
Looks up a process by its registered name.
On success, sets
Works as
Looks up a port by its registered name.
On success, sets
Works as