From 8cbf3bd3b9d552423812df4acc7a40d9a23fdeae Mon Sep 17 00:00:00 2001 From: Rickard Green Date: Mon, 2 May 2016 16:02:02 +0200 Subject: Add better support for communication with a process executing dirty NIF - Termination of a process... - Modify trace flags of process... - Process info on process... - Register/unregister of name on process... - Set group leader on process... ... while it is executing a dirty NIF. --- erts/doc/src/erl_nif.xml | 245 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 180 insertions(+), 65 deletions(-) (limited to 'erts/doc/src/erl_nif.xml') diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index ef64ac66dc..fefec3eeb6 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -138,29 +138,6 @@ ok automatically unloaded when the module code that it belongs to is purged by the code server.

-

- As mentioned in the warning text at - the beginning of this document it is of vital importance that a native function - 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 - millisecond has passed. This can be achieved using different approaches. - 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, either directly from Erlang code - or by having a native function schedule a future NIF call via the - enif_schedule_nif function. Function - enif_consume_timeslice can be - 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. If you have built your system - with the currently experimental support for dirty schedulers, - you may want to try out this functionality by dispatching the work to a - dirty NIF, - which does not have the same duration restriction as a normal NIF.

FUNCTIONALITY @@ -328,37 +305,161 @@ ok

- Long-running NIFs -

Native functions - - must normally run quickly, 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.

-

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 enif_schedule_nif - 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.

-

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. - Note that the dirty NIF functionality described here is experimental 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 ErlNifFunc - entry, or the application can call enif_schedule_nif, - passing to it a pointer to the dirty NIF to be executed and indicating with the flags - argument whether it expects the operation to be CPU-bound or I/O-bound.

-

Dirty NIF support is available only when the emulator is configured with dirty - schedulers enabled. This feature is currently disabled by default. The Erlang runtime - without SMP support currently do not support dirty schedulers even when the dirty - scheduler support has been enabled. To check at runtime for the presence - of dirty scheduler threads, code can use the - enif_system_info() API function.

+ Long-running NIFs + +

+ As mentioned in the warning text at + the beginning of this document it is of vital importance that a + native function 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 millisecond has passed. This can be achieved using different + approaches. 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. In some + cases this might however not always be possible, e.g. when calling + third-party libraries.

+ +

The + enif_consume_timeslice() + function can be used to inform the runtime system about the lenght of the + NIF call. It should typically always be used unless the NIF executes very + quickly.

+ +

If the NIF call is too lenghty one needs to handle this in one of the + following ways in order to avoid degraded responsiveness, scheduler load + balancing problems, and other strange behaviours:

+ + + Yielding NIF + +

+ 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 + enif_schedule_nif + 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. +

+

+ This approach is always preferred over the other alternatives + described below. This both from a performance perspective and + a system characteristics perspective. +

+
+ + Threaded NIF + +

+ 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 enif_send. + Information about thread primitives can be found below. +

+
+ + Dirty NIF + + + +

+ The dirty NIF functionality described here + is experimental. Dirty NIF support is available only when + the emulator is configured with dirty schedulers enabled. This + feature is currently disabled by default. The Erlang runtime + without SMP support do not support dirty schedulers even when + the dirty scheduler support has been enabled. To check at + runtime for the presence of dirty scheduler threads, code can + use the + enif_system_info() + API function. +

+
+ +

+ 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. 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 ErlNifFunc + entry, or the application can call + enif_schedule_nif, + passing to it a pointer to the dirty NIF to be executed and + indicating with the flags argument whether it expects the + operation to be CPU-bound or I/O-bound. A dirty NIF executing + on a dirty scheduler does not have the same duration restriction + as a normal NIF. +

+ +

+ While a process is executing a dirty NIF some operations that + communicate with it may 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, so other + processes waiting for such operations to complete might have to + wait for a very long time. Blocking multi scheduling, i.e., + calling + erlang:system_flag(multi_scheduling, + block), might also take a very long time to + complete. This since all ongoing dirty operations on all + dirty schedulers need to complete before the the block + operation can complete. +

+ +

+ A lot of operations communicating with a process executing a + dirty NIF can, however, complete while it is executing the + dirty NIF. For example, retreiving information about it via + process_info(), setting its group leader, + register/unregister its name, etc. +

+ +

+ Termination of a process executing a dirty NIF can only be + completed up to a certain point while it is executing the + dirty NIF. All Erlang resources such as registered names, + ETS tables, etc will be released. All links and monitors + will be triggered. The actual execution of the NIF will + however not be stopped. The NIF can safely contiue + execution, allocate heap memory, etc, but it is of course better + to stop executing as soon as possible. The NIF can check + whether current process is alive or not using + enif_is_current_process_alive. + Communication using + enif_send, + and enif_port_command + will also be dropped when the sending process is not alive. + Deallocation of certain internal resources such as process + heap, and process control block will be delayed until the + dirty NIF has completed. +

+ +

Currently known issues that are planned to be fixed:

+ + +

+ Since purging of a module currently might need to garbage + collect a process in order to determine if it has + references to the module, a process executing a dirty + NIF might delay purging for a very long time. Delaying + a purge operatin implies delaying all code + loding operations which might cause severe problems for + the system as a whole. +

+
+
+ +
+
+
@@ -507,6 +608,10 @@ typedef struct { CPU-bound, its flags field should be set to ERL_NIF_DIRTY_JOB_CPU_BOUND, or for I/O-bound jobs, ERL_NIF_DIRTY_JOB_IO_BOUND.

+

If one of the + ERL_NIF_DIRTY_JOB_*_BOUND flags is set, and the runtime + system has no support for dirty schedulers, the runtime system + will refuse to load the NIF library.

ErlNifBinary @@ -962,6 +1067,13 @@ typedef enum { Determine if a term is a binary

Return true if term is a binary

+ intenif_is_current_process_alive(ErlNifEnv* env) + Determine if currently executing process is alive or not. +

Return true if currently executing process is currently alive; otherwise + false.

+

This function can only be used from a NIF-calling thread, and with an + environment corresponding to currently executing processes.

+
intenif_is_empty_list(ErlNifEnv* env, ERL_NIF_TERM term) Determine if a term is an empty list

Return true if term is an empty list.

@@ -992,11 +1104,10 @@ typedef enum { intenif_is_on_dirty_scheduler(ErlNifEnv* env) Check to see if executing on a dirty scheduler thread -

Check to see if the current NIF is executing on a dirty scheduler thread. If the - emulator is built with threading support, calling enif_is_on_dirty_scheduler - 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.

+

Check to see if the current NIF is executing on a dirty scheduler thread. If + executing on a dirty scheduler thread true returned; otherwise false.

+

This function can only be used from a NIF-calling thread, and with an + environment corresponding to currently executing processes.

intenif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term) @@ -1010,7 +1121,8 @@ typedef enum { intenif_is_port_alive(ErlNifEnv* env, ErlNifPort *port_id) Determine if a local port is alive or not.

Return true if port_id is currently alive.

-

This function can only be used in a from a NIF-calling thread.

+

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.

intenif_is_process_alive(ErlNifEnv* env, ErlNifPid *pid) Determine if a local process is alive or not. @@ -1478,9 +1590,7 @@ enif_map_iterator_destroy(env, &iter); Send a port_command to to_port

This function works the same as erlang:port_command/2 - except that it is always completely asynchronous. This call may return false - if it detects that the port is already dead, otherwise it will return true. -

+ except that it is always completely asynchronous.

env The environment of the calling process. May not be NULL. @@ -1499,7 +1609,10 @@ enif_map_iterator_destroy(env, &iter); calls to enif_alloc_env, enif_make_copy, enif_port_command and enif_free_env into one call. This optimization is only usefull when a majority of the terms are to be copied from env to the msg_env.

-

The call may return false if it detects that the command failed for some reason. Otherwise true is returned.

+

This function return true if the command was successfully sent; otherwise, + false. The call may return false if it detects that the command failed for some + reason. For example, *to_port does not refer to a local port, if currently + executing process, i.e. the sender, is not alive, or if msg is invalid.

See also: enif_get_local_port.

@@ -1630,7 +1743,9 @@ enif_map_iterator_destroy(env, &iter); msg The message term to send. -

Return true on success, or false if *to_pid does not refer to an alive local process.

+

Return true if the message was successfully sent; otherwise, false. The send + operation will fail if *to_pid does not refer to an alive local process, + or if currently executing process, i.e. the sender, is not alive.

The message environment msg_env with all its terms (including msg) will be invalidated by a successful call to enif_send. The environment should either be freed with enif_free_env -- cgit v1.2.3