diff options
Diffstat (limited to 'erts')
116 files changed, 4469 insertions, 3861 deletions
diff --git a/erts/configure.in b/erts/configure.in index 0323058b4a..14c8b50680 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -570,6 +570,17 @@ else WFLAGS="" WERRORFLAGS="" fi + +AC_MSG_CHECKING([CFLAGS for -O switch]) +case "$CFLAGS" in + *-O*) AC_MSG_RESULT([yes]) ;; + *) + AC_MSG_ERROR([ + CFLAGS must contain a -O flag. If you need to edit the CFLAGS you probably + also want to add the default CFLAGS. The default CFLAGS are "-O2 -g". + If you want to build erts without any optimization, pass -O0 to CFLAGS.]) ;; +esac + dnl DEBUG_FLAGS is obsolete (I hope) AC_SUBST(DEBUG_FLAGS) AC_SUBST(DEBUG_CFLAGS) @@ -1489,7 +1500,7 @@ dnl Some Linuxes needs <sys/socketio.h> instead of <sys/sockio.h> dnl AC_CHECK_HEADERS(fcntl.h limits.h unistd.h syslog.h dlfcn.h ieeefp.h \ sys/types.h sys/stropts.h sys/sysctl.h \ - sys/ioctl.h sys/time.h sys/uio.h \ + sys/ioctl.h sys/time.h sys/uio.h sys/mman.h \ sys/socket.h sys/sockio.h sys/socketio.h \ net/errno.h malloc.h arpa/nameser.h libdlpi.h \ pty.h util.h libutil.h utmp.h langinfo.h poll.h sdkddkver.h) @@ -3001,7 +3012,11 @@ AC_TRY_RUN([ ], erl_code_model_small=yes, erl_code_model_small=no, -erl_code_model_small=no) +[case X$erl_xcomp_code_model_small in + X) erl_code_model_small=no;; + Xyes|Xno) erl_code_model_small=$erl_xcomp_code_model_small;; + *) AC_MSG_ERROR([Bad erl_xcomp_code_model_small value: $erl_xcomp_code_model_small]);; + esac]) AC_MSG_RESULT([$erl_code_model_small]) LDFLAGS="$saved_LDFLAGS" case $erl_code_model_small in diff --git a/erts/doc/src/atomics.xml b/erts/doc/src/atomics.xml index 3fca92fb97..455973f011 100644 --- a/erts/doc/src/atomics.xml +++ b/erts/doc/src/atomics.xml @@ -23,14 +23,14 @@ <title>atomics</title> </header> - <module>atomics</module> + <module since="OTP 21.2">atomics</module> <modulesummary>Atomic Functions</modulesummary> <description> <p>This module provides a set of functions to do atomic operations towards mutable atomic variables. The implementation utilizes only atomic hardware instructions without any software level locking, which makes it very efficient for concurrent access. The atomics are organized into - arrays with the follwing semantics:</p> + arrays with the following semantics:</p> <list type="bulleted"> <item> <p>Atomics are 64 bit integers.</p> @@ -70,7 +70,7 @@ <funcs> <func> - <name name="new" arity="2"/> + <name name="new" arity="2" since="OTP 21.2"/> <fsummary>Create atomic array</fsummary> <desc> <p>Create a new atomic array of <c><anno>Arity</anno></c> atomics.</p> @@ -85,11 +85,13 @@ bsl 64)-1</c>.</p> </item> </taglist> + <p>Atomics are not tied to the current process and are automatically + garbage collected when they are no longer referenced.</p> </desc> </func> <func> - <name name="put" arity="3"/> + <name name="put" arity="3" since="OTP 21.2"/> <fsummary>Set atomic value</fsummary> <desc> <p>Set atomic to <c><anno>Value</anno></c>.</p> @@ -97,7 +99,7 @@ </func> <func> - <name name="get" arity="2"/> + <name name="get" arity="2" since="OTP 21.2"/> <fsummary>Read atomic value</fsummary> <desc> <p>Read atomic value.</p> @@ -105,7 +107,7 @@ </func> <func> - <name name="add" arity="3"/> + <name name="add" arity="3" since="OTP 21.2"/> <fsummary>Add to atomic</fsummary> <desc> <p>Add <c><anno>Incr</anno></c> to atomic.</p> @@ -113,7 +115,7 @@ </func> <func> - <name name="add_get" arity="3"/> + <name name="add_get" arity="3" since="OTP 21.2"/> <fsummary>Atomic add and get</fsummary> <desc> <p>Atomic addition and return of the result.</p> @@ -121,7 +123,7 @@ </func> <func> - <name name="sub" arity="3"/> + <name name="sub" arity="3" since="OTP 21.2"/> <fsummary>Subtract from atomic</fsummary> <desc> <p>Subtract <c><anno>Decr</anno></c> from atomic.</p> @@ -129,7 +131,7 @@ </func> <func> - <name name="sub_get" arity="3"/> + <name name="sub_get" arity="3" since="OTP 21.2"/> <fsummary>Atomic sub and get</fsummary> <desc> <p>Atomic subtraction and return of the result.</p> @@ -137,7 +139,7 @@ </func> <func> - <name name="exchange" arity="3"/> + <name name="exchange" arity="3" since="OTP 21.2"/> <fsummary>Atomic exchange.</fsummary> <desc> <p>Atomically replaces the value of the atomic with @@ -147,7 +149,7 @@ </func> <func> - <name name="compare_exchange" arity="4"/> + <name name="compare_exchange" arity="4" since="OTP 21.2"/> <fsummary>Atomic compare and exchange.</fsummary> <desc> <p>Atomically compares the atomic with <c><anno>Expected</anno></c>, @@ -158,7 +160,7 @@ </func> <func> - <name name="info" arity="1"/> + <name name="info" arity="1" since="OTP 21.2"/> <fsummary>Get information about atomic array.</fsummary> <desc> <p>Return information about an atomic array in a map. The map diff --git a/erts/doc/src/counters.xml b/erts/doc/src/counters.xml index ba4a22759f..36816bd68d 100644 --- a/erts/doc/src/counters.xml +++ b/erts/doc/src/counters.xml @@ -23,13 +23,13 @@ <title>counters</title> </header> - <module>counters</module> + <module since="OTP 21.2">counters</module> <modulesummary>Counter Functions</modulesummary> <description> <p>This module provides a set of functions to do operations towards shared mutable counter variables. The implementation does not utilize any software level locking, which makes it very efficient for concurrent - access. The counters are organized into arrays with the follwing + access. The counters are organized into arrays with the following semantics:</p> <list type="bulleted"> <item> @@ -71,7 +71,7 @@ <funcs> <func> - <name name="new" arity="2"/> + <name name="new" arity="2" since="OTP 21.2"/> <fsummary>Create counter array</fsummary> <desc> <p>Create a new counter array of <c><anno>Size</anno></c> counters.</p> @@ -80,7 +80,7 @@ <taglist> <tag><c>atomics</c> (Default)</tag> <item><p>Counters will be sequentially consistent. If write - operation A is done sequencially before write operation B, then a concurrent reader + operation A is done sequentially before write operation B, then a concurrent reader may see none of them, only A, or both A and B. It cannot see only B.</p> </item> <tag><c>write_concurrency</c></tag> @@ -90,7 +90,7 @@ inconsistency and memory consumption per counter.</p> <p>Read operations may see sequentially inconsistent results with regard to concurrent write operations. Even if write operation A is done - sequencially before write operation B, a concurrent reader may see any + sequentially before write operation B, a concurrent reader may see any combination of A and B, including only B. A read operation is only guaranteed to see all writes done sequentially before the read. No writes are ever lost, but will eventually all be seen.</p> @@ -103,11 +103,13 @@ acceptable.</p> </item> </taglist> + <p>Counters are not tied to the current process and are automatically + garbage collected when they are no longer referenced.</p> </desc> </func> <func> - <name name="get" arity="2"/> + <name name="get" arity="2" since="OTP 21.2"/> <fsummary>Read counter value</fsummary> <desc> <p>Read counter value.</p> @@ -115,7 +117,7 @@ </func> <func> - <name name="add" arity="3"/> + <name name="add" arity="3" since="OTP 21.2"/> <fsummary>Add to counter</fsummary> <desc> <p>Add <c><anno>Incr</anno></c> to counter at index @@ -124,7 +126,7 @@ </func> <func> - <name name="sub" arity="3"/> + <name name="sub" arity="3" since="OTP 21.2"/> <fsummary>Subtract from counter</fsummary> <desc> <p>Subtract <c><anno>Decr</anno></c> from counter at index @@ -133,25 +135,25 @@ </func> <func> - <name name="put" arity="3"/> + <name name="put" arity="3" since="OTP 21.2"/> <fsummary>Set counter to value</fsummary> <desc> <p>Write <c><anno>Value</anno></c> to counter at index <c><anno>Ix</anno></c>.</p> <note> <p>Despite its name, the <c>write_concurrency</c> optimization does not - improve <c>put</c>. A call to <c>put</c> is a relative heavy + improve <c>put</c>. A call to <c>put</c> is a relatively heavy operation compared to the very lightweight and scalable <seealso marker="#add/3"><c>add</c></seealso> and <seealso marker="#sub/3"> <c>sub</c></seealso>. The cost for a <c>put</c> with - <c>write_concurrency</c> is lika a <seealso marker="#get/2"><c>get</c> + <c>write_concurrency</c> is like a <seealso marker="#get/2"><c>get</c> </seealso> plus a <c>put</c> without <c>write_concurrency</c>.</p> </note> </desc> </func> <func> - <name name="info" arity="1"/> + <name name="info" arity="1" since="OTP 21.2"/> <fsummary>Get information about counter array.</fsummary> <desc> <p>Return information about a counter array in a map. The map diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 7055889e4a..58678f2393 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -944,7 +944,7 @@ int suggested_stack_size;</code> <funcs> <func> - <name><ret>void</ret><nametext>add_driver_entry(ErlDrvEntry + <name since=""><ret>void</ret><nametext>add_driver_entry(ErlDrvEntry *de)</nametext></name> <fsummary>Add a driver entry.</fsummary> <desc> @@ -968,7 +968,7 @@ int suggested_stack_size;</code> </func> <func> - <name><ret>void *</ret> + <name since=""><ret>void *</ret> <nametext>driver_alloc(ErlDrvSizeT size)</nametext></name> <fsummary>Allocate memory.</fsummary> <desc> @@ -985,7 +985,7 @@ int suggested_stack_size;</code> </func> <func> - <name><ret>ErlDrvBinary *</ret> + <name since=""><ret>ErlDrvBinary *</ret> <nametext>driver_alloc_binary(ErlDrvSizeT size)</nametext></name> <fsummary>Allocate a driver binary.</fsummary> <desc> @@ -1008,7 +1008,7 @@ int suggested_stack_size;</code> </func> <func> - <name><ret>long</ret><nametext>driver_async(ErlDrvPort port, unsigned + <name since=""><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> @@ -1076,7 +1076,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>unsigned int</ret><nametext>driver_async_port_key(ErlDrvPort + <name since="OTP R16B02"><ret>unsigned int</ret><nametext>driver_async_port_key(ErlDrvPort port)</nametext></name> <fsummary>Calculate an async key from an ErlDrvPort.</fsummary> <desc> @@ -1096,7 +1096,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>long</ret> + <name since=""><ret>long</ret> <nametext>driver_binary_dec_refc(ErlDrvBinary *bin)</nametext></name> <fsummary>Decrement the reference count of a driver binary.</fsummary> <desc> @@ -1117,7 +1117,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>long</ret> + <name since=""><ret>long</ret> <nametext>driver_binary_get_refc(ErlDrvBinary *bin)</nametext></name> <fsummary>Get the reference count of a driver binary.</fsummary> <desc> @@ -1128,7 +1128,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>long</ret> + <name since=""><ret>long</ret> <nametext>driver_binary_inc_refc(ErlDrvBinary *bin)</nametext></name> <fsummary>Increment the reference count of a driver binary.</fsummary> <desc> @@ -1140,7 +1140,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvTermData</ret><nametext>driver_caller(ErlDrvPort + <name since=""><ret>ErlDrvTermData</ret><nametext>driver_caller(ErlDrvPort port)</nametext></name> <fsummary>Return the process making the driver call.</fsummary> <desc> @@ -1183,7 +1183,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret> + <name since=""><ret>int</ret> <nametext>driver_cancel_timer(ErlDrvPort port)</nametext></name> <fsummary>Cancel a previously set timer.</fsummary> <desc> @@ -1196,7 +1196,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_compare_monitors(const ErlDrvMonitor + <name since=""><ret>int</ret><nametext>driver_compare_monitors(const ErlDrvMonitor *monitor1, const ErlDrvMonitor *monitor2)</nametext></name> <fsummary>Compare two monitors.</fsummary> <desc> @@ -1211,7 +1211,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvTermData</ret><nametext>driver_connected(ErlDrvPort + <name since=""><ret>ErlDrvTermData</ret><nametext>driver_connected(ErlDrvPort port)</nametext></name> <fsummary>Return the port owner process.</fsummary> <desc> @@ -1223,7 +1223,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvPort</ret><nametext>driver_create_port(ErlDrvPort port, + <name since=""><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> @@ -1269,7 +1269,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_demonitor_process(ErlDrvPort port, + <name since=""><ret>int</ret><nametext>driver_demonitor_process(ErlDrvPort port, const ErlDrvMonitor *monitor)</nametext></name> <fsummary>Stop monitoring a process from a driver.</fsummary> <desc> @@ -1281,7 +1281,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvSizeT</ret><nametext>driver_deq(ErlDrvPort port, + <name since=""><ret>ErlDrvSizeT</ret><nametext>driver_deq(ErlDrvPort port, ErlDrvSizeT size)</nametext></name> <fsummary>Dequeue data from the head of the driver queue.</fsummary> <desc> @@ -1299,7 +1299,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_enq(ErlDrvPort port, char* buf, + <name since=""><ret>int</ret><nametext>driver_enq(ErlDrvPort port, char* buf, ErlDrvSizeT len)</nametext></name> <fsummary>Enqueue data in the driver queue.</fsummary> <desc> @@ -1325,7 +1325,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_enq_bin(ErlDrvPort port, + <name since=""><ret>int</ret><nametext>driver_enq_bin(ErlDrvPort port, ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext> </name> <fsummary>Enqueue binary in the driver queue.</fsummary> @@ -1346,7 +1346,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_enqv(ErlDrvPort port, ErlIOVec *ev, + <name since=""><ret>int</ret><nametext>driver_enqv(ErlDrvPort port, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name> <fsummary>Enqueue vector in the driver queue.</fsummary> <desc> @@ -1365,11 +1365,11 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_failure(ErlDrvPort port, int + <name since=""><ret>int</ret><nametext>driver_failure(ErlDrvPort port, int error)</nametext></name> - <name><ret>int</ret><nametext>driver_failure_atom(ErlDrvPort port, char + <name since=""><ret>int</ret><nametext>driver_failure_atom(ErlDrvPort port, char *string)</nametext></name> - <name><ret>int</ret><nametext>driver_failure_posix(ErlDrvPort port, int + <name since=""><ret>int</ret><nametext>driver_failure_posix(ErlDrvPort port, int error)</nametext></name> <fsummary>Fail with error.</fsummary> <desc> @@ -1393,7 +1393,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_failure_eof(ErlDrvPort + <name since=""><ret>int</ret><nametext>driver_failure_eof(ErlDrvPort port)</nametext></name> <fsummary>Fail with EOF.</fsummary> <desc> @@ -1408,7 +1408,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>driver_free(void *ptr)</nametext></name> + <name since=""><ret>void</ret><nametext>driver_free(void *ptr)</nametext></name> <fsummary>Free an allocated memory block.</fsummary> <desc> <marker id="driver_free"></marker> @@ -1422,7 +1422,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret> + <name since=""><ret>void</ret> <nametext>driver_free_binary(ErlDrvBinary *bin)</nametext></name> <fsummary>Free a driver binary.</fsummary> <desc> @@ -1436,7 +1436,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvTermData</ret> + <name since=""><ret>ErlDrvTermData</ret> <nametext>driver_get_monitored_process(ErlDrvPort port, const ErlDrvMonitor *monitor)</nametext></name> <fsummary>Retrieve the process ID from a monitor.</fsummary> @@ -1452,7 +1452,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret> + <name since=""><ret>int</ret> <nametext>driver_get_now(ErlDrvNowData *now)</nametext></name> <fsummary>Read a system time stamp.</fsummary> <desc> @@ -1473,7 +1473,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_lock_driver(ErlDrvPort + <name since=""><ret>int</ret><nametext>driver_lock_driver(ErlDrvPort port)</nametext></name> <fsummary>Ensure the driver is never unloaded.</fsummary> <desc> @@ -1486,7 +1486,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvTermData</ret><nametext>driver_mk_atom(char* + <name since=""><ret>ErlDrvTermData</ret><nametext>driver_mk_atom(char* string)</nametext></name> <fsummary>Make an atom from a name.</fsummary> <desc> @@ -1501,7 +1501,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvTermData</ret><nametext>driver_mk_port(ErlDrvPort + <name since=""><ret>ErlDrvTermData</ret><nametext>driver_mk_port(ErlDrvPort port)</nametext></name> <fsummary>Make an Erlang term port from a port.</fsummary> <desc> @@ -1517,7 +1517,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_monitor_process(ErlDrvPort port, + <name since=""><ret>int</ret><nametext>driver_monitor_process(ErlDrvPort port, ErlDrvTermData process, ErlDrvMonitor *monitor)</nametext></name> <fsummary>Monitor a process from a driver.</fsummary> <desc> @@ -1540,7 +1540,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_output(ErlDrvPort port, char *buf, + <name since=""><ret>int</ret><nametext>driver_output(ErlDrvPort port, char *buf, ErlDrvSizeT len)</nametext></name> <fsummary>Send data from driver to port owner.</fsummary> <desc> @@ -1560,7 +1560,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_output_binary(ErlDrvPort port, char + <name since=""><ret>int</ret><nametext>driver_output_binary(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, ErlDrvBinary* bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext></name> <fsummary>Send data from a driver binary to port owner.</fsummary> @@ -1589,7 +1589,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_output_term(ErlDrvPort port, + <name since=""><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> @@ -1608,7 +1608,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_output2(ErlDrvPort port, char *hbuf, + <name since=""><ret>int</ret><nametext>driver_output2(ErlDrvPort port, char *hbuf, ErlDrvSizeT hlen, char *buf, ErlDrvSizeT len)</nametext></name> <fsummary>Send data and binary data to port owner.</fsummary> <desc> @@ -1625,7 +1625,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_outputv(ErlDrvPort port, char* hbuf, + <name since=""><ret>int</ret><nametext>driver_outputv(ErlDrvPort port, char* hbuf, ErlDrvSizeT hlen, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name> <fsummary>Send vectorized data to port owner.</fsummary> <desc> @@ -1654,7 +1654,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvPDL</ret> + <name since=""><ret>ErlDrvPDL</ret> <nametext>driver_pdl_create(ErlDrvPort port)</nametext></name> <fsummary>Create a port data lock.</fsummary> <desc> @@ -1672,7 +1672,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>long</ret><nametext>driver_pdl_dec_refc(ErlDrvPDL + <name since=""><ret>long</ret><nametext>driver_pdl_dec_refc(ErlDrvPDL pdl)</nametext></name> <fsummary></fsummary> <desc> @@ -1686,7 +1686,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>long</ret> + <name since=""><ret>long</ret> <nametext>driver_pdl_get_refc(ErlDrvPDL pdl)</nametext></name> <fsummary></fsummary> <desc> @@ -1698,7 +1698,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>long</ret> + <name since=""><ret>long</ret> <nametext>driver_pdl_inc_refc(ErlDrvPDL pdl)</nametext></name> <fsummary></fsummary> <desc> @@ -1712,7 +1712,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret> + <name since=""><ret>void</ret> <nametext>driver_pdl_lock(ErlDrvPDL pdl)</nametext></name> <fsummary>Lock port data lock.</fsummary> <desc> @@ -1723,7 +1723,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret> + <name since=""><ret>void</ret> <nametext>driver_pdl_unlock(ErlDrvPDL pdl)</nametext></name> <fsummary>Unlock port data lock.</fsummary> <desc> @@ -1734,7 +1734,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>SysIOVec *</ret><nametext>driver_peekq(ErlDrvPort port, int + <name since=""><ret>SysIOVec *</ret><nametext>driver_peekq(ErlDrvPort port, int *vlen)</nametext></name> <fsummary>Get the driver queue as a vector.</fsummary> <desc> @@ -1755,7 +1755,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvSizeT</ret><nametext>driver_peekqv(ErlDrvPort port, + <name since="OTP R15B"><ret>ErlDrvSizeT</ret><nametext>driver_peekqv(ErlDrvPort port, ErlIOVec *ev)</nametext></name> <fsummary>Get the driver queue as an I/O vector.</fsummary> <desc> @@ -1775,7 +1775,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_pushq(ErlDrvPort port, char* buf, + <name since=""><ret>int</ret><nametext>driver_pushq(ErlDrvPort port, char* buf, ErlDrvSizeT len)</nametext></name> <fsummary>Push data at the head of the driver queue.</fsummary> <desc> @@ -1792,7 +1792,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_pushq_bin(ErlDrvPort port, + <name since=""><ret>int</ret><nametext>driver_pushq_bin(ErlDrvPort port, ErlDrvBinary *bin, ErlDrvSizeT offset, ErlDrvSizeT len)</nametext> </name> <fsummary>Push binary at the head of the driver queue.</fsummary> @@ -1812,7 +1812,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_pushqv(ErlDrvPort port, ErlIOVec + <name since=""><ret>int</ret><nametext>driver_pushqv(ErlDrvPort port, ErlIOVec *ev, ErlDrvSizeT skip)</nametext></name> <fsummary>Push vector at the head of the driver queue.</fsummary> <desc> @@ -1831,7 +1831,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_read_timer(ErlDrvPort port, unsigned + <name since=""><ret>int</ret><nametext>driver_read_timer(ErlDrvPort port, unsigned long *time_left)</nametext></name> <fsummary>Read the time left before time-out.</fsummary> <desc> @@ -1844,7 +1844,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void *</ret> + <name since=""><ret>void *</ret> <nametext>driver_realloc(void *ptr, ErlDrvSizeT size)</nametext></name> <fsummary>Resize an allocated memory block.</fsummary> <desc> @@ -1859,7 +1859,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvBinary *</ret> + <name since=""><ret>ErlDrvBinary *</ret> <nametext>driver_realloc_binary(ErlDrvBinary *bin, ErlDrvSizeT size) </nametext></name> <fsummary>Resize a driver binary.</fsummary> @@ -1873,7 +1873,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_select(ErlDrvPort port, ErlDrvEvent + <name since=""><ret>int</ret><nametext>driver_select(ErlDrvPort port, ErlDrvEvent event, int mode, int on)</nametext></name> <fsummary>Provides an event for having the emulator call the driver. </fsummary> @@ -1932,7 +1932,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_send_term(ErlDrvPort port, + <name since=""><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> @@ -1958,7 +1958,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>driver_set_timer(ErlDrvPort port, unsigned + <name since=""><ret>int</ret><nametext>driver_set_timer(ErlDrvPort port, unsigned long time)</nametext></name> <fsummary>Set a timer to call the driver.</fsummary> <desc> @@ -1977,7 +1977,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvSizeT</ret> + <name since=""><ret>ErlDrvSizeT</ret> <nametext>driver_sizeq(ErlDrvPort port)</nametext></name> <fsummary>Return the size of the driver queue.</fsummary> <desc> @@ -1991,7 +1991,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>driver_system_info(ErlDrvSysInfo + <name since=""><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> @@ -2008,7 +2008,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvSizeT</ret><nametext>driver_vec_to_buf(ErlIOVec *ev, + <name since=""><ret>ErlDrvSizeT</ret><nametext>driver_vec_to_buf(ErlIOVec *ev, char *buf, ErlDrvSizeT len)</nametext></name> <fsummary>Collect data segments into a buffer.</fsummary> <desc> @@ -2029,7 +2029,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_busy_msgq_limits(ErlDrvPort port, + <name since="OTP R16B"><ret>void</ret><nametext>erl_drv_busy_msgq_limits(ErlDrvPort port, ErlDrvSizeT *low, ErlDrvSizeT *high)</nametext></name> <fsummary>Set and get limits for busy port message queue.</fsummary> <desc> @@ -2083,7 +2083,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_cond_broadcast(ErlDrvCond + <name since=""><ret>void</ret><nametext>erl_drv_cond_broadcast(ErlDrvCond *cnd)</nametext></name> <fsummary>Broadcast on a condition variable.</fsummary> <desc> @@ -2097,7 +2097,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvCond *</ret><nametext>erl_drv_cond_create(char + <name since=""><ret>ErlDrvCond *</ret><nametext>erl_drv_cond_create(char *name)</nametext></name> <fsummary>Create a condition variable.</fsummary> <desc> @@ -2114,7 +2114,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_cond_destroy(ErlDrvCond + <name since=""><ret>void</ret><nametext>erl_drv_cond_destroy(ErlDrvCond *cnd)</nametext></name> <fsummary>Destroy a condition variable.</fsummary> <desc> @@ -2128,7 +2128,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>char *</ret><nametext>erl_drv_cond_name(ErlDrvCond + <name since="OTP R16B02"><ret>char *</ret><nametext>erl_drv_cond_name(ErlDrvCond *cnd)</nametext></name> <fsummary>Get name of driver mutex.</fsummary> <desc> @@ -2142,7 +2142,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_cond_signal(ErlDrvCond + <name since=""><ret>void</ret><nametext>erl_drv_cond_signal(ErlDrvCond *cnd)</nametext></name> <fsummary>Signal on a condition variable.</fsummary> <desc> @@ -2156,7 +2156,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_cond_wait(ErlDrvCond *cnd, + <name since=""><ret>void</ret><nametext>erl_drv_cond_wait(ErlDrvCond *cnd, ErlDrvMutex *mtx)</nametext></name> <fsummary>Wait on a condition variable.</fsummary> <desc> @@ -2185,7 +2185,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>erl_drv_consume_timeslice(ErlDrvPort port, + <name since="OTP R16B"><ret>int</ret><nametext>erl_drv_consume_timeslice(ErlDrvPort port, int percent)</nametext></name> <fsummary>Give the runtime system a hint about how much CPU time the current driver callback call has consumed.</fsummary> @@ -2228,7 +2228,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvTime</ret><nametext>erl_drv_convert_time_unit(ErlDrvTime + <name since="OTP 18.3"><ret>ErlDrvTime</ret><nametext>erl_drv_convert_time_unit(ErlDrvTime val, ErlDrvTimeUnit from, ErlDrvTimeUnit to)</nametext></name> <fsummary>Convert time unit of a time value.</fsummary> <desc> @@ -2254,7 +2254,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>erl_drv_equal_tids(ErlDrvTid tid1, + <name since=""><ret>int</ret><nametext>erl_drv_equal_tids(ErlDrvTid tid1, ErlDrvTid tid2)</nametext></name> <fsummary>Compare thread identifiers for equality.</fsummary> <desc> @@ -2276,7 +2276,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>erl_drv_getenv(const char *key, char + <name since=""><ret>int</ret><nametext>erl_drv_getenv(const char *key, char *value, size_t *value_size)</nametext></name> <fsummary>Get the value of an environment variable.</fsummary> <desc> @@ -2317,7 +2317,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_init_ack(ErlDrvPort port, + <name since="OTP 19.0"><ret>void</ret><nametext>erl_drv_init_ack(ErlDrvPort port, ErlDrvData res)</nametext></name> <fsummary>Acknowledge the start of the port.</fsummary> <desc> @@ -2345,7 +2345,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvTime</ret> + <name since="OTP 18.3"><ret>ErlDrvTime</ret> <nametext>erl_drv_monotonic_time(ErlDrvTimeUnit time_unit)</nametext> </name> <fsummary>Get Erlang monotonic time.</fsummary> @@ -2365,7 +2365,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>ErlDrvMutex *</ret><nametext>erl_drv_mutex_create(char + <name since=""><ret>ErlDrvMutex *</ret><nametext>erl_drv_mutex_create(char *name)</nametext></name> <fsummary>Create a mutex.</fsummary> <desc> @@ -2380,7 +2380,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_mutex_destroy(ErlDrvMutex + <name since=""><ret>void</ret><nametext>erl_drv_mutex_destroy(ErlDrvMutex *mtx)</nametext></name> <fsummary>Destroy a mutex.</fsummary> <desc> @@ -2395,7 +2395,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_mutex_lock(ErlDrvMutex + <name since=""><ret>void</ret><nametext>erl_drv_mutex_lock(ErlDrvMutex *mtx)</nametext></name> <fsummary>Lock a mutex.</fsummary> <desc> @@ -2414,7 +2414,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>char *</ret><nametext>erl_drv_mutex_name(ErlDrvMutex + <name since="OTP R16B02"><ret>char *</ret><nametext>erl_drv_mutex_name(ErlDrvMutex *mtx)</nametext></name> <fsummary>Get name of driver mutex.</fsummary> <desc> @@ -2428,7 +2428,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>erl_drv_mutex_trylock(ErlDrvMutex + <name since=""><ret>int</ret><nametext>erl_drv_mutex_trylock(ErlDrvMutex *mtx)</nametext></name> <fsummary>Try lock a mutex.</fsummary> <desc> @@ -2447,7 +2447,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>void</ret><nametext>erl_drv_mutex_unlock(ErlDrvMutex + <name since=""><ret>void</ret><nametext>erl_drv_mutex_unlock(ErlDrvMutex *mtx)</nametext></name> <fsummary>Unlock a mutex.</fsummary> <desc> @@ -2460,7 +2460,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code> </func> <func> - <name><ret>int</ret><nametext>erl_drv_output_term(ErlDrvTermData port, + <name since="OTP R16B"><ret>int</ret><nametext>erl_drv_output_term(ErlDrvTermData port, ErlDrvTermData* term, int n)</nametext></name> <fsummary>Send term data from driver to port owner.</fsummary> <desc> @@ -2637,7 +2637,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>int</ret><nametext>erl_drv_putenv(const char *key, char + <name since=""><ret>int</ret><nametext>erl_drv_putenv(const char *key, char *value)</nametext></name> <fsummary>Set the value of an environment variable.</fsummary> <desc> @@ -2668,7 +2668,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>ErlDrvRWLock *</ret><nametext>erl_drv_rwlock_create(char + <name since=""><ret>ErlDrvRWLock *</ret><nametext>erl_drv_rwlock_create(char *name)</nametext></name> <fsummary>Create an rwlock.</fsummary> <desc> @@ -2684,7 +2684,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_rwlock_destroy(ErlDrvRWLock + <name since=""><ret>void</ret><nametext>erl_drv_rwlock_destroy(ErlDrvRWLock *rwlck)</nametext></name> <fsummary>Destroy an rwlock.</fsummary> <desc> @@ -2699,7 +2699,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>char *</ret><nametext>erl_drv_rwlock_name(ErlDrvRWLock + <name since="OTP R16B02"><ret>char *</ret><nametext>erl_drv_rwlock_name(ErlDrvRWLock *rwlck)</nametext></name> <fsummary>Get name of driver mutex.</fsummary> <desc> @@ -2713,7 +2713,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_rwlock_rlock(ErlDrvRWLock + <name since=""><ret>void</ret><nametext>erl_drv_rwlock_rlock(ErlDrvRWLock *rwlck)</nametext></name> <fsummary>Read lock an rwlock.</fsummary> <desc> @@ -2733,7 +2733,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_rwlock_runlock(ErlDrvRWLock + <name since=""><ret>void</ret><nametext>erl_drv_rwlock_runlock(ErlDrvRWLock *rwlck)</nametext></name> <fsummary>Read unlock an rwlock.</fsummary> <desc> @@ -2746,7 +2746,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_rwlock_rwlock(ErlDrvRWLock + <name since=""><ret>void</ret><nametext>erl_drv_rwlock_rwlock(ErlDrvRWLock *rwlck)</nametext></name> <fsummary>Read/write lock an rwlock.</fsummary> <desc> @@ -2766,7 +2766,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_rwlock_rwunlock(ErlDrvRWLock + <name since=""><ret>void</ret><nametext>erl_drv_rwlock_rwunlock(ErlDrvRWLock *rwlck)</nametext></name> <fsummary>Read/write unlock an rwlock.</fsummary> <desc> @@ -2779,7 +2779,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>int</ret><nametext>erl_drv_rwlock_tryrlock(ErlDrvRWLock + <name since=""><ret>int</ret><nametext>erl_drv_rwlock_tryrlock(ErlDrvRWLock *rwlck)</nametext></name> <fsummary>Try to read lock an rwlock.</fsummary> <desc> @@ -2799,7 +2799,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>int</ret><nametext>erl_drv_rwlock_tryrwlock(ErlDrvRWLock + <name since=""><ret>int</ret><nametext>erl_drv_rwlock_tryrwlock(ErlDrvRWLock *rwlck)</nametext></name> <fsummary>Try to read/write lock an rwlock.</fsummary> <desc> @@ -2819,7 +2819,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>int</ret><nametext>erl_drv_send_term(ErlDrvTermData port, + <name since="OTP R16B"><ret>int</ret><nametext>erl_drv_send_term(ErlDrvTermData port, ErlDrvTermData receiver, ErlDrvTermData* term, int n)</nametext></name> <fsummary>Send term data to other process than port owner process. </fsummary> @@ -2843,7 +2843,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_set_os_pid(ErlDrvPort port, + <name since="OTP 19.0"><ret>void</ret><nametext>erl_drv_set_os_pid(ErlDrvPort port, ErlDrvSInt pid)</nametext></name> <fsummary>Set the os_pid for the port.</fsummary> <desc> @@ -2857,7 +2857,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>int</ret><nametext>erl_drv_thread_create(char *name, ErlDrvTid + <name since=""><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> @@ -2920,7 +2920,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_thread_exit(void + <name since=""><ret>void</ret><nametext>erl_drv_thread_exit(void *exit_value)</nametext></name> <fsummary>Terminate calling thread.</fsummary> <desc> @@ -2939,7 +2939,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>int</ret><nametext>erl_drv_thread_join(ErlDrvTid tid, void + <name since=""><ret>int</ret><nametext>erl_drv_thread_join(ErlDrvTid tid, void **exit_value)</nametext></name> <fsummary>Join with another thread.</fsummary> <desc> @@ -2962,7 +2962,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>char *</ret><nametext>erl_drv_thread_name(ErlDrvTid + <name since="OTP R16B02"><ret>char *</ret><nametext>erl_drv_thread_name(ErlDrvTid tid)</nametext></name> <fsummary>Get name of driver mutex.</fsummary> <desc> @@ -2976,7 +2976,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>ErlDrvThreadOpts *</ret> + <name since=""><ret>ErlDrvThreadOpts *</ret> <nametext>erl_drv_thread_opts_create(char *name)</nametext></name> <fsummary>Create thread options.</fsummary> <desc> @@ -3005,7 +3005,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret> + <name since=""><ret>void</ret> <nametext>erl_drv_thread_opts_destroy(ErlDrvThreadOpts *opts)</nametext> </name> <fsummary>Destroy thread options.</fsummary> @@ -3020,7 +3020,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>ErlDrvTid</ret> + <name since=""><ret>ErlDrvTid</ret> <nametext>erl_drv_thread_self(void)</nametext></name> <fsummary>Get the thread identifier of the current thread.</fsummary> <desc> @@ -3031,7 +3031,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>ErlDrvTime</ret><nametext>erl_drv_time_offset(ErlDrvTimeUnit + <name since="OTP 18.3"><ret>ErlDrvTime</ret><nametext>erl_drv_time_offset(ErlDrvTimeUnit time_unit)</nametext></name> <fsummary>Get current time offset.</fsummary> <desc> @@ -3054,7 +3054,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void *</ret><nametext>erl_drv_tsd_get(ErlDrvTSDKey + <name since=""><ret>void *</ret><nametext>erl_drv_tsd_get(ErlDrvTSDKey key)</nametext></name> <fsummary>Get thread-specific data.</fsummary> <desc> @@ -3069,7 +3069,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>int</ret><nametext>erl_drv_tsd_key_create(char *name, + <name since=""><ret>int</ret><nametext>erl_drv_tsd_key_create(char *name, ErlDrvTSDKey *key)</nametext></name> <fsummary>Create a thread-specific data key.</fsummary> <desc> @@ -3086,7 +3086,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_tsd_key_destroy(ErlDrvTSDKey + <name since=""><ret>void</ret><nametext>erl_drv_tsd_key_destroy(ErlDrvTSDKey key)</nametext></name> <fsummary>Destroy a thread-specific data key.</fsummary> <desc> @@ -3111,7 +3111,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>erl_drv_tsd_set(ErlDrvTSDKey key, void + <name since=""><ret>void</ret><nametext>erl_drv_tsd_set(ErlDrvTSDKey key, void *data)</nametext></name> <fsummary>Set thread-specific data.</fsummary> <desc> @@ -3138,7 +3138,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>char *</ret><nametext>erl_errno_id(int error)</nametext></name> + <name since=""><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> @@ -3150,7 +3150,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>int</ret><nametext>remove_driver_entry(ErlDrvEntry + <name since=""><ret>int</ret><nametext>remove_driver_entry(ErlDrvEntry *de)</nametext></name> <fsummary>Remove a driver entry.</fsummary> <desc> @@ -3164,7 +3164,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>set_busy_port(ErlDrvPort port, int + <name since=""><ret>void</ret><nametext>set_busy_port(ErlDrvPort port, int on)</nametext></name> <fsummary>Signal or unsignal port as busy.</fsummary> <desc> @@ -3195,7 +3195,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0] </func> <func> - <name><ret>void</ret><nametext>set_port_control_flags(ErlDrvPort port, + <name since=""><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> diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index b437d180c8..34042cb4de 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -963,7 +963,7 @@ typedef struct { <funcs> <func> - <name><ret>void *</ret><nametext>enif_alloc(size_t size)</nametext></name> + <name since=""><ret>void *</ret><nametext>enif_alloc(size_t size)</nametext></name> <fsummary>Allocate dynamic memory.</fsummary> <desc> <p>Allocates memory of <c>size</c> bytes.</p> @@ -974,7 +974,7 @@ typedef struct { </func> <func> - <name><ret>int</ret> + <name since=""><ret>int</ret> <nametext>enif_alloc_binary(size_t size, ErlNifBinary* bin)</nametext> </name> <fsummary>Create a new binary.</fsummary> @@ -998,7 +998,7 @@ typedef struct { </func> <func> - <name><ret>ErlNifEnv *</ret><nametext>enif_alloc_env()</nametext></name> + <name since="OTP R14B"><ret>ErlNifEnv *</ret><nametext>enif_alloc_env()</nametext></name> <fsummary>Create a new environment.</fsummary> <desc> <p>Allocates a new process independent environment. The environment can @@ -1012,7 +1012,7 @@ typedef struct { </func> <func> - <name><ret>void *</ret><nametext>enif_alloc_resource(ErlNifResourceType* + <name since="OTP R13B04"><ret>void *</ret><nametext>enif_alloc_resource(ErlNifResourceType* type, unsigned size)</nametext></name> <fsummary>Allocate a memory-managed resource object.</fsummary> <desc> @@ -1022,7 +1022,7 @@ typedef struct { </func> <func> - <name><ret>size_t</ret><nametext>enif_binary_to_term(ErlNifEnv *env, + <name since="OTP 19.0"><ret>size_t</ret><nametext>enif_binary_to_term(ErlNifEnv *env, const unsigned char* data, size_t size, ERL_NIF_TERM *term, ErlNifBinaryToTerm opts)</nametext></name> <fsummary>Create a term from the external format.</fsummary> @@ -1047,7 +1047,7 @@ typedef struct { </func> <func> - <name><ret>void</ret><nametext>enif_clear_env(ErlNifEnv* env)</nametext> + <name since="OTP R14B"><ret>void</ret><nametext>enif_clear_env(ErlNifEnv* env)</nametext> </name> <fsummary>Clear an environment for reuse.</fsummary> <desc> @@ -1058,7 +1058,7 @@ typedef struct { </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_compare(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs)</nametext> </name> <fsummary>Compare two terms.</fsummary> @@ -1073,7 +1073,7 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>enif_compare_monitors(const ErlNifMonitor + <name since="OTP 20.0"><ret>int</ret><nametext>enif_compare_monitors(const ErlNifMonitor *monitor1, const ErlNifMonitor *monitor2)</nametext></name> <fsummary>Compare two monitors.</fsummary> <desc> @@ -1088,7 +1088,7 @@ typedef struct { </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_cond_broadcast(ErlNifCond *cnd)</nametext></name> <fsummary></fsummary> <desc> @@ -1098,7 +1098,7 @@ typedef struct { </func> <func> - <name><ret>ErlNifCond *</ret> + <name since="OTP R13B04"><ret>ErlNifCond *</ret> <nametext>enif_cond_create(char *name)</nametext></name> <fsummary></fsummary> <desc> @@ -1108,7 +1108,7 @@ typedef struct { </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_cond_destroy(ErlNifCond *cnd)</nametext></name> <fsummary></fsummary> <desc> @@ -1118,7 +1118,7 @@ typedef struct { </func> <func> - <name><ret>char*</ret> + <name since="OTP 21.0"><ret>char*</ret> <nametext>enif_cond_name(ErlNifCond* cnd)</nametext></name> <fsummary></fsummary> <desc> @@ -1128,7 +1128,7 @@ typedef struct { </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_cond_signal(ErlNifCond *cnd)</nametext></name> <fsummary></fsummary> <desc> @@ -1138,7 +1138,7 @@ typedef struct { </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_cond_wait(ErlNifCond *cnd, ErlNifMutex *mtx)</nametext> </name> <fsummary></fsummary> @@ -1149,7 +1149,7 @@ typedef struct { </func> <func> - <name><ret>int</ret> + <name since="OTP R16B"><ret>int</ret> <nametext>enif_consume_timeslice(ErlNifEnv *env, int percent)</nametext> </name> <fsummary></fsummary> @@ -1184,7 +1184,7 @@ typedef struct { </func> <func> - <name><ret>ErlNifTime</ret><nametext>enif_convert_time_unit(ErlNifTime + <name since="OTP 18.3"><ret>ErlNifTime</ret><nametext>enif_convert_time_unit(ErlNifTime val, ErlNifTimeUnit from, ErlNifTimeUnit to)</nametext></name> <fsummary>Convert time unit of a time value.</fsummary> <desc> @@ -1209,7 +1209,7 @@ typedef struct { </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP 19.0"><ret>ERL_NIF_TERM</ret> <nametext>enif_cpu_time(ErlNifEnv *)</nametext></name> <fsummary></fsummary> <desc> @@ -1225,8 +1225,8 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>enif_demonitor_process(ErlNifEnv* caller_env, void* obj, - const ErlNifMonitor* mon)</nametext></name> + <name since="OTP 20.0"><ret>int</ret><nametext>enif_demonitor_process(ErlNifEnv* caller_env, + void* obj, const ErlNifMonitor* mon)</nametext></name> <fsummary>Cancel a process monitor.</fsummary> <desc> <marker id="enif_demonitor_process"></marker> @@ -1252,7 +1252,7 @@ typedef struct { </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_equal_tids(ErlNifTid tid1, ErlNifTid tid2)</nametext> </name> <fsummary></fsummary> @@ -1263,7 +1263,7 @@ typedef struct { </func> <func> - <name><ret>int</ret><nametext>enif_fprintf(FILE *stream, const char *format, ...)</nametext></name> + <name since="OTP 21.0"><ret>int</ret><nametext>enif_fprintf(FILE *stream, const char *format, ...)</nametext></name> <fsummary>Format strings and Erlang terms.</fsummary> <desc> <p>Similar to <c>fprintf</c> but this format string also accepts @@ -1276,7 +1276,7 @@ typedef struct { </func> <func> - <name><ret>void</ret><nametext>enif_free(void* ptr)</nametext></name> + <name since=""><ret>void</ret><nametext>enif_free(void* ptr)</nametext></name> <fsummary>Free dynamic memory.</fsummary> <desc> <p>Frees memory allocated by @@ -1285,7 +1285,7 @@ typedef struct { </func> <func> - <name><ret>void</ret> + <name since="OTP R14B"><ret>void</ret> <nametext>enif_free_env(ErlNifEnv* env)</nametext></name> <fsummary>Free an environment allocated with enif_alloc_env.</fsummary> <desc> @@ -1296,7 +1296,7 @@ typedef struct { </func> <func> - <name><ret>void</ret> + <name since="OTP 20.1"><ret>void</ret> <nametext>enif_free_iovec(ErlNifIOvec* iov)</nametext></name> <fsummary>Free an ErlIOVec</fsummary> <desc> @@ -1321,7 +1321,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_atom(ErlNifEnv* env, ERL_NIF_TERM + <name since="OTP R13B04"><ret>int</ret><nametext>enif_get_atom(ErlNifEnv* env, ERL_NIF_TERM term, char* buf, unsigned size, ErlNifCharEncoding encode)</nametext> </name> <fsummary>Get the text representation of an atom term.</fsummary> @@ -1337,7 +1337,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_atom_length(ErlNifEnv* env, + <name since="OTP R14B"><ret>int</ret><nametext>enif_get_atom_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len, ErlNifCharEncoding encode)</nametext> </name> <fsummary>Get the length of atom <c>term</c>.</fsummary> @@ -1351,7 +1351,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_double(ErlNifEnv* env, + <name since="OTP R13B04"><ret>int</ret><nametext>enif_get_double(ErlNifEnv* env, ERL_NIF_TERM term, double* dp)</nametext></name> <fsummary>Read a floating-point number term.</fsummary> <desc> @@ -1362,7 +1362,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_int(ErlNifEnv* env, ERL_NIF_TERM + <name since=""><ret>int</ret><nametext>enif_get_int(ErlNifEnv* env, ERL_NIF_TERM term, int* ip)</nametext></name> <fsummary>Read an integer term.</fsummary> <desc> @@ -1373,7 +1373,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_int64(ErlNifEnv* env, ERL_NIF_TERM + <name since="OTP R14B"><ret>int</ret><nametext>enif_get_int64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifSInt64* ip)</nametext></name> <fsummary>Read a 64-bit integer term.</fsummary> <desc> @@ -1384,7 +1384,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_local_pid(ErlNifEnv* env, + <name since="OTP R14B"><ret>int</ret><nametext>enif_get_local_pid(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPid* pid)</nametext></name> <fsummary>Read a local pid term.</fsummary> <desc> @@ -1396,7 +1396,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_local_port(ErlNifEnv* env, + <name since="OTP 19.0"><ret>int</ret><nametext>enif_get_local_port(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifPort* port_id)</nametext></name> <fsummary>Read a local port term.</fsummary> <desc> @@ -1408,7 +1408,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_list_cell(ErlNifEnv* env, + <name since=""><ret>int</ret><nametext>enif_get_list_cell(ErlNifEnv* env, ERL_NIF_TERM list, ERL_NIF_TERM* head, ERL_NIF_TERM* tail)</nametext> </name> <fsummary>Get head and tail from a list.</fsummary> @@ -1420,7 +1420,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_list_length(ErlNifEnv* env, + <name since="OTP R14B"><ret>int</ret><nametext>enif_get_list_length(ErlNifEnv* env, ERL_NIF_TERM term, unsigned* len)</nametext></name> <fsummary>Get the length of list <c>term</c>.</fsummary> <desc> @@ -1431,7 +1431,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_long(ErlNifEnv* env, ERL_NIF_TERM + <name since="OTP R13B04"><ret>int</ret><nametext>enif_get_long(ErlNifEnv* env, ERL_NIF_TERM term, long int* ip)</nametext></name> <fsummary>Read a long integer term.</fsummary> <desc> @@ -1442,7 +1442,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_map_size(ErlNifEnv* env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_get_map_size(ErlNifEnv* env, ERL_NIF_TERM term, size_t *size)</nametext></name> <fsummary>Read the size of a map term.</fsummary> <desc> @@ -1454,7 +1454,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_map_value(ErlNifEnv* env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_get_map_value(ErlNifEnv* env, ERL_NIF_TERM map, ERL_NIF_TERM key, ERL_NIF_TERM* value)</nametext> </name> <fsummary>Get the value of a key in a map.</fsummary> @@ -1467,7 +1467,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_resource(ErlNifEnv* env, + <name since="OTP R13B04"><ret>int</ret><nametext>enif_get_resource(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifResourceType* type, void** objp)</nametext> </name> <fsummary>Get the pointer to a resource object.</fsummary> @@ -1480,7 +1480,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_string(ErlNifEnv* env, + <name since="OTP R13B04"><ret>int</ret><nametext>enif_get_string(ErlNifEnv* env, ERL_NIF_TERM list, char* buf, unsigned size, ErlNifCharEncoding encode)</nametext></name> <fsummary>Get a C-string from a list.</fsummary> @@ -1504,7 +1504,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_tuple(ErlNifEnv* env, ERL_NIF_TERM + <name since="OTP R13B04"><ret>int</ret><nametext>enif_get_tuple(ErlNifEnv* env, ERL_NIF_TERM term, int* arity, const ERL_NIF_TERM** array)</nametext></name> <fsummary>Inspect the elements of a tuple.</fsummary> <desc> @@ -1520,7 +1520,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_uint(ErlNifEnv* env, ERL_NIF_TERM + <name since="OTP R13B04"><ret>int</ret><nametext>enif_get_uint(ErlNifEnv* env, ERL_NIF_TERM term, unsigned int* ip)</nametext></name> <fsummary>Read an unsigned integer term.</fsummary> <desc> @@ -1532,7 +1532,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_uint64(ErlNifEnv* env, + <name since="OTP R14B"><ret>int</ret><nametext>enif_get_uint64(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifUInt64* ip)</nametext></name> <fsummary>Read an unsigned 64-bit integer term.</fsummary> <desc> @@ -1544,7 +1544,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_get_ulong(ErlNifEnv* env, ERL_NIF_TERM + <name since=""><ret>int</ret><nametext>enif_get_ulong(ErlNifEnv* env, ERL_NIF_TERM term, unsigned long* ip)</nametext></name> <fsummary>Read an unsigned integer term.</fsummary> <desc> @@ -1557,7 +1557,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_getenv(const char* key, char* value, + <name since="OTP 18.2"><ret>int</ret><nametext>enif_getenv(const char* key, char* value, size_t *value_size)</nametext></name> <fsummary>Get the value of an environment variable.</fsummary> <desc> @@ -1567,7 +1567,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_has_pending_exception(ErlNifEnv* env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_has_pending_exception(ErlNifEnv* env, ERL_NIF_TERM* reason)</nametext></name> <fsummary>Check if an exception has been raised.</fsummary> <desc> @@ -1588,7 +1588,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name> + <name since="OTP 20.0"> <ret>ErlNifUInt64</ret> <nametext>enif_hash(ErlNifHash type, ERL_NIF_TERM term, ErlNifUInt64 salt)</nametext> </name> @@ -1601,7 +1601,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_inspect_binary(ErlNifEnv* env, + <name since=""><ret>int</ret><nametext>enif_inspect_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, ErlNifBinary* bin)</nametext></name> <fsummary>Inspect the content of a binary.</fsummary> <desc> @@ -1613,7 +1613,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_inspect_iolist_as_binary(ErlNifEnv* + <name since="OTP R13B04"><ret>int</ret><nametext>enif_inspect_iolist_as_binary(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifBinary* bin)</nametext></name> <fsummary>Inspect the content of an iolist.</fsummary> <desc> @@ -1627,7 +1627,7 @@ enif_free_iovec(iovec);]]></code> </func> <func> - <name><ret>int</ret><nametext>enif_inspect_iovec(ErlNifEnv* + <name since="OTP 20.1"><ret>int</ret><nametext>enif_inspect_iovec(ErlNifEnv* env, size_t max_elements, ERL_NIF_TERM iovec_term, ERL_NIF_TERM* tail, ErlNifIOVec** iovec)</nametext></name> <fsummary>Inspect a list of binaries as an ErlNifIOVec.</fsummary> @@ -1667,7 +1667,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ErlNifIOQueue *</ret> + <name since="OTP 20.1"><ret>ErlNifIOQueue *</ret> <nametext>enif_ioq_create(ErlNifIOQueueOpts opts)</nametext></name> <fsummary>Create a new IO Queue</fsummary> <desc> @@ -1678,7 +1678,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>void</ret> + <name since="OTP 20.1"><ret>void</ret> <nametext>enif_ioq_destroy(ErlNifIOQueue *q)</nametext></name> <fsummary>Destroy an IO Queue and free it's content</fsummary> <desc> @@ -1687,7 +1687,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP 20.1"><ret>int</ret> <nametext>enif_ioq_deq(ErlNifIOQueue *q, size_t count, size_t *size)</nametext></name> <fsummary>Dequeue count bytes from the IO Queue</fsummary> <desc> @@ -1700,7 +1700,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP 20.1"><ret>int</ret> <nametext>enif_ioq_enq_binary(ErlNifIOQueue *q, ErlNifBinary *bin, size_t skip)</nametext></name> <fsummary>Enqueue the binary into the IO Queue</fsummary> <desc> @@ -1713,7 +1713,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP 20.1"><ret>int</ret> <nametext>enif_ioq_enqv(ErlNifIOQueue *q, ErlNifIOVec *iovec, size_t skip)</nametext></name> <fsummary>Enqueue the iovec into the IO Queue</fsummary> <desc> @@ -1724,7 +1724,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>SysIOVec *</ret> + <name since="OTP 20.1"><ret>SysIOVec *</ret> <nametext>enif_ioq_peek(ErlNifIOQueue *q, int *iovlen)</nametext></name> <fsummary>Peek inside the IO Queue</fsummary> <desc> @@ -1738,7 +1738,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP 21.0"><ret>int</ret> <nametext>enif_ioq_peek_head(ErlNifEnv *env, ErlNifIOQueue *q, size_t *size, ERL_NIF_TERM *bin_term)</nametext></name> <fsummary>Peek the head of the IO Queue.</fsummary> <desc> @@ -1753,7 +1753,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>size_t</ret> + <name since="OTP 20.1"><ret>size_t</ret> <nametext>enif_ioq_size(ErlNifIOQueue *q)</nametext></name> <fsummary>Get the current size of the IO Queue</fsummary> <desc> @@ -1762,7 +1762,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_is_atom(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> </name> <fsummary>Determine if a term is an atom.</fsummary> @@ -1772,7 +1772,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since=""><ret>int</ret> <nametext>enif_is_binary(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> </name> <fsummary>Determine if a term is a binary.</fsummary> @@ -1782,7 +1782,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP 19.0"><ret>int</ret> <nametext>enif_is_current_process_alive(ErlNifEnv* env)</nametext> </name> <fsummary>Determine if currently executing process is alive.</fsummary> @@ -1795,7 +1795,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_is_empty_list(ErlNifEnv* env, + <name since="OTP R13B04"><ret>int</ret><nametext>enif_is_empty_list(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> <fsummary>Determine if a term is an empty list.</fsummary> <desc> @@ -1804,7 +1804,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_is_exception(ErlNifEnv* env, + <name since="OTP R14B03"><ret>int</ret><nametext>enif_is_exception(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> <fsummary>Determine if a term is an exception.</fsummary> <desc><marker id="enif_is_exception"/> @@ -1813,7 +1813,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_is_fun(ErlNifEnv* env, ERL_NIF_TERM + <name since="OTP R13B04"><ret>int</ret><nametext>enif_is_fun(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> <fsummary>Determine if a term is a fun.</fsummary> <desc> @@ -1822,7 +1822,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_is_identical(ERL_NIF_TERM lhs, + <name since="OTP R13B04"><ret>int</ret><nametext>enif_is_identical(ERL_NIF_TERM lhs, ERL_NIF_TERM rhs)</nametext></name> <fsummary>Erlang operator =:=.</fsummary> <desc> @@ -1832,7 +1832,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP R14B"><ret>int</ret> <nametext>enif_is_list(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> </name> <fsummary>Determine if a term is a list.</fsummary> @@ -1842,7 +1842,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_is_map(ErlNifEnv* env, ERL_NIF_TERM + <name since="OTP 18.0"><ret>int</ret><nametext>enif_is_map(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> <fsummary>Determine if a term is a map.</fsummary> <desc> @@ -1852,7 +1852,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_is_number(ErlNifEnv* env, ERL_NIF_TERM + <name since="OTP R15B"><ret>int</ret><nametext>enif_is_number(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> <fsummary>Determine if a term is a number (integer or float).</fsummary> <desc> @@ -1861,7 +1861,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> </name> <fsummary>Determine if a term is a pid.</fsummary> @@ -1871,7 +1871,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_is_port(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> </name> <fsummary>Determine if a term is a port.</fsummary> @@ -1881,7 +1881,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_is_port_alive(ErlNifEnv* env, + <name since="OTP 19.0"><ret>int</ret><nametext>enif_is_port_alive(ErlNifEnv* env, ErlNifPort *port_id)</nametext></name> <fsummary>Determine if a local port is alive.</fsummary> <desc> @@ -1893,7 +1893,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_is_process_alive(ErlNifEnv* env, + <name since="OTP 19.0"><ret>int</ret><nametext>enif_is_process_alive(ErlNifEnv* env, ErlNifPid *pid)</nametext></name> <fsummary>Determine if a local process is alive.</fsummary> <desc> @@ -1905,7 +1905,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_is_ref(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> </name> <fsummary>Determine if a term is a reference.</fsummary> @@ -1915,7 +1915,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP R14B"><ret>int</ret> <nametext>enif_is_tuple(ErlNifEnv* env, ERL_NIF_TERM term)</nametext> </name> <fsummary>Determine if a term is a tuple.</fsummary> @@ -1925,7 +1925,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP R14B"><ret>int</ret> <nametext>enif_keep_resource(void* obj)</nametext> </name> <fsummary>Add a reference to a resource object.</fsummary> @@ -1941,7 +1941,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since=""><ret>ERL_NIF_TERM</ret> <nametext>enif_make_atom(ErlNifEnv* env, const char* name)</nametext> </name> <fsummary>Create an atom term.</fsummary> @@ -1955,7 +1955,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_atom_len(ErlNifEnv* env, + <name since="OTP R14B"><ret>ERL_NIF_TERM</ret><nametext>enif_make_atom_len(ErlNifEnv* env, const char* name, size_t len)</nametext></name> <fsummary>Create an atom term.</fsummary> <desc> @@ -1969,7 +1969,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since=""><ret>ERL_NIF_TERM</ret> <nametext>enif_make_badarg(ErlNifEnv* env)</nametext></name> <fsummary>Make a badarg exception.</fsummary> <desc> @@ -1997,7 +1997,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since=""><ret>ERL_NIF_TERM</ret> <nametext>enif_make_binary(ErlNifEnv* env, ErlNifBinary* bin)</nametext> </name> <fsummary>Make a binary term.</fsummary> @@ -2010,7 +2010,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_copy(ErlNifEnv* dst_env, + <name since="OTP R14B"><ret>ERL_NIF_TERM</ret><nametext>enif_make_copy(ErlNifEnv* dst_env, ERL_NIF_TERM src_term)</nametext></name> <fsummary>Make a copy of a term.</fsummary> <desc> @@ -2021,7 +2021,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_double(ErlNifEnv* env, double d)</nametext></name> <fsummary>Create a floating-point term.</fsummary> <desc> @@ -2033,7 +2033,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_make_existing_atom(ErlNifEnv* env, + <name since="OTP R13B04"><ret>int</ret><nametext>enif_make_existing_atom(ErlNifEnv* env, const char* name, ERL_NIF_TERM* atom, ErlNifCharEncoding encode)</nametext></name> <fsummary>Create an existing atom term.</fsummary> @@ -2049,7 +2049,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_make_existing_atom_len(ErlNifEnv* env, + <name since="OTP R14B"><ret>int</ret><nametext>enif_make_existing_atom_len(ErlNifEnv* env, const char* name, size_t len, ERL_NIF_TERM* atom, ErlNifCharEncoding encoding)</nametext></name> <fsummary>Create an existing atom term.</fsummary> @@ -2065,7 +2065,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </desc> </func> - <func><name><ret>ERL_NIF_TERM</ret> + <func><name since=""><ret>ERL_NIF_TERM</ret> <nametext>enif_make_int(ErlNifEnv* env, int i)</nametext></name> <fsummary>Create an integer term.</fsummary> <desc> @@ -2074,7 +2074,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R14B"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_int64(ErlNifEnv* env, ErlNifSInt64 i)</nametext> </name> <fsummary>Create an integer term.</fsummary> @@ -2084,7 +2084,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since=""><ret>ERL_NIF_TERM</ret> <nametext>enif_make_list(ErlNifEnv* env, unsigned cnt, ...)</nametext> </name> <fsummary>Create a list term.</fsummary> @@ -2097,24 +2097,24 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_list1(ErlNifEnv* env, ERL_NIF_TERM e1)</nametext> </name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list2(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_list2(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list3(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_list3(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list4(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_list4(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list5(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_list5(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list6(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_list6(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list7(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_list7(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list8(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_list8(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list9(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_list9(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)</nametext></name> <fsummary>Create a list term.</fsummary> <desc> @@ -2126,7 +2126,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_list_cell(ErlNifEnv* + <name since=""><ret>ERL_NIF_TERM</ret><nametext>enif_make_list_cell(ErlNifEnv* env, ERL_NIF_TERM head, ERL_NIF_TERM tail)</nametext></name> <fsummary>Create a list cell.</fsummary> <desc> @@ -2135,7 +2135,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_list_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt)</nametext></name> <fsummary>Create a list term from an array.</fsummary> @@ -2147,7 +2147,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_long(ErlNifEnv* env, long int i)</nametext></name> <fsummary>Create an integer term from a long int.</fsummary> <desc> @@ -2156,7 +2156,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_make_map_put(ErlNifEnv* env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_make_map_put(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM value, ERL_NIF_TERM* map_out)</nametext></name> <fsummary>Insert key-value pair in map.</fsummary> @@ -2172,7 +2172,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_make_map_remove(ErlNifEnv* env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_make_map_remove(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM* map_out)</nametext> </name> <fsummary>Remove key from map.</fsummary> @@ -2188,7 +2188,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_make_map_update(ErlNifEnv* env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_make_map_update(ErlNifEnv* env, ERL_NIF_TERM map_in, ERL_NIF_TERM key, ERL_NIF_TERM new_value, ERL_NIF_TERM* map_out)</nametext></name> <fsummary>Replace value for key in map.</fsummary> @@ -2203,7 +2203,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP 21.0"><ret>int</ret> <nametext>enif_make_map_from_arrays(ErlNifEnv* env, ERL_NIF_TERM keys[], ERL_NIF_TERM values[], size_t cnt, ERL_NIF_TERM *map_out)</nametext></name> <fsummary>Make map term from the given keys and values.</fsummary> @@ -2217,7 +2217,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>unsigned char *</ret><nametext>enif_make_new_binary(ErlNifEnv* + <name since="OTP R14B"><ret>unsigned char *</ret><nametext>enif_make_new_binary(ErlNifEnv* env, size_t size, ERL_NIF_TERM* termp)</nametext></name> <fsummary>Allocate and create a new binary term.</fsummary> <desc> @@ -2233,7 +2233,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP 18.0"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_new_map(ErlNifEnv* env)</nametext></name> <fsummary>Make an empty map term.</fsummary> <desc> @@ -2242,7 +2242,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R14B"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_pid(ErlNifEnv* env, const ErlNifPid* pid)</nametext> </name> <fsummary>Make a pid term.</fsummary> @@ -2252,7 +2252,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_ref(ErlNifEnv* env)</nametext></name> <fsummary>Create a reference.</fsummary> <desc> @@ -2262,7 +2262,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_resource(ErlNifEnv* env, void* obj)</nametext> </name> <fsummary>Create an opaque handle to a resource object.</fsummary> @@ -2285,7 +2285,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); between nodes.</p> <list type="bulleted"> <item> - <p>Two resource terms will compare equal iff they + <p>Two resource terms will compare equal if and only if they would yield the same resource object pointer when passed to <seealso marker="#enif_get_resource"><c>enif_get_resource</c></seealso>.</p> </item> @@ -2310,7 +2310,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R14B"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_resource_binary(ErlNifEnv* env, void* obj, const void* data, size_t size)</nametext></name> <fsummary>Create a custom binary term.</fsummary> @@ -2336,7 +2336,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret> + <name since="OTP R15B"><ret>int</ret> <nametext>enif_make_reverse_list(ErlNifEnv* env, ERL_NIF_TERM list_in, ERL_NIF_TERM *list_out)</nametext></name> <fsummary>Create the reverse of a list.</fsummary> @@ -2352,7 +2352,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_string(ErlNifEnv* env, + <name since=""><ret>ERL_NIF_TERM</ret><nametext>enif_make_string(ErlNifEnv* env, const char* string, ErlNifCharEncoding encoding)</nametext></name> <fsummary>Create a string.</fsummary> <desc> @@ -2363,7 +2363,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_string_len(ErlNifEnv* + <name since="OTP R14B"><ret>ERL_NIF_TERM</ret><nametext>enif_make_string_len(ErlNifEnv* env, const char* string, size_t len, ErlNifCharEncoding encoding)</nametext></name> <fsummary>Create a string.</fsummary> @@ -2376,7 +2376,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_sub_binary(ErlNifEnv* + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_sub_binary(ErlNifEnv* env, ERL_NIF_TERM bin_term, size_t pos, size_t size)</nametext></name> <fsummary>Make a subbinary term.</fsummary> <desc> @@ -2388,7 +2388,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple(ErlNifEnv* env, + <name since=""><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple(ErlNifEnv* env, unsigned cnt, ...)</nametext></name> <fsummary>Creates a tuple term.</fsummary> <desc> @@ -2399,23 +2399,23 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple1(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple1(ErlNifEnv* env, ERL_NIF_TERM e1)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple2(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple2(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple3(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple3(ErlNifEnv* env, ERL_NIF_TERM e1, ERL_NIF_TERM e2, ERL_NIF_TERM e3)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple4(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple4(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e4)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple5(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple5(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e5)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple6(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple6(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e6)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple7(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple7(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e7)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple8(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple8(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e8)</nametext></name> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple9(ErlNifEnv* env, + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret><nametext>enif_make_tuple9(ErlNifEnv* env, ERL_NIF_TERM e1, ..., ERL_NIF_TERM e9)</nametext></name> <fsummary>Create a tuple term.</fsummary> <desc> @@ -2427,7 +2427,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_tuple_from_array(ErlNifEnv* env, const ERL_NIF_TERM arr[], unsigned cnt)</nametext></name> <fsummary>Create a tuple term from an array.</fsummary> @@ -2438,7 +2438,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R13B04"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_uint(ErlNifEnv* env, unsigned int i)</nametext> </name> <fsummary>Create an unsigned integer term.</fsummary> @@ -2448,7 +2448,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP R14B"><ret>ERL_NIF_TERM</ret> <nametext>enif_make_uint64(ErlNifEnv* env, ErlNifUInt64 i)</nametext> </name> <fsummary>Create an unsigned integer term.</fsummary> @@ -2458,7 +2458,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since=""><ret>ERL_NIF_TERM</ret> <nametext>enif_make_ulong(ErlNifEnv* env, unsigned long i)</nametext> </name> <fsummary>Create an integer term from an unsigned long int.</fsummary> @@ -2468,7 +2468,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_make_unique_integer(ErlNifEnv + <name since="OTP 19.0"><ret>ERL_NIF_TERM</ret><nametext>enif_make_unique_integer(ErlNifEnv *env, ErlNifUniqueInteger properties)</nametext></name> <fsummary></fsummary> <desc> @@ -2486,7 +2486,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> - <name><ret>int</ret><nametext>enif_map_iterator_create(ErlNifEnv *env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_map_iterator_create(ErlNifEnv *env, ERL_NIF_TERM map, ErlNifMapIterator *iter, ErlNifMapIteratorEntry entry)</nametext></name> <fsummary>Create a map iterator.</fsummary> @@ -2521,7 +2521,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret><nametext>enif_map_iterator_destroy(ErlNifEnv *env, + <name since="OTP 18.0"><ret>void</ret><nametext>enif_map_iterator_destroy(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name> <fsummary>Destroy a map iterator.</fsummary> <desc> @@ -2532,7 +2532,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret><nametext>enif_map_iterator_get_pair(ErlNifEnv *env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_map_iterator_get_pair(ErlNifEnv *env, ErlNifMapIterator *iter, ERL_NIF_TERM *key, ERL_NIF_TERM *value)</nametext></name> <fsummary>Get key and value at current map iterator position.</fsummary> @@ -2545,7 +2545,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret><nametext>enif_map_iterator_is_head(ErlNifEnv *env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_map_iterator_is_head(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name> <fsummary>Check if map iterator is positioned before first.</fsummary> <desc> @@ -2555,7 +2555,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret><nametext>enif_map_iterator_is_tail(ErlNifEnv *env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_map_iterator_is_tail(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name> <fsummary>Check if map iterator is positioned after last.</fsummary> <desc> @@ -2565,7 +2565,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret><nametext>enif_map_iterator_next(ErlNifEnv *env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_map_iterator_next(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name> <fsummary>Increment map iterator to point to next entry.</fsummary> <desc> @@ -2577,7 +2577,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret><nametext>enif_map_iterator_prev(ErlNifEnv *env, + <name since="OTP 18.0"><ret>int</ret><nametext>enif_map_iterator_prev(ErlNifEnv *env, ErlNifMapIterator *iter)</nametext></name> <fsummary>Decrement map iterator to point to previous entry.</fsummary> <desc> @@ -2589,8 +2589,8 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret><nametext>enif_monitor_process(ErlNifEnv* caller_env, void* obj, - const ErlNifPid* target_pid, ErlNifMonitor* mon)</nametext></name> + <name since="OTP 20.0"><ret>int</ret><nametext>enif_monitor_process(ErlNifEnv* caller_env, + void* obj, const ErlNifPid* target_pid, ErlNifMonitor* mon)</nametext></name> <fsummary>Monitor a process from a resource.</fsummary> <desc> <marker id="enif_monitor_process"></marker> @@ -2621,7 +2621,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>ErlNifTime</ret> + <name since="OTP 18.3"><ret>ErlNifTime</ret> <nametext>enif_monotonic_time(ErlNifTimeUnit time_unit)</nametext> </name> <fsummary>Get Erlang monotonic time.</fsummary> @@ -2642,7 +2642,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>ErlNifMutex *</ret> + <name since="OTP R13B04"><ret>ErlNifMutex *</ret> <nametext>enif_mutex_create(char *name)</nametext></name> <fsummary></fsummary> <desc> @@ -2652,7 +2652,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_mutex_destroy(ErlNifMutex *mtx)</nametext></name> <fsummary></fsummary> <desc> @@ -2662,7 +2662,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_mutex_lock(ErlNifMutex *mtx)</nametext></name> <fsummary></fsummary> <desc> @@ -2672,7 +2672,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>char*</ret> + <name since="OTP 21.0"><ret>char*</ret> <nametext>enif_mutex_name(ErlNifMutex* mtx)</nametext></name> <fsummary></fsummary> <desc> @@ -2682,7 +2682,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_mutex_trylock(ErlNifMutex *mtx)</nametext></name> <fsummary></fsummary> <desc> @@ -2692,7 +2692,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_mutex_unlock(ErlNifMutex *mtx)</nametext></name> <fsummary></fsummary> <desc> @@ -2702,7 +2702,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>ERL_NIF_TERM</ret> + <name since="OTP 19.0"><ret>ERL_NIF_TERM</ret> <nametext>enif_now_time(ErlNifEnv *env)</nametext></name> <fsummary></fsummary> <desc> @@ -2713,7 +2713,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>ErlNifResourceType *</ret> + <name since="OTP R13B04"><ret>ErlNifResourceType *</ret> <nametext>enif_open_resource_type(ErlNifEnv* env, const char* module_str, const char* name, ErlNifResourceDtor* dtor, ErlNifResourceFlags flags, ErlNifResourceFlags* tried)</nametext> @@ -2752,7 +2752,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>ErlNifResourceType *</ret> + <name since="OTP 20.0"><ret>ErlNifResourceType *</ret> <nametext>enif_open_resource_type_x(ErlNifEnv* env, const char* name, const ErlNifResourceTypeInit* init, ErlNifResourceFlags flags, ErlNifResourceFlags* tried)</nametext> @@ -2771,7 +2771,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret><nametext>enif_port_command(ErlNifEnv* env, const + <name since="OTP 19.0"><ret>int</ret><nametext>enif_port_command(ErlNifEnv* env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg)</nametext> </name> <fsummary>Send a port_command to to_port.</fsummary> @@ -2814,7 +2814,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void *</ret> + <name since="OTP R13B04"><ret>void *</ret> <nametext>enif_priv_data(ErlNifEnv* env)</nametext></name> <fsummary>Get the private data of a NIF library.</fsummary> <desc> @@ -2825,7 +2825,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_raise_exception(ErlNifEnv* + <name since="OTP 18.0"><ret>ERL_NIF_TERM</ret><nametext>enif_raise_exception(ErlNifEnv* env, ERL_NIF_TERM reason)</nametext></name> <fsummary>Raise a NIF error exception.</fsummary> <desc> @@ -2848,7 +2848,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void *</ret> + <name since="OTP 20.2"><ret>void *</ret> <nametext>enif_realloc(void* ptr, size_t size)</nametext></name> <fsummary>Reallocate dynamic memory.</fsummary> <desc> @@ -2862,7 +2862,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_realloc_binary(ErlNifBinary* bin, size_t size)</nametext> </name> <fsummary>Change the size of a binary.</fsummary> @@ -2876,7 +2876,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since=""><ret>void</ret> <nametext>enif_release_binary(ErlNifBinary* bin)</nametext></name> <fsummary>Release a binary.</fsummary> <desc> @@ -2887,7 +2887,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_release_resource(void* obj)</nametext></name> <fsummary>Release a resource object.</fsummary> <desc> @@ -2906,7 +2906,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>ErlNifRWLock *</ret> + <name since="OTP R13B04"><ret>ErlNifRWLock *</ret> <nametext>enif_rwlock_create(char *name)</nametext></name> <fsummary></fsummary> <desc> @@ -2916,7 +2916,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_rwlock_destroy(ErlNifRWLock *rwlck)</nametext></name> <fsummary></fsummary> <desc> @@ -2926,7 +2926,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>char*</ret> + <name since="OTP 21.0"><ret>char*</ret> <nametext>enif_rwlock_name(ErlNifRWLock* rwlck)</nametext></name> <fsummary></fsummary> <desc> @@ -2936,7 +2936,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_rwlock_rlock(ErlNifRWLock *rwlck)</nametext></name> <fsummary></fsummary> <desc> @@ -2946,7 +2946,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_rwlock_runlock(ErlNifRWLock *rwlck)</nametext></name> <fsummary></fsummary> <desc> @@ -2956,7 +2956,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_rwlock_rwlock(ErlNifRWLock *rwlck)</nametext></name> <fsummary></fsummary> <desc> @@ -2966,7 +2966,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_rwlock_rwunlock(ErlNifRWLock *rwlck)</nametext></name> <fsummary></fsummary> <desc> @@ -2976,7 +2976,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_rwlock_tryrlock(ErlNifRWLock *rwlck)</nametext></name> <fsummary></fsummary> <desc> @@ -2986,7 +2986,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_rwlock_tryrwlock(ErlNifRWLock *rwlck)</nametext></name> <fsummary></fsummary> <desc> @@ -2996,7 +2996,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>ERL_NIF_TERM</ret><nametext>enif_schedule_nif(ErlNifEnv* env, + <name since="OTP 17.3"><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> @@ -3041,7 +3041,7 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> - <name><ret>int</ret> + <name since="OTP 20.0"><ret>int</ret> <nametext>enif_select(ErlNifEnv* env, ErlNifEvent event, enum ErlNifSelectFlags mode, void* obj, const ErlNifPid* pid, ERL_NIF_TERM ref)</nametext> </name> @@ -3063,6 +3063,11 @@ enif_map_iterator_destroy(env, &iter);</code> <code type="none">{select, Obj, Ref, ready_input | ready_output}</code> <p><c>ready_input</c> or <c>ready_output</c> indicates if the event object is ready for reading or writing.</p> + <note><p>For complete control over the message format use the newer functions + <seealso marker="#enif_select_read"><c>enif_select_read</c></seealso> or + <seealso marker="#enif_select_write"><c>enif_select_write</c></seealso> + introduced in erts-11.0 (OTP-22.0).</p> + </note> <p>Argument <c>pid</c> may be <c>NULL</c> to indicate the calling process.</p> <p>Argument <c>obj</c> is a resource object obtained from <seealso marker="#enif_alloc_resource"><c>enif_alloc_resource</c></seealso>. @@ -3157,7 +3162,35 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>ErlNifPid *</ret> + <name since="OTP 22.0"><ret>int</ret> + <nametext>enif_select_read(ErlNifEnv* env, ErlNifEvent event, void* obj, + const ErlNifPid* pid, ERL_NIF_TERM msg, ErlNifEnv* msg_env)</nametext> + </name> + <name since="OTP 22.0"><ret>int</ret> + <nametext>enif_select_write(ErlNifEnv* env, ErlNifEvent event, void* obj, + const ErlNifPid* pid, ERL_NIF_TERM msg, ErlNifEnv* msg_env)</nametext> + </name> + <fsummary>Manage subscription on IO event.</fsummary> + <desc> + <p>These are variants of <seealso marker="#enif_select">enif_select</seealso> + where you can supply your own message term <c>msg</c> that will be sent to + the process instead of the predefined tuple <c>{select,_,_,_}.</c></p> + <p>Argument <c>msg_env</c> must either be <c>NULL</c> or the environment of + <c>msg</c> allocated with <seealso marker="#enif_alloc_env"> + <c>enif_alloc_env</c></seealso>. If argument <c>msg_env</c> is + <c>NULL</c> the term <c>msg</c> will be copied, otherwise both + <c>msg</c> and <c>msg_env</c> will be invalidated by a successful call + to <c>enif_select_read</c> or <c>enif_select_write</c>.</p> + <p>Apart from the message format <c>enif_select_read</c> and + <c>enif_select_write</c> behaves exactly the same as <seealso + marker="#enif_select">enif_select</seealso> with argument <c>mode</c> as + either <c>ERL_NIF_SELECT_READ</c> or <c>ERL_NIF_SELECT_WRITE</c>. To + cancel or close events use <seealso marker="#enif_select">enif_select</seealso>.</p> + </desc> + </func> + + <func> + <name since="OTP R14B"><ret>ErlNifPid *</ret> <nametext>enif_self(ErlNifEnv* caller_env, ErlNifPid* pid)</nametext> </name> <fsummary>Get the pid of the calling process.</fsummary> @@ -3170,8 +3203,8 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret><nametext>enif_send(ErlNifEnv* caller_env, ErlNifPid* to_pid, - ErlNifEnv* msg_env, ERL_NIF_TERM msg)</nametext></name> + <name since="OTP R14B"><ret>int</ret><nametext>enif_send(ErlNifEnv* caller_env, + ErlNifPid* to_pid, ErlNifEnv* msg_env, ERL_NIF_TERM msg)</nametext></name> <fsummary>Send a message to a process.</fsummary> <desc> <p>Sends a message to a process.</p> @@ -3217,7 +3250,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>unsigned</ret> + <name since="OTP R13B04"><ret>unsigned</ret> <nametext>enif_sizeof_resource(void* obj)</nametext></name> <fsummary>Get the byte size of a resource object.</fsummary> <desc> @@ -3228,7 +3261,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret><nametext>enif_snprintf(char *str, size_t size, const + <name since="OTP 19.0"><ret>int</ret><nametext>enif_snprintf(char *str, size_t size, const char *format, ...)</nametext></name> <fsummary>Format strings and Erlang terms.</fsummary> <desc> @@ -3242,7 +3275,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>void</ret><nametext>enif_system_info(ErlNifSysInfo + <name since="OTP R13B04"><ret>void</ret><nametext>enif_system_info(ErlNifSysInfo *sys_info_ptr, size_t size)</nametext></name> <fsummary>Get information about the Erlang runtime system.</fsummary> <desc> @@ -3252,7 +3285,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret><nametext>enif_term_to_binary(ErlNifEnv *env, + <name since="OTP 19.0"><ret>int</ret><nametext>enif_term_to_binary(ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin)</nametext></name> <fsummary>Convert a term to the external format.</fsummary> <desc> @@ -3269,7 +3302,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_thread_create(char *name,ErlNifTid *tid,void * (*func)(void *),void *args,ErlNifThreadOpts *opts)</nametext></name> @@ -3281,7 +3314,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_thread_exit(void *resp)</nametext></name> <fsummary></fsummary> <desc> @@ -3291,7 +3324,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_thread_join(ErlNifTid, void **respp)</nametext></name> <fsummary></fsummary> <desc> @@ -3301,7 +3334,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>char*</ret> + <name since="OTP 21.0"><ret>char*</ret> <nametext>enif_thread_name(ErlNifTid tid)</nametext></name> <fsummary>Thread name</fsummary> <desc> @@ -3311,7 +3344,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>ErlNifThreadOpts *</ret> + <name since="OTP R13B04"><ret>ErlNifThreadOpts *</ret> <nametext>enif_thread_opts_create(char *name)</nametext></name> <fsummary></fsummary> <desc> @@ -3321,7 +3354,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_thread_opts_destroy(ErlNifThreadOpts *opts)</nametext> </name> <fsummary></fsummary> @@ -3332,7 +3365,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>ErlNifTid</ret> + <name since="OTP R13B04"><ret>ErlNifTid</ret> <nametext>enif_thread_self(void)</nametext></name> <fsummary></fsummary> <desc> @@ -3342,7 +3375,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret> + <name since="OTP 19.0"><ret>int</ret> <nametext>enif_thread_type(void)</nametext></name> <fsummary>Determine type of current thread</fsummary> <desc> @@ -3364,7 +3397,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>ErlNifTime</ret> + <name since="OTP 18.3"><ret>ErlNifTime</ret> <nametext>enif_time_offset(ErlNifTimeUnit time_unit)</nametext></name> <fsummary>Get current time offset.</fsummary> <desc> @@ -3386,7 +3419,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>void *</ret> + <name since="OTP R13B04"><ret>void *</ret> <nametext>enif_tsd_get(ErlNifTSDKey key)</nametext></name> <fsummary></fsummary> <desc> @@ -3396,7 +3429,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret> + <name since="OTP R13B04"><ret>int</ret> <nametext>enif_tsd_key_create(char *name, ErlNifTSDKey *key)</nametext> </name> <fsummary></fsummary> @@ -3407,7 +3440,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_tsd_key_destroy(ErlNifTSDKey key)</nametext></name> <fsummary></fsummary> <desc> @@ -3417,7 +3450,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>void</ret> + <name since="OTP R13B04"><ret>void</ret> <nametext>enif_tsd_set(ErlNifTSDKey key, void *data)</nametext></name> <fsummary></fsummary> <desc> @@ -3427,7 +3460,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret> + <name since="OTP 21.0"><ret>int</ret> <nametext>enif_vfprintf(FILE *stream, const char *format, va_list ap) </nametext></name> <fsummary>Format strings and Erlang terms.</fsummary> @@ -3439,7 +3472,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret> + <name since="OTP 21.0"><ret>int</ret> <nametext>enif_vsnprintf(char *str, size_t size, const char *format, va_list ap) </nametext></name> <fsummary>Format strings and Erlang terms.</fsummary> @@ -3451,7 +3484,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret> + <name since="OTP 20.0"><ret>int</ret> <nametext>enif_whereis_pid(ErlNifEnv *env, ERL_NIF_TERM name, ErlNifPid *pid)</nametext></name> <fsummary>Looks up a process by its registered name.</fsummary> @@ -3479,7 +3512,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> - <name><ret>int</ret> + <name since="OTP 20.0"><ret>int</ret> <nametext>enif_whereis_port(ErlNifEnv *env, ERL_NIF_TERM name, ErlNifPort *port)</nametext></name> <fsummary>Looks up a port by its registered name.</fsummary> diff --git a/erts/doc/src/erl_prim_loader.xml b/erts/doc/src/erl_prim_loader.xml index 286bac6c93..043d11b7b4 100644 --- a/erts/doc/src/erl_prim_loader.xml +++ b/erts/doc/src/erl_prim_loader.xml @@ -29,7 +29,7 @@ <rev></rev> <file>erl_prim_loader.xml</file> </header> - <module>erl_prim_loader</module> + <module since="">erl_prim_loader</module> <modulesummary>Low-level Erlang loader.</modulesummary> <description> <p>This module is used to load all Erlang modules into @@ -47,7 +47,7 @@ <funcs> <func> - <name name="get_file" arity="1"/> + <name name="get_file" arity="1" since=""/> <fsummary>Get a file.</fsummary> <desc> <p>Fetches a file using the low-level loader. @@ -65,7 +65,7 @@ </func> <func> - <name name="get_path" arity="0"/> + <name name="get_path" arity="0" since=""/> <fsummary>Get the path set in the loader.</fsummary> <desc> <p>Gets the path set in the loader. The path is @@ -75,7 +75,7 @@ </func> <func> - <name name="list_dir" arity="1"/> + <name name="list_dir" arity="1" since=""/> <fsummary>List files in a directory.</fsummary> <desc> <p>Lists all the files in a directory. Returns @@ -92,7 +92,7 @@ </func> <func> - <name name="read_file_info" arity="1"/> + <name name="read_file_info" arity="1" since=""/> <fsummary>Get information about a file.</fsummary> <desc> <p>Retrieves information about a file. Returns @@ -114,7 +114,7 @@ </func> <func> - <name name="read_link_info" arity="1"/> + <name name="read_link_info" arity="1" since="OTP 17.1.2"/> <fsummary>Get information about a link or file.</fsummary> <desc> <p>Works like @@ -131,7 +131,7 @@ </func> <func> - <name name="set_path" arity="1"/> + <name name="set_path" arity="1" since=""/> <fsummary>Set the path of the loader.</fsummary> <desc> <p>Sets the path of the loader if diff --git a/erts/doc/src/erl_tracer.xml b/erts/doc/src/erl_tracer.xml index fd3c17f337..fa4717bc2f 100644 --- a/erts/doc/src/erl_tracer.xml +++ b/erts/doc/src/erl_tracer.xml @@ -28,7 +28,7 @@ <date></date> <rev></rev> </header> - <module>erl_tracer</module> + <module since="OTP 19.0">erl_tracer</module> <modulesummary>Erlang tracer behavior.</modulesummary> <description> <p>This behavior module implements the back end of the Erlang @@ -195,7 +195,7 @@ <funcs> <func> - <name>Module:enabled(TraceTag, TracerState, Tracee) -> Result</name> + <name since="OTP 19.0">Module:enabled(TraceTag, TracerState, Tracee) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> <v>TraceTag = <seealso marker="#type-trace_tag"> @@ -224,7 +224,7 @@ </func> <func> - <name>Module:enabled_call(TraceTag, TracerState, Tracee) -> Result</name> + <name since="OTP 19.0">Module:enabled_call(TraceTag, TracerState, Tracee) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> <v>TraceTag = <seealso marker="#type-trace_tag_call"> @@ -244,7 +244,7 @@ </func> <func> - <name>Module:enabled_garbage_collection(TraceTag, TracerState, Tracee) -> Result</name> + <name since="OTP 19.0">Module:enabled_garbage_collection(TraceTag, TracerState, Tracee) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> <v>TraceTag = <seealso marker="#type-trace_tag_gc"> @@ -264,7 +264,7 @@ </func> <func> - <name>Module:enabled_ports(TraceTag, TracerState, Tracee) -> Result</name> + <name since="OTP 19.0">Module:enabled_ports(TraceTag, TracerState, Tracee) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> <v>TraceTag = <seealso marker="#type-trace_tag_ports"> @@ -284,7 +284,7 @@ </func> <func> - <name>Module:enabled_procs(TraceTag, TracerState, Tracee) -> Result</name> + <name since="OTP 19.0">Module:enabled_procs(TraceTag, TracerState, Tracee) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> <v>TraceTag = <seealso marker="#type-trace_tag_procs"> @@ -304,7 +304,7 @@ </func> <func> - <name>Module:enabled_receive(TraceTag, TracerState, Tracee) -> Result + <name since="OTP 19.0">Module:enabled_receive(TraceTag, TracerState, Tracee) -> Result </name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -325,7 +325,7 @@ </func> <func> - <name>Module:enabled_running_ports(TraceTag, TracerState, Tracee) -> + <name since="OTP 19.0">Module:enabled_running_ports(TraceTag, TracerState, Tracee) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -346,7 +346,7 @@ </func> <func> - <name>Module:enabled_running_procs(TraceTag, TracerState, Tracee) -> + <name since="OTP 19.0">Module:enabled_running_procs(TraceTag, TracerState, Tracee) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -368,7 +368,7 @@ </func> <func> - <name>Module:enabled_send(TraceTag, TracerState, Tracee) -> Result</name> + <name since="OTP 19.0">Module:enabled_send(TraceTag, TracerState, Tracee) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> <v>TraceTag = <seealso marker="#type-trace_tag_send"> @@ -388,7 +388,7 @@ </func> <func> - <name>Module:trace(TraceTag, TracerState, Tracee, TraceTerm, + <name since="OTP 19.0">Module:trace(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -417,7 +417,7 @@ </func> <func> - <name name="trace">Module:trace(seq_trace, TracerState, Label, + <name name="trace" since="OTP 19.0">Module:trace(seq_trace, TracerState, Label, SeqTraceInfo, Opts) -> Result</name> <fsummary>Check if a sequence trace event is to be generated.</fsummary> <type> @@ -439,7 +439,7 @@ </func> <func> - <name>Module:trace_call(TraceTag, TracerState, Tracee, TraceTerm, + <name since="OTP 19.0">Module:trace_call(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -463,7 +463,7 @@ </func> <func> - <name>Module:trace_garbage_collection(TraceTag, TracerState, Tracee, + <name since="OTP 19.0">Module:trace_garbage_collection(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -487,7 +487,7 @@ </func> <func> - <name>Module:trace_ports(TraceTag, TracerState, Tracee, TraceTerm, + <name since="OTP 19.0">Module:trace_ports(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -511,7 +511,7 @@ </func> <func> - <name>Module:trace_procs(TraceTag, TracerState, Tracee, TraceTerm, + <name since="OTP 19.0">Module:trace_procs(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -535,7 +535,7 @@ </func> <func> - <name>Module:trace_receive(TraceTag, TracerState, Tracee, TraceTerm, + <name since="OTP 19.0">Module:trace_receive(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -559,7 +559,7 @@ </func> <func> - <name>Module:trace_running_ports(TraceTag, TracerState, Tracee, + <name since="OTP 19.0">Module:trace_running_ports(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -583,7 +583,7 @@ </func> <func> - <name>Module:trace_running_procs(TraceTag, TracerState, Tracee, + <name since="OTP 19.0">Module:trace_running_procs(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> @@ -607,7 +607,7 @@ </func> <func> - <name>Module:trace_send(TraceTag, TracerState, Tracee, TraceTerm, + <name since="OTP 19.0">Module:trace_send(TraceTag, TracerState, Tracee, TraceTerm, Opts) -> Result</name> <fsummary>Check if a trace event is to be generated.</fsummary> <type> diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index bd33e35603..d30583be4b 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -29,7 +29,7 @@ <rev></rev> <file>erlang.xml</file> </header> - <module>erlang</module> + <module since="">erlang</module> <modulesummary>The Erlang BIFs.</modulesummary> <description> <p>By convention, most Built-In Functions (BIFs) are included @@ -210,8 +210,8 @@ <funcs> <func> - <name name="abs" arity="1" clause_i="1"/> - <name name="abs" arity="1" clause_i="2"/> + <name name="abs" arity="1" clause_i="1" since=""/> + <name name="abs" arity="1" clause_i="2" since=""/> <fsummary>Arithmetical absolute value.</fsummary> <desc> <p>Returns an integer or float that is the arithmetical @@ -227,7 +227,7 @@ </func> <func> - <name name="adler32" arity="1"/> + <name name="adler32" arity="1" since=""/> <fsummary>Compute adler32 checksum.</fsummary> <desc> <p>Computes and returns the adler32 checksum for @@ -236,7 +236,7 @@ </func> <func> - <name name="adler32" arity="2"/> + <name name="adler32" arity="2" since=""/> <fsummary>Compute adler32 checksum.</fsummary> <desc> <p>Continues computing the adler32 checksum by combining @@ -253,7 +253,7 @@ Y = erlang:adler32([Data1,Data2]).</code> </func> <func> - <name name="adler32_combine" arity="3"/> + <name name="adler32_combine" arity="3" since=""/> <fsummary>Combine two adler32 checksums.</fsummary> <desc> <p>Combines two previously computed adler32 checksums. @@ -272,7 +272,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="append_element" arity="2"/> + <name name="append_element" arity="2" since=""/> <fsummary>Append an extra element to a tuple.</fsummary> <desc> <p>Returns a new tuple that has one element more than @@ -289,7 +289,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="apply" arity="2"/> + <name name="apply" arity="2" since=""/> <fsummary>Apply a function to an argument list.</fsummary> <desc> <p>Calls a fun, passing the elements in <c><anno>Args</anno></c> @@ -307,7 +307,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="apply" arity="3"/> + <name name="apply" arity="3" since=""/> <fsummary>Apply a function to an argument list.</fsummary> <desc> <p>Returns the result of applying <c>Function</c> in @@ -337,7 +337,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="atom_to_binary" arity="2"/> + <name name="atom_to_binary" arity="2" since=""/> <fsummary>Return the binary representation of an atom.</fsummary> <desc> <p>Returns a binary corresponding to the text @@ -362,7 +362,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="atom_to_list" arity="1"/> + <name name="atom_to_list" arity="1" since=""/> <fsummary>Text representation of an atom.</fsummary> <desc> <p>Returns a string corresponding to the text @@ -374,7 +374,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_part" arity="2"/> + <name name="binary_part" arity="2" since="OTP R14B"/> <fsummary>Extract a part of a binary.</fsummary> <desc> <p>Extracts the part of the binary described by @@ -399,7 +399,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_part" arity="3"/> + <name name="binary_part" arity="3" since="OTP R14B"/> <fsummary>Extract a part of a binary.</fsummary> <desc> <p>The same as <c>binary_part(<anno>Subject</anno>, @@ -409,7 +409,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_to_atom" arity="2"/> + <name name="binary_to_atom" arity="2" since=""/> <fsummary>Convert from text representation to an atom.</fsummary> <desc> <p>Returns the atom whose text representation is @@ -438,7 +438,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_to_existing_atom" arity="2"/> + <name name="binary_to_existing_atom" arity="2" since=""/> <fsummary>Convert from text representation to an atom.</fsummary> <desc> <p>As @@ -459,7 +459,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_to_float" arity="1"/> + <name name="binary_to_float" arity="1" since="OTP R16B"/> <fsummary>Convert from text representation to a float.</fsummary> <desc> <p>Returns the float whose text representation is @@ -473,7 +473,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_to_integer" arity="1"/> + <name name="binary_to_integer" arity="1" since="OTP R16B"/> <fsummary>Convert from text representation to an integer.</fsummary> <desc> <p>Returns an integer whose text representation is @@ -487,7 +487,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_to_integer" arity="2"/> + <name name="binary_to_integer" arity="2" since="OTP R16B"/> <fsummary>Convert from text representation to an integer.</fsummary> <desc> <p>Returns an integer whose text representation in base @@ -502,7 +502,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_to_list" arity="1"/> + <name name="binary_to_list" arity="1" since=""/> <fsummary>Convert a binary to a list.</fsummary> <desc> <p>Returns a list of integers corresponding to the bytes of @@ -511,7 +511,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_to_list" arity="3"/> + <name name="binary_to_list" arity="3" since=""/> <fsummary>Convert part of a binary to a list.</fsummary> <type_desc variable="Start">1..byte_size(<c><anno>Binary</anno></c>) </type_desc> @@ -533,7 +533,7 @@ Z = erlang:adler32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="binary_to_term" arity="1"/> + <name name="binary_to_term" arity="1" since=""/> <fsummary>Decode an Erlang external term format binary.</fsummary> <desc> <p>Returns an Erlang term that is the result of decoding @@ -559,7 +559,7 @@ hello </func> <func> - <name name="binary_to_term" arity="2"/> + <name name="binary_to_term" arity="2" since="OTP R13B04"/> <fsummary>Decode an Erlang external term format binary.</fsummary> <desc> <p>As <c>binary_to_term/1</c>, but takes these options:</p> @@ -613,7 +613,7 @@ hello </func> <func> - <name name="bit_size" arity="1"/> + <name name="bit_size" arity="1" since=""/> <fsummary>Return the size of a bitstring.</fsummary> <desc> <p>Returns an integer that is the size in bits of @@ -628,7 +628,7 @@ hello </func> <func> - <name name="bitstring_to_list" arity="1"/> + <name name="bitstring_to_list" arity="1" since=""/> <fsummary>Convert a bitstring to a list.</fsummary> <desc> <p>Returns a list of integers corresponding to the bytes of @@ -639,7 +639,7 @@ hello </func> <func> - <name name="bump_reductions" arity="1"/> + <name name="bump_reductions" arity="1" since=""/> <fsummary>Increment the reduction counter.</fsummary> <desc> <p>This implementation-dependent function increments @@ -657,7 +657,7 @@ hello </func> <func> - <name name="byte_size" arity="1"/> + <name name="byte_size" arity="1" since=""/> <fsummary>Return the size of a bitstring (or binary).</fsummary> <desc> <p>Returns an integer that is the number of bytes needed to @@ -674,7 +674,7 @@ hello </func> <func> - <name name="cancel_timer" arity="1"/> + <name name="cancel_timer" arity="1" since=""/> <fsummary>Cancel a timer.</fsummary> <desc> <p>Cancels a timer. The same as calling @@ -684,7 +684,7 @@ hello </func> <func> - <name name="cancel_timer" arity="2"/> + <name name="cancel_timer" arity="2" since="OTP 18.0"/> <fsummary>Cancel a timer.</fsummary> <desc> <p>Cancels a timer that has been created by @@ -766,7 +766,7 @@ hello </func> <func> - <name name="ceil" arity="1"/> + <name name="ceil" arity="1" since="OTP 20.0"/> <fsummary>Returns the smallest integer not less than the argument</fsummary> <desc> <p>Returns the smallest integer not less than @@ -779,7 +779,7 @@ hello </desc> </func> <func> - <name name="check_old_code" arity="1"/> + <name name="check_old_code" arity="1" since="OTP R14B04"/> <fsummary>Check if a module has old code.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Module</anno></c> has old code, @@ -790,7 +790,7 @@ hello </func> <func> - <name name="check_process_code" arity="2"/> + <name name="check_process_code" arity="2" since=""/> <fsummary>Check if a process executes old code for a module.</fsummary> <desc> <p>The same as @@ -801,7 +801,7 @@ hello </func> <func> - <name name="check_process_code" arity="3"/> + <name name="check_process_code" arity="3" since="OTP 17.0"/> <fsummary>Check if a process executes old code for a module.</fsummary> <desc> <p>Checks if the node local process identified by @@ -904,7 +904,7 @@ hello </func> <func> - <name name="convert_time_unit" arity="3"/> + <name name="convert_time_unit" arity="3" since="OTP 18.0"/> <fsummary>Convert time unit of a time value.</fsummary> <desc> <p>Converts the <c><anno>Time</anno></c> value of time unit @@ -922,7 +922,7 @@ hello </func> <func> - <name name="crc32" arity="1"/> + <name name="crc32" arity="1" since=""/> <fsummary>Compute crc32 (IEEE 802.3) checksum.</fsummary> <desc> <p>Computes and returns the crc32 (IEEE 802.3 style) checksum @@ -931,7 +931,7 @@ hello </func> <func> - <name name="crc32" arity="2"/> + <name name="crc32" arity="2" since=""/> <fsummary>Compute crc32 (IEEE 802.3) checksum.</fsummary> <desc> <p>Continues computing the crc32 checksum by combining @@ -948,7 +948,7 @@ Y = erlang:crc32([Data1,Data2]).</code> </func> <func> - <name name="crc32_combine" arity="3"/> + <name name="crc32_combine" arity="3" since=""/> <fsummary>Combine two crc32 (IEEE 802.3) checksums.</fsummary> <desc> <p>Combines two previously computed crc32 checksums. @@ -967,7 +967,7 @@ Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="date" arity="0"/> + <name name="date" arity="0" since=""/> <fsummary>Current date.</fsummary> <desc> <p>Returns the current date as <c>{Year, Month, Day}</c>.</p> @@ -980,7 +980,7 @@ Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="decode_packet" arity="3"/> + <name name="decode_packet" arity="3" since=""/> <fsummary>Extract a protocol packet from a binary.</fsummary> <desc> <p>Decodes the binary <c><anno>Bin</anno></c> according to the packet @@ -1090,7 +1090,7 @@ Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="delete_element" arity="2"/> + <name name="delete_element" arity="2" since="OTP R16B"/> <fsummary>Delete element at index in a tuple.</fsummary> <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>)</type_desc> <desc> @@ -1103,7 +1103,7 @@ Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="delete_module" arity="1"/> + <name name="delete_module" arity="1" since=""/> <fsummary>Make the current code for a module old.</fsummary> <desc> <p>Makes the current code for <c><anno>Module</anno></c> become old @@ -1121,7 +1121,7 @@ Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="demonitor" arity="1"/> + <name name="demonitor" arity="1" since=""/> <fsummary>Stop monitoring.</fsummary> <desc> <p>If <c><anno>MonitorRef</anno></c> is a reference that the @@ -1163,7 +1163,7 @@ Z = erlang:crc32_combine(X,Y,iolist_size(Data2)).</code> </func> <func> - <name name="demonitor" arity="2"/> + <name name="demonitor" arity="2" since=""/> <fsummary>Stop monitoring.</fsummary> <desc> <p>The returned value is <c>true</c> unless <c>info</c> is part @@ -1231,7 +1231,7 @@ end</code> </func> <func> - <name name="disconnect_node" arity="1"/> + <name name="disconnect_node" arity="1" since=""/> <fsummary>Force the disconnection of a node.</fsummary> <desc> <p>Forces the disconnection of a node. This appears to @@ -1245,7 +1245,7 @@ end</code> </func> <func> - <name name="display" arity="1"/> + <name name="display" arity="1" since=""/> <fsummary>Print a term on standard output.</fsummary> <desc> <p>Prints a text representation of <c><anno>Term</anno></c> on the @@ -1257,7 +1257,7 @@ end</code> </func> <func> - <name name="dist_ctrl_get_data" arity="1"/> + <name name="dist_ctrl_get_data" arity="1" since="OTP 21.0"/> <fsummary>Get distribution channel data to pass to another node.</fsummary> <desc> <p> @@ -1290,7 +1290,7 @@ end</code> </func> <func> - <name name="dist_ctrl_get_data_notification" arity="1"/> + <name name="dist_ctrl_get_data_notification" arity="1" since="OTP 21.0"/> <fsummary>Request notification about available outgoing distribution channel data.</fsummary> <desc> <p> @@ -1326,7 +1326,7 @@ end</code> </func> <func> - <name name="dist_ctrl_input_handler" arity="2"/> + <name name="dist_ctrl_input_handler" arity="2" since="OTP 21.0"/> <fsummary>Register distribution channel input handler process.</fsummary> <desc> <p> @@ -1359,7 +1359,7 @@ end</code> </func> <func> - <name name="dist_ctrl_put_data" arity="2"/> + <name name="dist_ctrl_put_data" arity="2" since="OTP 21.0"/> <fsummary>Pass data into the VM from a distribution channel.</fsummary> <desc> <p> @@ -1392,7 +1392,7 @@ end</code> </func> <func> - <name name="element" arity="2"/> + <name name="element" arity="2" since=""/> <fsummary>Return the Nth element of a tuple.</fsummary> <type_desc variable="N">1..tuple_size(<anno>Tuple</anno>)</type_desc> <desc> @@ -1406,7 +1406,7 @@ b</pre> </func> <func> - <name name="erase" arity="0"/> + <name name="erase" arity="0" since=""/> <fsummary>Return and delete the process dictionary.</fsummary> <desc> <p>Returns the process dictionary and deletes it, for @@ -1420,7 +1420,7 @@ b</pre> </func> <func> - <name name="erase" arity="1"/> + <name name="erase" arity="1" since=""/> <fsummary>Return and delete a value from the process dictionary. </fsummary> <desc> @@ -1437,7 +1437,7 @@ b</pre> </func> <func> - <name name="error" arity="1"/> + <name name="error" arity="1" since=""/> <fsummary>Stop execution with a specified reason.</fsummary> <desc> <p>Stops the execution of the calling process with the reason @@ -1461,7 +1461,7 @@ b</pre> </func> <func> - <name name="error" arity="2"/> + <name name="error" arity="2" since=""/> <fsummary>Stop execution with a specified reason.</fsummary> <desc> <p>Stops the execution of the calling process with the reason @@ -1478,7 +1478,7 @@ b</pre> </func> <func> - <name name="exit" arity="1"/> + <name name="exit" arity="1" since=""/> <fsummary>Stop execution with a specified reason.</fsummary> <desc> <p>Stops the execution of the calling process with exit reason @@ -1495,7 +1495,7 @@ b</pre> </func> <func> - <name name="exit" arity="2"/> + <name name="exit" arity="2" since=""/> <fsummary>Send an exit signal to a process or a port.</fsummary> <desc> <p>Sends an exit signal with exit reason <c><anno>Reason</anno></c> to @@ -1531,7 +1531,7 @@ b</pre> </func> <func> - <name name="external_size" arity="1"/> + <name name="external_size" arity="1" since="OTP R14B04"/> <fsummary>Calculate the maximum size for a term encoded in the Erlang external term format.</fsummary> <desc> @@ -1550,7 +1550,7 @@ erlang:external_size(<anno>Term</anno>, [])</code> </func> <func> - <name name="external_size" arity="2"/> + <name name="external_size" arity="2" since="OTP R14B04"/> <fsummary>Calculate the maximum size for a term encoded in the Erlang external term format.</fsummary> <desc> @@ -1570,7 +1570,7 @@ true</pre> </func> <func> - <name name="float" arity="1"/> + <name name="float" arity="1" since=""/> <fsummary>Convert a number to a float.</fsummary> <desc> <p>Returns a float by converting <c><anno>Number</anno></c> to a float, @@ -1592,7 +1592,7 @@ true</pre> </func> <func> - <name name="float_to_binary" arity="1"/> + <name name="float_to_binary" arity="1" since="OTP R16B"/> <fsummary>Text representation of a float.</fsummary> <desc> <p>The same as @@ -1601,7 +1601,7 @@ true</pre> </func> <func> - <name name="float_to_binary" arity="2"/> + <name name="float_to_binary" arity="2" since="OTP R16B"/> <fsummary>Text representation of a float formatted using specified options.</fsummary> <desc> @@ -1619,7 +1619,7 @@ true</pre> </func> <func> - <name name="float_to_list" arity="1"/> + <name name="float_to_list" arity="1" since=""/> <fsummary>Text representation of a float.</fsummary> <desc> <p>The same as @@ -1628,7 +1628,7 @@ true</pre> </func> <func> - <name name="float_to_list" arity="2"/> + <name name="float_to_list" arity="2" since="OTP R16B"/> <fsummary>Text representation of a float formatted using specified options.</fsummary> <desc> @@ -1664,7 +1664,7 @@ true</pre> </func> <func> - <name name="floor" arity="1"/> + <name name="floor" arity="1" since="OTP 20.0"/> <fsummary>Returns the largest integer not greater than the argument</fsummary> <desc> <p>Returns the largest integer not greater than @@ -1678,7 +1678,7 @@ true</pre> </func> <func> - <name name="fun_info" arity="1"/> + <name name="fun_info" arity="1" since=""/> <fsummary>Information about a fun.</fsummary> <desc> <p>Returns a list with information about the fun @@ -1783,7 +1783,7 @@ true</pre> </func> <func> - <name name="fun_info" arity="2"/> + <name name="fun_info" arity="2" since=""/> <fsummary>Information about a fun.</fsummary> <type name="fun_info_item"/> <desc> @@ -1803,7 +1803,7 @@ true</pre> </func> <func> - <name name="fun_to_list" arity="1"/> + <name name="fun_to_list" arity="1" since=""/> <fsummary>Text representation of a fun.</fsummary> <desc> <p>Returns a string corresponding to the text @@ -1812,7 +1812,7 @@ true</pre> </func> <func> - <name name="function_exported" arity="3"/> + <name name="function_exported" arity="3" since=""/> <fsummary>Check if a function is exported and loaded.</fsummary> <desc> <p>Returns <c>true</c> if the module <c><anno>Module</anno></c> is @@ -1828,7 +1828,7 @@ true</pre> </func> <func> - <name name="garbage_collect" arity="0"/> + <name name="garbage_collect" arity="0" since=""/> <fsummary>Force an immediate garbage collection of the calling process. </fsummary> <desc> @@ -1844,7 +1844,7 @@ true</pre> </func> <func> - <name name="garbage_collect" arity="1"/> + <name name="garbage_collect" arity="1" since=""/> <fsummary>Garbage collect a process.</fsummary> <desc> <p>The same as @@ -1854,7 +1854,7 @@ true</pre> </func> <func> - <name name="garbage_collect" arity="2"/> + <name name="garbage_collect" arity="2" since="OTP 17.0"/> <fsummary>Garbage collect a process.</fsummary> <desc> <p>Garbage collects the node local process identified by @@ -1919,7 +1919,7 @@ true</pre> </func> <func> - <name name="get" arity="0"/> + <name name="get" arity="0" since=""/> <fsummary>Return the process dictionary.</fsummary> <desc> <p>Returns the process dictionary as a list of @@ -1934,7 +1934,7 @@ true</pre> </func> <func> - <name name="get" arity="1"/> + <name name="get" arity="1" since=""/> <fsummary>Return a value from the process dictionary.</fsummary> <desc> <p>Returns the value <c><anno>Val</anno></c> associated with @@ -1950,7 +1950,7 @@ true</pre> </func> <func> - <name name="get_cookie" arity="0"/> + <name name="get_cookie" arity="0" since=""/> <fsummary>Get the magic cookie of the local node.</fsummary> <desc> <p>Returns the magic cookie of the local node if the node is @@ -1959,7 +1959,7 @@ true</pre> </func> <func> - <name name="get_keys" arity="0"/> + <name name="get_keys" arity="0" since="OTP 18.0"/> <fsummary>Return a list of all keys from the process dictionary. </fsummary> <desc> @@ -1975,7 +1975,7 @@ true</pre> </func> <func> - <name name="get_keys" arity="1"/> + <name name="get_keys" arity="1" since=""/> <fsummary>Return a list of keys from the process dictionary.</fsummary> <desc> <p>Returns a list of keys that are associated with the value @@ -1993,7 +1993,7 @@ true</pre> </func> <func> - <name name="get_stacktrace" arity="0"/> + <name name="get_stacktrace" arity="0" since=""/> <fsummary>Get the call stack back-trace of the last exception.</fsummary> <type name="stack_item"/> <desc> @@ -2058,7 +2058,7 @@ end</pre> </func> <func> - <name name="group_leader" arity="0"/> + <name name="group_leader" arity="0" since=""/> <fsummary>Get the group leader for the calling process.</fsummary> <desc> <p>Returns the process identifier of the group leader for the @@ -2073,7 +2073,7 @@ end</pre> </func> <func> - <name name="group_leader" arity="2"/> + <name name="group_leader" arity="2" since=""/> <fsummary>Set the group leader for a process.</fsummary> <desc> <p>Sets the group leader of <c><anno>Pid</anno></c> @@ -2094,7 +2094,7 @@ end</pre> </func> <func> - <name name="halt" arity="0"/> + <name name="halt" arity="0" since=""/> <fsummary>Halt the Erlang runtime system and indicate normal exit to the calling environment.</fsummary> <desc> @@ -2107,7 +2107,7 @@ os_prompt%</pre> </func> <func> - <name name="halt" arity="1"/> + <name name="halt" arity="1" since=""/> <fsummary>Halt the Erlang runtime system.</fsummary> <desc> <p>The same as <seealso marker="#halt/2"> @@ -2121,7 +2121,7 @@ os_prompt%</pre> </func> <func> - <name name="halt" arity="2"/> + <name name="halt" arity="2" since="OTP R15B01"/> <fsummary>Halt the Erlang runtime system.</fsummary> <desc> <p><c><anno>Status</anno></c> must be a non-negative integer, a string, @@ -2163,7 +2163,7 @@ os_prompt%</pre> </func> <func> - <name name="hd" arity="1"/> + <name name="hd" arity="1" since=""/> <fsummary>Head of a list.</fsummary> <desc> <p>Returns the head of <c><anno>List</anno></c>, that is, @@ -2178,7 +2178,7 @@ os_prompt%</pre> </func> <func> - <name name="hibernate" arity="3"/> + <name name="hibernate" arity="3" since=""/> <fsummary>Hibernate a process until a message is sent to it.</fsummary> <desc> <p>Puts the calling process into a wait state where its memory @@ -2219,7 +2219,7 @@ os_prompt%</pre> </func> <func> - <name name="insert_element" arity="3"/> + <name name="insert_element" arity="3" since="OTP R16B"/> <fsummary>Insert an element at index in a tuple.</fsummary> <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno>) + 1</type_desc> @@ -2237,7 +2237,7 @@ os_prompt%</pre> </func> <func> - <name name="integer_to_binary" arity="1"/> + <name name="integer_to_binary" arity="1" since="OTP R16B"/> <fsummary>Text representation of an integer.</fsummary> <desc> <p>Returns a binary corresponding to the text @@ -2249,7 +2249,7 @@ os_prompt%</pre> </func> <func> - <name name="integer_to_binary" arity="2"/> + <name name="integer_to_binary" arity="2" since="OTP R16B"/> <fsummary>Text representation of an integer.</fsummary> <desc> <p>Returns a binary corresponding to the text @@ -2262,7 +2262,7 @@ os_prompt%</pre> </func> <func> - <name name="integer_to_list" arity="1"/> + <name name="integer_to_list" arity="1" since=""/> <fsummary>Text representation of an integer.</fsummary> <desc> <p>Returns a string corresponding to the text @@ -2274,7 +2274,7 @@ os_prompt%</pre> </func> <func> - <name name="integer_to_list" arity="2"/> + <name name="integer_to_list" arity="2" since=""/> <fsummary>Text representation of an integer.</fsummary> <desc> <p>Returns a string corresponding to the text @@ -2287,7 +2287,7 @@ os_prompt%</pre> </func> <func> - <name name="iolist_size" arity="1"/> + <name name="iolist_size" arity="1" since=""/> <fsummary>Size of an iolist.</fsummary> <desc> <p>Returns an integer, that is the size in bytes, @@ -2300,7 +2300,7 @@ os_prompt%</pre> </func> <func> - <name name="iolist_to_binary" arity="1"/> + <name name="iolist_to_binary" arity="1" since=""/> <fsummary>Convert an iolist to a binary.</fsummary> <desc> <p>Returns a binary that is made from the integers and @@ -2318,7 +2318,7 @@ os_prompt%</pre> </func> <func> - <name name="iolist_to_iovec" arity="1"/> + <name name="iolist_to_iovec" arity="1" since="OTP 20.1"/> <fsummary>Converts an iolist to a iovec.</fsummary> <desc> <p>Returns an iovec that is made from the integers and binaries in @@ -2327,7 +2327,7 @@ os_prompt%</pre> </func> <func> - <name name="is_alive" arity="0"/> + <name name="is_alive" arity="0" since=""/> <fsummary>Check whether the local node is alive.</fsummary> <desc> <p>Returns <c>true</c> if the local node is alive (that is, if @@ -2337,7 +2337,7 @@ os_prompt%</pre> </func> <func> - <name name="is_atom" arity="1"/> + <name name="is_atom" arity="1" since=""/> <fsummary>Check whether a term is an atom.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is an atom, @@ -2347,7 +2347,7 @@ os_prompt%</pre> </func> <func> - <name name="is_binary" arity="1"/> + <name name="is_binary" arity="1" since=""/> <fsummary>Check whether a term is a binary.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a binary, @@ -2358,7 +2358,7 @@ os_prompt%</pre> </func> <func> - <name name="is_bitstring" arity="1"/> + <name name="is_bitstring" arity="1" since=""/> <fsummary>Check whether a term is a bitstring.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a @@ -2368,7 +2368,7 @@ os_prompt%</pre> </func> <func> - <name name="is_boolean" arity="1"/> + <name name="is_boolean" arity="1" since=""/> <fsummary>Check whether a term is a boolean.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is the @@ -2379,7 +2379,7 @@ os_prompt%</pre> </func> <func> - <name name="is_builtin" arity="3"/> + <name name="is_builtin" arity="3" since=""/> <fsummary>Check if a function is a BIF implemented in C.</fsummary> <desc> <p>This BIF is useful for builders of cross-reference tools.</p> @@ -2390,7 +2390,7 @@ os_prompt%</pre> </func> <func> - <name name="is_float" arity="1"/> + <name name="is_float" arity="1" since=""/> <fsummary>Check whether a term is a float.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a floating point @@ -2400,7 +2400,7 @@ os_prompt%</pre> </func> <func> - <name name="is_function" arity="1"/> + <name name="is_function" arity="1" since=""/> <fsummary>Check whether a term is a fun.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a fun, otherwise @@ -2410,7 +2410,7 @@ os_prompt%</pre> </func> <func> - <name name="is_function" arity="2"/> + <name name="is_function" arity="2" since=""/> <fsummary>Check whether a term is a fun with a specified given arity. </fsummary> <desc> @@ -2422,7 +2422,7 @@ os_prompt%</pre> </func> <func> - <name name="is_integer" arity="1"/> + <name name="is_integer" arity="1" since=""/> <fsummary>Check whether a term is an integer.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is an integer, @@ -2432,7 +2432,7 @@ os_prompt%</pre> </func> <func> - <name name="is_list" arity="1"/> + <name name="is_list" arity="1" since=""/> <fsummary>Check whether a term is a list.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a list with @@ -2442,7 +2442,7 @@ os_prompt%</pre> </func> <func> - <name name="is_map" arity="1"/> + <name name="is_map" arity="1" since="OTP 17.0"/> <fsummary>Check whether a term is a map.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a map, @@ -2452,7 +2452,7 @@ os_prompt%</pre> </func> <func> - <name name="is_map_key" arity="2"/> + <name name="is_map_key" arity="2" since="OTP 21.0"/> <fsummary></fsummary> <desc> <p>Returns <c>true</c> if map <c><anno>Map</anno></c> contains @@ -2472,7 +2472,7 @@ false</code> </func> <func> - <name name="is_number" arity="1"/> + <name name="is_number" arity="1" since=""/> <fsummary>Check whether a term is a number.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is an integer or a @@ -2482,7 +2482,7 @@ false</code> </func> <func> - <name name="is_pid" arity="1"/> + <name name="is_pid" arity="1" since=""/> <fsummary>Check whether a term is a process identifier.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a process @@ -2492,7 +2492,7 @@ false</code> </func> <func> - <name name="is_port" arity="1"/> + <name name="is_port" arity="1" since=""/> <fsummary>Check whether a term is a port.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a port identifier, @@ -2502,7 +2502,7 @@ false</code> </func> <func> - <name name="is_process_alive" arity="1"/> + <name name="is_process_alive" arity="1" since=""/> <fsummary>Check whether a process is alive.</fsummary> <desc> <p><c><anno>Pid</anno></c> must refer to a process at the local @@ -2514,7 +2514,7 @@ false</code> </func> <func> - <name name="is_record" arity="2"/> + <name name="is_record" arity="2" since=""/> <fsummary>Check whether a term appears to be a record.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a tuple and its @@ -2535,7 +2535,7 @@ false</code> </func> <func> - <name name="is_record" arity="3"/> + <name name="is_record" arity="3" since=""/> <fsummary>Check whether a term appears to be a record.</fsummary> <desc> <p><c><anno>RecordTag</anno></c> must be an atom.</p> @@ -2554,7 +2554,7 @@ false</code> </func> <func> - <name name="is_reference" arity="1"/> + <name name="is_reference" arity="1" since=""/> <fsummary>Check whether a term is a reference.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a reference, @@ -2564,7 +2564,7 @@ false</code> </func> <func> - <name name="is_tuple" arity="1"/> + <name name="is_tuple" arity="1" since=""/> <fsummary>Check whether a term is a tuple.</fsummary> <desc> <p>Returns <c>true</c> if <c><anno>Term</anno></c> is a tuple, @@ -2574,7 +2574,7 @@ false</code> </func> <func> - <name name="length" arity="1"/> + <name name="length" arity="1" since=""/> <fsummary>Length of a list.</fsummary> <desc> <p>Returns the length of <c><anno>List</anno></c>, for example:</p> @@ -2586,7 +2586,7 @@ false</code> </func> <func> - <name name="link" arity="1"/> + <name name="link" arity="1" since=""/> <fsummary>Create a link to another process (or port).</fsummary> <desc> <p>Creates a link between the calling process and another @@ -2613,7 +2613,7 @@ false</code> </func> <func> - <name name="list_to_atom" arity="1"/> + <name name="list_to_atom" arity="1" since=""/> <fsummary>Convert from text representation to an atom.</fsummary> <desc> <p>Returns the atom whose text representation is @@ -2633,7 +2633,7 @@ false</code> </func> <func> - <name name="list_to_binary" arity="1"/> + <name name="list_to_binary" arity="1" since=""/> <fsummary>Convert a list to a binary.</fsummary> <desc> <p>Returns a binary that is made from the integers and @@ -2651,7 +2651,7 @@ false</code> </func> <func> - <name name="list_to_bitstring" arity="1"/> + <name name="list_to_bitstring" arity="1" since=""/> <fsummary>Convert a list to a bitstring.</fsummary> <type name="bitstring_list"/> <desc> @@ -2672,7 +2672,7 @@ false</code> </func> <func> - <name name="list_to_existing_atom" arity="1"/> + <name name="list_to_existing_atom" arity="1" since=""/> <fsummary>Convert from text representation to an atom.</fsummary> <desc> <p>Returns the atom whose text representation is @@ -2693,7 +2693,7 @@ false</code> </func> <func> - <name name="list_to_float" arity="1"/> + <name name="list_to_float" arity="1" since=""/> <fsummary>Convert from text representation to a float.</fsummary> <desc> <p>Returns the float whose text representation is @@ -2707,7 +2707,7 @@ false</code> </func> <func> - <name name="list_to_integer" arity="1"/> + <name name="list_to_integer" arity="1" since=""/> <fsummary>Convert from text representation to an integer.</fsummary> <desc> <p>Returns an integer whose text representation is @@ -2721,7 +2721,7 @@ false</code> </func> <func> - <name name="list_to_integer" arity="2"/> + <name name="list_to_integer" arity="2" since=""/> <fsummary>Convert from text representation to an integer.</fsummary> <desc> <p>Returns an integer whose text representation in base @@ -2736,7 +2736,7 @@ false</code> </func> <func> - <name name="list_to_pid" arity="1"/> + <name name="list_to_pid" arity="1" since=""/> <fsummary>Convert from text representation to a pid.</fsummary> <desc> <p>Returns a process identifier whose text representation is a @@ -2754,7 +2754,7 @@ false</code> </func> <func> - <name name="list_to_port" arity="1"/> + <name name="list_to_port" arity="1" since="OTP 20.0"/> <fsummary>Convert from text representation to a port.</fsummary> <desc> <p>Returns a port identifier whose text representation is a @@ -2772,7 +2772,7 @@ false</code> </func> <func> - <name name="list_to_ref" arity="1"/> + <name name="list_to_ref" arity="1" since="OTP 20.0"/> <fsummary>Convert from text representation to a ref.</fsummary> <desc> <p>Returns a reference whose text representation is a @@ -2790,7 +2790,7 @@ false</code> </func> <func> - <name name="list_to_tuple" arity="1"/> + <name name="list_to_tuple" arity="1" since=""/> <fsummary>Convert a list to a tuple.</fsummary> <desc> <p>Returns a tuple corresponding to <c><anno>List</anno></c>, @@ -2803,7 +2803,7 @@ false</code> </func> <func> - <name name="load_module" arity="2"/> + <name name="load_module" arity="2" since=""/> <fsummary>Load object code for a module.</fsummary> <desc> <p>If <c><anno>Binary</anno></c> contains the object code for module @@ -2836,7 +2836,7 @@ false</code> </func> <func> - <name name="load_nif" arity="2"/> + <name name="load_nif" arity="2" since=""/> <fsummary>Load NIF library.</fsummary> <desc> <p>Loads and links a dynamic library containing native @@ -2889,7 +2889,7 @@ false</code> </func> <func> - <name name="loaded" arity="0"/> + <name name="loaded" arity="0" since=""/> <fsummary>List all loaded modules.</fsummary> <desc> <p>Returns a list of all loaded Erlang modules (current and @@ -2900,7 +2900,7 @@ false</code> </func> <func> - <name name="localtime" arity="0"/> + <name name="localtime" arity="0" since=""/> <fsummary>Current local date and time.</fsummary> <desc> <p>Returns the current local date and time, @@ -2915,7 +2915,7 @@ false</code> </func> <func> - <name name="localtime_to_universaltime" arity="1"/> + <name name="localtime_to_universaltime" arity="1" since=""/> <fsummary>Convert from local to Universal Time Coordinated (UTC) date and time.</fsummary> <desc> @@ -2932,7 +2932,7 @@ false</code> </func> <func> - <name name="localtime_to_universaltime" arity="2"/> + <name name="localtime_to_universaltime" arity="2" since=""/> <fsummary>Convert from local to Universal Time Coordinated (UTC) date and time.</fsummary> <desc> @@ -2958,7 +2958,7 @@ false</code> </func> <func> - <name name="make_ref" arity="0"/> + <name name="make_ref" arity="0" since=""/> <fsummary>Return a unique reference.</fsummary> <desc> <p>Returns a @@ -2975,7 +2975,7 @@ false</code> </func> <func> - <name name="make_tuple" arity="2"/> + <name name="make_tuple" arity="2" since=""/> <fsummary>Create a new tuple of a specified arity.</fsummary> <desc> <p>Creates a new tuple of the specified <c><anno>Arity</anno></c>, where @@ -2987,7 +2987,7 @@ false</code> </func> <func> - <name name="make_tuple" arity="3"/> + <name name="make_tuple" arity="3" since=""/> <fsummary>Create a new tuple with specifed arity and contents.</fsummary> <desc> <p>Creates a tuple of size <c><anno>Arity</anno></c>, where each element @@ -3005,7 +3005,7 @@ false</code> </func> <func> - <name name="map_get" arity="2" /> + <name name="map_get" arity="2" since="OTP 21.0"/> <fsummary>Extract a value from a map</fsummary> <desc> <p>Returns value <c><anno>Value</anno></c> associated with @@ -3024,7 +3024,7 @@ false</code> </func> <func> - <name name="map_size" arity="1"/> + <name name="map_size" arity="1" since="OTP 17.0"/> <fsummary>Return the size of a map.</fsummary> <desc> <p>Returns an integer, which is the number of key-value pairs @@ -3037,7 +3037,7 @@ false</code> </func> <func> - <name name="match_spec_test" arity="3"/> + <name name="match_spec_test" arity="3" since="OTP 19.0"/> <fsummary>Test that a match specification works.</fsummary> <desc> <p>Tests a match specification used in calls to @@ -3075,7 +3075,7 @@ false</code> </func> <func> - <name name="max" arity="2"/> + <name name="max" arity="2" since=""/> <fsummary>Return the largest of two terms.</fsummary> <desc> <p>Returns the largest of <c><anno>Term1</anno></c> and @@ -3085,7 +3085,7 @@ false</code> </func> <func> - <name name="md5" arity="1"/> + <name name="md5" arity="1" since=""/> <fsummary>Compute an MD5 message digest.</fsummary> <desc> <p>Computes an MD5 message digest from <c><anno>Data</anno></c>, where @@ -3103,7 +3103,7 @@ false</code> </func> <func> - <name name="md5_final" arity="1"/> + <name name="md5_final" arity="1" since=""/> <fsummary>Finish the update of an MD5 context and return the computed MD5 message digest.</fsummary> <desc> @@ -3113,7 +3113,7 @@ false</code> </func> <func> - <name name="md5_init" arity="0"/> + <name name="md5_init" arity="0" since=""/> <fsummary>Create an MD5 context.</fsummary> <desc> <p>Creates an MD5 context, to be used in the following calls to @@ -3122,7 +3122,7 @@ false</code> </func> <func> - <name name="md5_update" arity="2"/> + <name name="md5_update" arity="2" since=""/> <fsummary>Update an MD5 context with data and return a new context. </fsummary> <desc> @@ -3133,7 +3133,7 @@ false</code> </func> <func> - <name name="memory" arity="0"/> + <name name="memory" arity="0" since=""/> <fsummary>Information about dynamically allocated memory.</fsummary> <type name="memory_type"/> <desc> @@ -3277,8 +3277,8 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="memory" arity="1" clause_i="1"/> - <name name="memory" arity="1" clause_i="2"/> + <name name="memory" arity="1" clause_i="1" since=""/> + <name name="memory" arity="1" clause_i="2" since=""/> <fsummary>Information about dynamically allocated memory.</fsummary> <type name="memory_type"/> <desc> @@ -3317,7 +3317,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="min" arity="2"/> + <name name="min" arity="2" since=""/> <fsummary>Return the smallest of two terms.</fsummary> <desc> <p>Returns the smallest of <c><anno>Term1</anno></c> and @@ -3327,7 +3327,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="module_loaded" arity="1"/> + <name name="module_loaded" arity="1" since=""/> <fsummary>Check if a module is loaded.</fsummary> <desc> <p>Returns <c>true</c> if the module <c><anno>Module</anno></c> @@ -3342,9 +3342,9 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="monitor" arity="2" clause_i="1"/> - <name name="monitor" arity="2" clause_i="2"/> - <name name="monitor" arity="2" clause_i="3"/> + <name name="monitor" arity="2" clause_i="1" since=""/> + <name name="monitor" arity="2" clause_i="2" since="?"/> + <name name="monitor" arity="2" clause_i="3" since="OTP 18.0"/> <fsummary>Start monitoring.</fsummary> <type name="registered_name"/> <type name="registered_process_identifier"/> @@ -3516,7 +3516,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="monitor_node" arity="2"/> + <name name="monitor_node" arity="2" since=""/> <fsummary>Monitor the status of a node.</fsummary> <desc> <p>Monitor the status of the node <c><anno>Node</anno></c>. @@ -3540,7 +3540,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="monitor_node" arity="3"/> + <name name="monitor_node" arity="3" since=""/> <fsummary>Monitor the status of a node.</fsummary> <desc> <p>Behaves as @@ -3566,7 +3566,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="monotonic_time" arity="0"/> + <name name="monotonic_time" arity="0" since="OTP 18.0"/> <fsummary>Current Erlang monotonic time.</fsummary> <desc> <p>Returns the current @@ -3600,7 +3600,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="monotonic_time" arity="1"/> + <name name="monotonic_time" arity="1" since="OTP 18.0"/> <fsummary>Current Erlang monotonic time.</fsummary> <desc> <p>Returns the current @@ -3618,7 +3618,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="nif_error" arity="1"/> + <name name="nif_error" arity="1" since="OTP R14B"/> <fsummary>Stop execution with a specified reason.</fsummary> <desc> <p>Works exactly like @@ -3631,7 +3631,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="nif_error" arity="2"/> + <name name="nif_error" arity="2" since="OTP R14B"/> <fsummary>Stop execution with a specified reason.</fsummary> <desc> <p>Works exactly like @@ -3644,7 +3644,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="node" arity="0"/> + <name name="node" arity="0" since=""/> <fsummary>Name of the local node.</fsummary> <desc> <p>Returns the name of the local node. If the node is not alive, @@ -3654,7 +3654,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="node" arity="1"/> + <name name="node" arity="1" since=""/> <fsummary>At which node a pid, port, or reference originates.</fsummary> <desc> <p>Returns the node where <c><anno>Arg</anno></c> originates. @@ -3667,7 +3667,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="nodes" arity="0"/> + <name name="nodes" arity="0" since=""/> <fsummary>All visible nodes in the system.</fsummary> <desc> <p>Returns a list of all visible nodes in the system, except @@ -3676,7 +3676,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="nodes" arity="1"/> + <name name="nodes" arity="1" since=""/> <fsummary>All nodes of a certain type in the system.</fsummary> <desc> <p>Returns a list of nodes according to the argument specified. @@ -3719,7 +3719,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="now" arity="0"/> + <name name="now" arity="0" since=""/> <fsummary>Elapsed time since 00:00 GMT.</fsummary> <type name="timestamp"/> <desc> @@ -3748,7 +3748,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="open_port" arity="2"/> + <name name="open_port" arity="2" since=""/> <fsummary>Open a port.</fsummary> <desc> <p>Returns a port identifier as the result of opening a @@ -4089,7 +4089,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="phash" arity="2"/> + <name name="phash" arity="2" since=""/> <fsummary>Portable hash function.</fsummary> <type_desc variable="Range">Range = 1..2^32, Hash = 1..Range</type_desc> <desc> @@ -4104,8 +4104,8 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="phash2" arity="1"/> - <name name="phash2" arity="2"/> + <name name="phash2" arity="1" since=""/> + <name name="phash2" arity="2" since=""/> <fsummary>Portable hash function.</fsummary> <type_desc variable="Range">1..2^32</type_desc> <type_desc variable="Hash">0..Range-1</type_desc> @@ -4129,7 +4129,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="pid_to_list" arity="1"/> + <name name="pid_to_list" arity="1" since=""/> <fsummary>Text representation of a pid.</fsummary> <desc> <p>Returns a string corresponding to the text @@ -4138,7 +4138,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_call" arity="3"/> + <name name="port_call" arity="3" since=""/> <fsummary>Perform a synchronous call to a port with term data.</fsummary> <desc> <p>Performs a synchronous call to a port. The meaning of @@ -4183,7 +4183,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_close" arity="1"/> + <name name="port_close" arity="1" since=""/> <fsummary>Close an open port.</fsummary> <desc> <p>Closes an open port. Roughly the same as <c><anno>Port</anno> ! @@ -4223,7 +4223,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_command" arity="2"/> + <name name="port_command" arity="2" since=""/> <fsummary>Send data to a port.</fsummary> <desc> <p>Sends data to a port. Same as @@ -4270,7 +4270,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_command" arity="3"/> + <name name="port_command" arity="3" since=""/> <fsummary>Send data to a port.</fsummary> <desc> <p>Sends data to a port. <c>port_command(Port, Data, [])</c> @@ -4329,7 +4329,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_connect" arity="2"/> + <name name="port_connect" arity="2" since=""/> <fsummary>Set the owner of a port.</fsummary> <desc> <p>Sets the port owner (the connected port) to <c><anno>Pid</anno></c>. @@ -4398,7 +4398,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_control" arity="3"/> + <name name="port_control" arity="3" since=""/> <fsummary>Perform a synchronous control operation on a port.</fsummary> <desc> <p>Performs a synchronous control operation on a port. @@ -4435,7 +4435,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="1"/> + <name name="port_info" arity="1" since=""/> <fsummary>Information about a port.</fsummary> <desc> <p>Returns a list containing tuples with information about @@ -4466,7 +4466,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="1"/> + <name name="port_info" arity="2" clause_i="1" since=""/> <fsummary>Information about the connected process of a port.</fsummary> <desc> <p><c><anno>Pid</anno></c> is the process identifier of the process @@ -4482,7 +4482,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="2"/> + <name name="port_info" arity="2" clause_i="2" since=""/> <fsummary>Information about the internal index of a port.</fsummary> <desc> <p><c><anno>Index</anno></c> is the internal index of the port. This @@ -4498,7 +4498,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="3"/> + <name name="port_info" arity="2" clause_i="3" since=""/> <fsummary>Information about the input of a port.</fsummary> <desc> <p><c><anno>Bytes</anno></c> is the total number of bytes @@ -4514,7 +4514,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="4"/> + <name name="port_info" arity="2" clause_i="4" since=""/> <fsummary>Information about the links of a port.</fsummary> <desc> <p><c><anno>Pids</anno></c> is a list of the process identifiers @@ -4530,7 +4530,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="5"/> + <name name="port_info" arity="2" clause_i="5" since="?"/> <fsummary>Information about the locking of a port.</fsummary> <desc> <p><c><anno>Locking</anno></c> is one of the following:</p> @@ -4551,7 +4551,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="6"/> + <name name="port_info" arity="2" clause_i="6" since="?"/> <fsummary>Information about the memory size of a port.</fsummary> <desc> <p><c><anno>Bytes</anno></c> is the total number of @@ -4569,7 +4569,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="7"/> + <name name="port_info" arity="2" clause_i="7" since="?"/> <fsummary>Information about the monitors of a port.</fsummary> <desc> <p><c><anno>Monitors</anno></c> represent processes monitored by @@ -4585,7 +4585,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="8"/> + <name name="port_info" arity="2" clause_i="8" since="?"/> <fsummary>Which processes are monitoring this port.</fsummary> <desc> <p>Returns list of pids that are monitoring given port at the @@ -4601,7 +4601,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="9"/> + <name name="port_info" arity="2" clause_i="9" since=""/> <fsummary>Information about the name of a port.</fsummary> <desc> <p><c><anno>Name</anno></c> is the command name set by @@ -4617,7 +4617,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="10"/> + <name name="port_info" arity="2" clause_i="10" since="?"/> <fsummary>Information about the OS pid of a port.</fsummary> <desc> <p><c><anno>OsPid</anno></c> is the process identifier (or equivalent) @@ -4636,7 +4636,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="11"/> + <name name="port_info" arity="2" clause_i="11" since=""/> <fsummary>Information about the output of a port.</fsummary> <desc> <p><c><anno>Bytes</anno></c> is the total number of bytes written @@ -4655,7 +4655,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="12"/> + <name name="port_info" arity="2" clause_i="12" since="?"/> <fsummary>Information about the parallelism hint of a port.</fsummary> <desc> <p><c><anno>Boolean</anno></c> corresponds to the port parallelism @@ -4666,7 +4666,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="13"/> + <name name="port_info" arity="2" clause_i="13" since="?"/> <fsummary>Information about the queue size of a port.</fsummary> <desc> <p><c><anno>Bytes</anno></c> is the total number @@ -4683,7 +4683,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_info" arity="2" clause_i="14"/> + <name name="port_info" arity="2" clause_i="14" since=""/> <fsummary>Information about the registered name of a port.</fsummary> <desc> <p><c><anno>RegisteredName</anno></c> is the registered name of @@ -4700,7 +4700,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="port_to_list" arity="1"/> + <name name="port_to_list" arity="1" since=""/> <fsummary>Text representation of a port identifier.</fsummary> <desc> <p>Returns a string corresponding to the text @@ -4709,7 +4709,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="ports" arity="0"/> + <name name="ports" arity="0" since=""/> <fsummary>List all existing ports.</fsummary> <desc> <p>Returns a list of port identifiers corresponding to all the @@ -4719,7 +4719,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="pre_loaded" arity="0"/> + <name name="pre_loaded" arity="0" since=""/> <fsummary>List all preloaded modules.</fsummary> <desc> <p>Returns a list of Erlang modules that are preloaded in @@ -4730,7 +4730,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_display" arity="2"/> + <name name="process_display" arity="2" since=""/> <fsummary>Write information about a local process on standard error. </fsummary> <desc> @@ -4744,7 +4744,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="1"/> + <name name="process_flag" arity="2" clause_i="1" since=""/> <fsummary>Set process flag trap_exit for the calling process.</fsummary> <desc> <p>When <c>trap_exit</c> is set to <c>true</c>, exit signals @@ -4761,7 +4761,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="2"/> + <name name="process_flag" arity="2" clause_i="2" since=""/> <fsummary>Set process flag error_handler for the calling process. </fsummary> <desc> @@ -4776,7 +4776,7 @@ RealSystem = system + MissedSystem</code> <func> <name name="process_flag" arity="2" clause_i="3" - anchor="process_flag_min_heap_size"/> + anchor="process_flag_min_heap_size" since=""/> <fsummary>Set process flag min_heap_size for the calling process. </fsummary> <desc> @@ -4786,7 +4786,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="4"/> + <name name="process_flag" arity="2" clause_i="4" since="?"/> <fsummary>Set process flag min_bin_vheap_size for the calling process. </fsummary> <desc> @@ -4798,7 +4798,7 @@ RealSystem = system + MissedSystem</code> <func> <name name="process_flag" arity="2" clause_i="5" - anchor="process_flag_max_heap_size"/> + anchor="process_flag_max_heap_size" since="?"/> <fsummary>Set process flag max_heap_size for the calling process. </fsummary> <type name="max_heap_size"/> @@ -4872,7 +4872,7 @@ RealSystem = system + MissedSystem</code> <func> <name name="process_flag" arity="2" clause_i="6" - anchor="process_flag_message_queue_data"/> + anchor="process_flag_message_queue_data" since="?"/> <fsummary>Set process flag message_queue_data for the calling process. </fsummary> <type name="message_queue_data"/> @@ -4914,7 +4914,7 @@ RealSystem = system + MissedSystem</code> <func> <name name="process_flag" arity="2" clause_i="7" - anchor="process_flag_priority"/> + anchor="process_flag_priority" since=""/> <fsummary>Set process flag priority for the calling process.</fsummary> <type name="priority_level"/> <desc> @@ -4986,7 +4986,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="8"/> + <name name="process_flag" arity="2" clause_i="8" since=""/> <fsummary>Set process flag save_calls for the calling process.</fsummary> <desc> <p><c><anno>N</anno></c> must be an integer in the interval 0..10000. @@ -5017,7 +5017,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="2" clause_i="9"/> + <name name="process_flag" arity="2" clause_i="9" since=""/> <fsummary>Set process flag sensitive for the calling process.</fsummary> <desc> <p>Sets or clears flag <c>sensitive</c> for the current process. @@ -5051,7 +5051,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_flag" arity="3"/> + <name name="process_flag" arity="3" since="?"/> <fsummary>Set process flags for a process.</fsummary> <desc> <p>Sets certain flags for the process <c><anno>Pid</anno></c>, @@ -5066,7 +5066,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_info" arity="1"/> + <name name="process_info" arity="1" since=""/> <fsummary>Information about a process.</fsummary> <type name="process_info_result_item"/> <type name="priority_level"/> @@ -5117,8 +5117,8 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="process_info" arity="2" clause_i="1"/> - <name name="process_info" arity="2" clause_i="2"/> + <name name="process_info" arity="2" clause_i="1" since=""/> + <name name="process_info" arity="2" clause_i="2" since=""/> <fsummary>Information about a process.</fsummary> <type name="process_info_item"/> <type name="process_info_result_item"/> @@ -5451,7 +5451,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="processes" arity="0"/> + <name name="processes" arity="0" since=""/> <fsummary>All processes.</fsummary> <desc> <p>Returns a list of process identifiers corresponding to @@ -5468,7 +5468,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="purge_module" arity="1"/> + <name name="purge_module" arity="1" since=""/> <fsummary>Remove old code for a module.</fsummary> <desc> <p>Removes old code for <c><anno>Module</anno></c>. @@ -5493,7 +5493,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="put" arity="2"/> + <name name="put" arity="2" since=""/> <fsummary>Add a new value to the process dictionary.</fsummary> <desc> <p>Adds a new <c><anno>Key</anno></c> to the process dictionary, @@ -5515,7 +5515,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="raise" arity="3"/> + <name name="raise" arity="3" since=""/> <fsummary>Stop execution with an exception of specified class, reason, and call stack backtrace.</fsummary> <type name="raise_stacktrace"/> @@ -5554,7 +5554,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="read_timer" arity="1"/> + <name name="read_timer" arity="1" since=""/> <fsummary>Read the state of a timer.</fsummary> <desc> <p>Reads the state of a timer. The same as calling @@ -5564,7 +5564,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="read_timer" arity="2"/> + <name name="read_timer" arity="2" since="OTP 18.0"/> <fsummary>Read the state of a timer.</fsummary> <desc> <p>Reads the state of a timer that has been created by either @@ -5620,7 +5620,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="ref_to_list" arity="1"/> + <name name="ref_to_list" arity="1" since=""/> <fsummary>Text representation of a reference.</fsummary> <desc> <p>Returns a string corresponding to the text @@ -5633,7 +5633,7 @@ RealSystem = system + MissedSystem</code> </func> <func> - <name name="register" arity="2"/> + <name name="register" arity="2" since=""/> <fsummary>Register a name for a pid (or port).</fsummary> <desc> <p>Associates the name <c><anno>RegName</anno></c> with a process @@ -5662,7 +5662,7 @@ true</pre> </func> <func> - <name name="registered" arity="0"/> + <name name="registered" arity="0" since=""/> <fsummary>All registered names.</fsummary> <desc> <p>Returns a list of names that have been registered using @@ -5675,7 +5675,7 @@ true</pre> </func> <func> - <name name="resume_process" arity="1"/> + <name name="resume_process" arity="1" since=""/> <fsummary>Resume a suspended process.</fsummary> <desc> <p>Decreases the suspend count on the process identified by @@ -5716,7 +5716,7 @@ true</pre> </func> <func> - <name name="round" arity="1"/> + <name name="round" arity="1" since=""/> <fsummary>Return an integer by rounding a number.</fsummary> <desc> <p>Returns an integer by rounding <c><anno>Number</anno></c>, @@ -5729,7 +5729,7 @@ true</pre> </func> <func> - <name name="self" arity="0"/> + <name name="self" arity="0" since=""/> <fsummary>Return pid of the calling process.</fsummary> <desc> <p>Returns the process identifier of the calling process, for @@ -5742,7 +5742,7 @@ true</pre> </func> <func> - <name name="send" arity="2"/> + <name name="send" arity="2" since=""/> <fsummary>Send a message.</fsummary> <type name="dst"/> <desc> @@ -5762,7 +5762,7 @@ true</pre> </func> <func> - <name name="send" arity="3"/> + <name name="send" arity="3" since=""/> <fsummary>Send a message conditionally.</fsummary> <type name="dst"/> <desc> @@ -5794,7 +5794,7 @@ true</pre> </func> <func> - <name name="send_after" arity="3"/> + <name name="send_after" arity="3" since=""/> <fsummary>Start a timer.</fsummary> <desc> <p>Starts a timer. The same as calling @@ -5805,7 +5805,7 @@ true</pre> </func> <func> - <name name="send_after" arity="4"/> + <name name="send_after" arity="4" since="OTP 18.0"/> <fsummary>Start a timer.</fsummary> <desc> <p>Starts a timer. When the timer expires, the message @@ -5818,7 +5818,7 @@ true</pre> </func> <func> - <name name="send_nosuspend" arity="2"/> + <name name="send_nosuspend" arity="2" since=""/> <fsummary>Try to send a message without ever blocking.</fsummary> <type name="dst"/> <desc> @@ -5868,7 +5868,7 @@ true</pre> </func> <func> - <name name="send_nosuspend" arity="3"/> + <name name="send_nosuspend" arity="3" since=""/> <fsummary>Try to send a message without ever blocking.</fsummary> <type name="dst"/> <desc> @@ -5901,7 +5901,7 @@ true</pre> </func> <func> - <name name="set_cookie" arity="2"/> + <name name="set_cookie" arity="2" since=""/> <fsummary>Set the magic cookie of a node.</fsummary> <desc> <p>Sets the magic cookie of <c><anno>Node</anno></c> to the atom @@ -5918,7 +5918,7 @@ true</pre> </func> <func> - <name name="setelement" arity="3"/> + <name name="setelement" arity="3" since=""/> <fsummary>Set the Nth element of a tuple.</fsummary> <type_desc variable="Index">1..tuple_size(<anno>Tuple1</anno></type_desc> <desc> @@ -5935,7 +5935,7 @@ true</pre> </func> <func> - <name name="size" arity="1"/> + <name name="size" arity="1" since=""/> <fsummary>Size of a tuple or binary.</fsummary> <desc> <p>Returns the number of elements in a tuple or the number of @@ -5958,7 +5958,7 @@ true</pre> </func> <func> - <name name="spawn" arity="1"/> + <name name="spawn" arity="1" since=""/> <fsummary>Create a new process with a fun as entry point.</fsummary> <desc> <p>Returns the process identifier of a new process started by the @@ -5969,7 +5969,7 @@ true</pre> </func> <func> - <name name="spawn" arity="2"/> + <name name="spawn" arity="2" since=""/> <fsummary>Create a new process with a fun as entry point on a specified node.</fsummary> <desc> @@ -5983,7 +5983,7 @@ true</pre> </func> <func> - <name name="spawn" arity="3"/> + <name name="spawn" arity="3" since=""/> <fsummary>Create a new process with a function as entry point.</fsummary> <desc> <p>Returns the process identifier of a new process started by @@ -6008,7 +6008,7 @@ true</pre> </func> <func> - <name name="spawn" arity="4"/> + <name name="spawn" arity="4" since=""/> <fsummary>Create a new process with a function as entry point on a specified node.</fsummary> <desc> @@ -6023,7 +6023,7 @@ true</pre> </func> <func> - <name name="spawn_link" arity="1"/> + <name name="spawn_link" arity="1" since=""/> <fsummary>Create and link to a new process with a fun as entry point. </fsummary> <desc> @@ -6037,7 +6037,7 @@ true</pre> </func> <func> - <name name="spawn_link" arity="2"/> + <name name="spawn_link" arity="2" since=""/> <fsummary>Create and link to a new process with a fun as entry point on a specified node.</fsummary> <desc> @@ -6054,7 +6054,7 @@ true</pre> </func> <func> - <name name="spawn_link" arity="3"/> + <name name="spawn_link" arity="3" since=""/> <fsummary>Create and link to a new process with a function as entry point. </fsummary> <desc> @@ -6068,7 +6068,7 @@ true</pre> </func> <func> - <name name="spawn_link" arity="4"/> + <name name="spawn_link" arity="4" since=""/> <fsummary>Create and link to a new process with a function as entry point on a specified node.</fsummary> <desc> @@ -6086,7 +6086,7 @@ true</pre> </func> <func> - <name name="spawn_monitor" arity="1"/> + <name name="spawn_monitor" arity="1" since=""/> <fsummary>Create and monitor a new process with a fun as entry point. </fsummary> <desc> @@ -6100,7 +6100,7 @@ true</pre> </func> <func> - <name name="spawn_monitor" arity="3"/> + <name name="spawn_monitor" arity="3" since=""/> <fsummary>Create and monitor a new process with a function as entry point. </fsummary> <desc> @@ -6114,7 +6114,7 @@ true</pre> </func> <func> - <name name="spawn_opt" arity="2"/> + <name name="spawn_opt" arity="2" since=""/> <fsummary>Create a new process with a fun as entry point.</fsummary> <type name="priority_level"/> <type name="max_heap_size"/> @@ -6132,7 +6132,7 @@ true</pre> </func> <func> - <name name="spawn_opt" arity="3"/> + <name name="spawn_opt" arity="3" since=""/> <fsummary>Create a new process with a fun as entry point on a specified node.</fsummary> <type name="priority_level"/> @@ -6150,7 +6150,7 @@ true</pre> </func> <func> - <name name="spawn_opt" arity="4"/> + <name name="spawn_opt" arity="4" since=""/> <fsummary>Create a new process with a function as entry point.</fsummary> <type name="priority_level"/> <type name="max_heap_size"/> @@ -6286,7 +6286,7 @@ true</pre> </func> <func> - <name name="spawn_opt" arity="5"/> + <name name="spawn_opt" arity="5" since=""/> <fsummary>Create a new process with a function as entry point on a specified node.</fsummary> <type name="priority_level"/> @@ -6309,7 +6309,7 @@ true</pre> </func> <func> - <name name="split_binary" arity="2"/> + <name name="split_binary" arity="2" since=""/> <fsummary>Split a binary into two.</fsummary> <type_desc variable="Pos">0..byte_size(Bin)</type_desc> <desc> @@ -6333,7 +6333,7 @@ true</pre> </func> <func> - <name name="start_timer" arity="3"/> + <name name="start_timer" arity="3" since=""/> <fsummary>Start a timer.</fsummary> <desc> <p>Starts a timer. The same as calling @@ -6344,7 +6344,7 @@ true</pre> </func> <func> - <name name="start_timer" arity="4"/> + <name name="start_timer" arity="4" since="OTP 18.0"/> <fsummary>Start a timer.</fsummary> <desc> <p>Starts a timer. When the timer expires, the message @@ -6403,7 +6403,7 @@ true</pre> <func> <name name="statistics" arity="1" clause_i="1" - anchor="statistics_active_tasks"/> + anchor="statistics_active_tasks" since="?"/> <fsummary>Information about active processes and ports.</fsummary> <desc> <p>Returns the same as @@ -6418,7 +6418,7 @@ true</pre> <func> <name name="statistics" arity="1" clause_i="2" - anchor="statistics_active_tasks_all"/> + anchor="statistics_active_tasks_all" since="?"/> <fsummary>Information about active processes and ports.</fsummary> <desc> <p>Returns a list where each element represents the amount @@ -6459,7 +6459,7 @@ true</pre> </func> <func> - <name name="statistics" arity="1" clause_i="3"/> + <name name="statistics" arity="1" clause_i="3" since=""/> <fsummary>Information about context switches.</fsummary> <desc> <p>Returns the total number of context switches since the @@ -6469,7 +6469,7 @@ true</pre> <func> <name name="statistics" arity="1" clause_i="4" - anchor="statistics_exact_reductions"/> + anchor="statistics_exact_reductions" since=""/> <fsummary>Information about exact reductions.</fsummary> <desc> <p>Returns the number of exact reductions.</p> @@ -6483,7 +6483,7 @@ true</pre> </func> <func> - <name name="statistics" arity="1" clause_i="5"/> + <name name="statistics" arity="1" clause_i="5" since=""/> <fsummary>Information about garbage collection.</fsummary> <desc> <p>Returns information about garbage collection, for example:</p> @@ -6495,7 +6495,7 @@ true</pre> </func> <func> - <name name="statistics" arity="1" clause_i="6"/> + <name name="statistics" arity="1" clause_i="6" since=""/> <fsummary>Information about I/O.</fsummary> <desc> <p>Returns <c><anno>Input</anno></c>, @@ -6507,7 +6507,7 @@ true</pre> <func> <name name="statistics" arity="1" clause_i="7" - anchor="statistics_microstate_accounting"/> + anchor="statistics_microstate_accounting" since="?"/> <fsummary>Information about microstate accounting.</fsummary> <desc> <p>Microstate accounting can be used to measure how much time the Erlang @@ -6650,7 +6650,7 @@ lists:map( <func> <name name="statistics" arity="1" clause_i="8" - anchor="statistics_reductions"/> + anchor="statistics_reductions" since=""/> <fsummary>Information about reductions.</fsummary> <desc> <p>Returns information about reductions, for example:</p> @@ -6669,7 +6669,7 @@ lists:map( <func> <name name="statistics" arity="1" clause_i="9" - anchor="statistics_run_queue"/> + anchor="statistics_run_queue" since=""/> <fsummary>Information about the run-queues.</fsummary> <desc> <p>Returns the total length of all normal run-queues. That is, the number @@ -6686,7 +6686,7 @@ lists:map( <func> <name name="statistics" arity="1" clause_i="10" - anchor="statistics_run_queue_lengths"/> + anchor="statistics_run_queue_lengths" since="?"/> <fsummary>Information about the run-queue lengths.</fsummary> <desc> <p>Returns the same as @@ -6701,7 +6701,7 @@ lists:map( <func> <name name="statistics" arity="1" clause_i="11" - anchor="statistics_run_queue_lengths_all"/> + anchor="statistics_run_queue_lengths_all" since="?"/> <fsummary>Information about the run-queue lengths.</fsummary> <desc> <p>Returns a list where each element represents the amount @@ -6743,7 +6743,7 @@ lists:map( </func> <func> - <name name="statistics" arity="1" clause_i="12"/> + <name name="statistics" arity="1" clause_i="12" since=""/> <fsummary>Information about runtime.</fsummary> <desc> <p>Returns information about runtime, in milliseconds.</p> @@ -6762,7 +6762,7 @@ lists:map( <func> <name name="statistics" arity="1" clause_i="13" - anchor="statistics_scheduler_wall_time"/> + anchor="statistics_scheduler_wall_time" since="?"/> <fsummary>Information about each schedulers work time.</fsummary> <desc> <p>Returns a list of tuples with @@ -6886,7 +6886,7 @@ ok <func> <name name="statistics" arity="1" clause_i="14" - anchor="statistics_scheduler_wall_time_all"/> + anchor="statistics_scheduler_wall_time_all" since="?"/> <fsummary>Information about each schedulers work time.</fsummary> <desc> <p>The same as @@ -6914,7 +6914,7 @@ ok </func> <func> <name name="statistics" arity="1" clause_i="15" - anchor="statistics_total_active_tasks"/> + anchor="statistics_total_active_tasks" since="?"/> <fsummary>Information about active processes and ports.</fsummary> <desc> <p>The same as calling @@ -6925,7 +6925,7 @@ ok <func> <name name="statistics" arity="1" clause_i="16" - anchor="statistics_total_active_tasks_all"/> + anchor="statistics_total_active_tasks_all" since="?"/> <fsummary>Information about active processes and ports.</fsummary> <desc> <p>The same as calling @@ -6936,7 +6936,7 @@ ok <func> <name name="statistics" arity="1" clause_i="17" - anchor="statistics_total_run_queue_lengths"/> + anchor="statistics_total_run_queue_lengths" since="?"/> <fsummary>Information about the run-queue lengths.</fsummary> <desc> <p>The same as calling @@ -6947,7 +6947,7 @@ ok <func> <name name="statistics" arity="1" clause_i="18" - anchor="statistics_total_run_queue_lengths_all"/> + anchor="statistics_total_run_queue_lengths_all" since="?"/> <fsummary>Information about the run-queue lengths.</fsummary> <desc> <p>The same as calling @@ -6957,7 +6957,7 @@ ok </func> <func> - <name name="statistics" arity="1" clause_i="19"/> + <name name="statistics" arity="1" clause_i="19" since=""/> <fsummary>Information about wall clock.</fsummary> <desc> <p>Returns information about wall clock. <c>wall_clock</c> can @@ -6968,7 +6968,7 @@ ok </func> <func> - <name name="suspend_process" arity="1"/> + <name name="suspend_process" arity="1" since=""/> <fsummary>Suspend a process.</fsummary> <desc> <p>Suspends the process identified by @@ -6983,7 +6983,7 @@ ok </func> <func> - <name name="suspend_process" arity="2"/> + <name name="suspend_process" arity="2" since=""/> <fsummary>Suspend a process.</fsummary> <desc> <p>Increases the suspend count on the process identified by @@ -7129,7 +7129,7 @@ ok </func> <func> - <name name="system_flag" arity="2" clause_i="1"/> + <name name="system_flag" arity="2" clause_i="1" since=""/> <fsummary>Set system flag <c>backtrace_depth</c>.</fsummary> <desc> <p>Sets the maximum depth of call stack back-traces in the @@ -7142,7 +7142,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="2" - anchor="system_flag_cpu_topology"/> + anchor="system_flag_cpu_topology" since=""/> <fsummary>Set system flag <c>cpu_topology</c>.</fsummary> <type name="cpu_topology"/> <type name="level_entry"/> @@ -7190,7 +7190,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="3" - anchor="system_flag_dirty_cpu_schedulers_online"/> + anchor="system_flag_dirty_cpu_schedulers_online" since="?"/> <fsummary>Set system_flag_dirty_cpu_schedulers_online.</fsummary> <desc> <p> @@ -7218,7 +7218,7 @@ ok </func> <func> - <name name="system_flag" arity="2" clause_i="4"/> + <name name="system_flag" arity="2" clause_i="4" since="?"/> <fsummary>Set system flag for erts_alloc.</fsummary> <desc> <p>Sets system flags for @@ -7235,7 +7235,7 @@ ok </func> <func> - <name name="system_flag" arity="2" clause_i="5"/> + <name name="system_flag" arity="2" clause_i="5" since=""/> <fsummary>Set system flag fullsweep_after.</fsummary> <desc> <p>Sets system flag <c>fullsweep_after</c>. @@ -7255,7 +7255,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="6" - anchor="system_flag_microstate_accounting"/> + anchor="system_flag_microstate_accounting" since="?"/> <fsummary>Set system flag microstate_accounting.</fsummary> <desc> <p> @@ -7268,7 +7268,7 @@ ok </func> <func> - <name name="system_flag" arity="2" clause_i="7"/> + <name name="system_flag" arity="2" clause_i="7" since=""/> <fsummary>Set system flag min_heap_size.</fsummary> <desc> <p>Sets the default minimum heap size for processes. The size @@ -7283,7 +7283,7 @@ ok </func> <func> - <name name="system_flag" arity="2" clause_i="8"/> + <name name="system_flag" arity="2" clause_i="8" since="?"/> <fsummary>Set system flag min_bin_vheap_size.</fsummary> <desc> <p>Sets the default minimum binary virtual heap size for @@ -7301,7 +7301,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="9" - anchor="system_flag_max_heap_size"/> + anchor="system_flag_max_heap_size" since="?"/> <fsummary>Set system flag max_heap_size.</fsummary> <type name="max_heap_size"/> <desc> @@ -7319,7 +7319,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="10" - anchor="system_flag_multi_scheduling"/> + anchor="system_flag_multi_scheduling" since=""/> <fsummary>Set system flag multi_scheduling.</fsummary> <desc> <p> @@ -7375,7 +7375,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="11" - anchor="system_flag_scheduler_bind_type"/> + anchor="system_flag_scheduler_bind_type" since=""/> <fsummary>Set system flag scheduler_bind_type.</fsummary> <type name="scheduler_bind_type"/> <desc> @@ -7502,7 +7502,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="12" - anchor="system_flag_scheduler_wall_time"/> + anchor="system_flag_scheduler_wall_time" since="?"/> <fsummary>Set system flag scheduler_wall_time.</fsummary> <desc> <p> @@ -7515,7 +7515,7 @@ ok <func> <name name="system_flag" arity="2" clause_i="13" - anchor="system_flag_schedulers_online"/> + anchor="system_flag_schedulers_online" since=""/> <fsummary>Set system flag schedulers_online.</fsummary> <desc> <p> @@ -7543,7 +7543,39 @@ ok </func> <func> - <name name="system_flag" arity="2" clause_i="14"/> + <name name="system_flag" arity="2" clause_i="14" since="OTP 21.3"/> + <fsummary>Set system logger process.</fsummary> + <desc> + <p>Sets the process that will receive the logging + messages generated by ERTS. If set to <c>undefined</c>, + all logging messages generated by ERTS will be dropped. + The messages will be in the format:</p> + <code> +{log,Level,Format,ArgList,Metadata} where + +Level = atom(), +Format = string(), +ArgList = list(term()), +Metadata = #{ pid => pid(), + group_leader => pid(), + time := logger:timestamp(), + error_logger := #{ emulator := true, tag := atom() } + </code> + <p>If the <c>system_logger</c> process dies, + this flag will be reset to <c>logger</c>.</p> + <p>The default is the process named <c>logger</c>.</p> + <p>Returns the old value of the flag.</p> + <note><p>This function is designed to be used by the + KERNEL <seealso marker="kernel:logger"><c>logger</c></seealso>. + Be careful if you change it to something else as + log messages may be lost. If you want to intercept + emulator log messages, do it by adding a specialized handler + to the KERNEL logger.</p></note> + </desc> + </func> + + <func> + <name name="system_flag" arity="2" clause_i="15" since=""/> <fsummary>Set system flag trace_control_word.</fsummary> <desc> <p>Sets the value of the node trace control word to @@ -7557,8 +7589,8 @@ ok </func> <func> - <name name="system_flag" arity="2" clause_i="15" - anchor="system_flag_time_offset"/> + <name name="system_flag" arity="2" clause_i="16" + anchor="system_flag_time_offset" since="?"/> <fsummary>Finalize the time offset.</fsummary> <desc> <p> @@ -7587,7 +7619,7 @@ ok </func> <func> - <name name="system_info" arity="1" clause_i="76"/> + <name name="system_info" arity="1" clause_i="76" since=""/> <fsummary>System info overview.</fsummary> <desc> <p>Returns information about the current system. @@ -7707,8 +7739,9 @@ ok <seealso marker="#system_info_nif_version"><c>nif_version</c></seealso>, <seealso marker="#system_info_otp_release"><c>otp_release</c></seealso>, <seealso marker="#system_info_port_parallelism"><c>port_parallelism</c></seealso>, - <seealso marker="#system_info_system_version"><c>system_version</c></seealso>, <seealso marker="#system_info_system_architecture"><c>system_architecture</c></seealso>, + <seealso marker="#system_info_system_logger"><c>system_logger</c></seealso>, + <seealso marker="#system_info_system_version"><c>system_version</c></seealso>, <seealso marker="#system_info_trace_control_word"><c>trace_control_word</c></seealso>, <seealso marker="#system_info_version"><c>version</c></seealso>, <seealso marker="#system_info_wordsize"><c>wordsize</c></seealso> @@ -7720,12 +7753,12 @@ ok <func> <name name="system_info" arity="1" clause_i="1" - anchor="system_info_allocator"/> <!-- allocated_areas --> - <name name="system_info" arity="1" clause_i="2"/> <!-- allocator --> - <name name="system_info" arity="1" clause_i="3"/> <!-- {allocator, _} --> - <name name="system_info" arity="1" clause_i="4"/> <!-- alloc_util_allocators --> - <name name="system_info" arity="1" clause_i="5"/> <!-- {allocator_sizes, _} --> - <name name="system_info" arity="1" clause_i="27"/> <!-- elib_malloc --> + anchor="system_info_allocator" since=""/> <!-- allocated_areas --> + <name name="system_info" arity="1" clause_i="2" since=""/> <!-- allocator --> + <name name="system_info" arity="1" clause_i="3" since=""/> <!-- {allocator, _} --> + <name name="system_info" arity="1" clause_i="4" since=""/> <!-- alloc_util_allocators --> + <name name="system_info" arity="1" clause_i="5" since=""/> <!-- {allocator_sizes, _} --> + <name name="system_info" arity="1" clause_i="27" since=""/> <!-- elib_malloc --> <fsummary>Information about the system allocators.</fsummary> <type variable="Allocator" name_i="2"/> <type variable="Version" name_i="2"/> @@ -7877,10 +7910,10 @@ ok <func> <name name="system_info" arity="1" clause_i="12" - anchor="system_info_cpu_topology"/> <!-- cpu_topology --> - <name name="system_info" arity="1" clause_i="13"/> <!-- {cpu_topology, _} --> - <name name="system_info" arity="1" clause_i="38"/> <!-- logical_processors --> - <name name="system_info" arity="1" clause_i="73"/> <!-- update_cpu_info --> + anchor="system_info_cpu_topology" since=""/> <!-- cpu_topology --> + <name name="system_info" arity="1" clause_i="13" since=""/> <!-- {cpu_topology, _} --> + <name name="system_info" arity="1" clause_i="38" since=""/> <!-- logical_processors --> + <name name="system_info" arity="1" clause_i="74" since="?"/> <!-- update_cpu_info --> <fsummary>Information about the CPU topology of the system.</fsummary> <type name="cpu_topology"/> <type name="level_entry"/> @@ -8032,15 +8065,15 @@ ok <func> <name name="system_info" arity="1" clause_i="31" - anchor="system_info_process"/> <!-- fullsweep_after --> - <name name="system_info" arity="1" clause_i="32"/> <!-- garbage_collection --> - <name name="system_info" arity="1" clause_i="33"/> <!-- heap_sizes --> - <name name="system_info" arity="1" clause_i="34"/> <!-- heap_type --> - <name name="system_info" arity="1" clause_i="40"/> <!-- max_heap_size --> - <name name="system_info" arity="1" clause_i="41"/> <!-- message_queue_data --> - <name name="system_info" arity="1" clause_i="42"/> <!-- min_heap_size --> - <name name="system_info" arity="1" clause_i="43"/> <!-- min_bin_vheap_size --> - <name name="system_info" arity="1" clause_i="57"/> <!-- procs --> + anchor="system_info_process" since="?"/> <!-- fullsweep_after --> + <name name="system_info" arity="1" clause_i="32" since=""/> <!-- garbage_collection --> + <name name="system_info" arity="1" clause_i="33" since=""/> <!-- heap_sizes --> + <name name="system_info" arity="1" clause_i="34" since=""/> <!-- heap_type --> + <name name="system_info" arity="1" clause_i="40" since="?"/> <!-- max_heap_size --> + <name name="system_info" arity="1" clause_i="41" since="?"/> <!-- message_queue_data --> + <name name="system_info" arity="1" clause_i="42" since="?"/> <!-- min_heap_size --> + <name name="system_info" arity="1" clause_i="43" since="?"/> <!-- min_bin_vheap_size --> + <name name="system_info" arity="1" clause_i="57" since=""/> <!-- procs --> <fsummary>Information about the default process heap settings.</fsummary> <type name="message_queue_data"/> <type name="max_heap_size"/> @@ -8150,14 +8183,14 @@ ok </func> <func> - <name name="system_info" arity="1" clause_i="6" anchor="system_info_limits"/> <!-- atom_count --> - <name name="system_info" arity="1" clause_i="7"/> <!-- atom_limit --> - <name name="system_info" arity="1" clause_i="29"/> <!-- ets_count --> - <name name="system_info" arity="1" clause_i="30"/> <!-- ets_limit --> - <name name="system_info" arity="1" clause_i="53"/> <!-- port_count --> - <name name="system_info" arity="1" clause_i="54"/> <!-- port_limit --> - <name name="system_info" arity="1" clause_i="55"/> <!-- process_count --> - <name name="system_info" arity="1" clause_i="56"/> <!-- process_limit --> + <name name="system_info" arity="1" clause_i="6" anchor="system_info_limits" since="?"/> <!-- atom_count --> + <name name="system_info" arity="1" clause_i="7" since="?"/> <!-- atom_limit --> + <name name="system_info" arity="1" clause_i="29" since="?"/> <!-- ets_count --> + <name name="system_info" arity="1" clause_i="30" since="?"/> <!-- ets_limit --> + <name name="system_info" arity="1" clause_i="53" since="?"/> <!-- port_count --> + <name name="system_info" arity="1" clause_i="54" since="?"/> <!-- port_limit --> + <name name="system_info" arity="1" clause_i="55" since=""/> <!-- process_count --> + <name name="system_info" arity="1" clause_i="56" since=""/> <!-- process_limit --> <fsummary>Information about various system limits.</fsummary> <desc> <marker id="system_info_limits"/> @@ -8231,14 +8264,14 @@ ok <func> <name name="system_info" arity="1" clause_i="26" - anchor="system_info_time"/> <!-- end_time --> - <name name="system_info" arity="1" clause_i="50"/> <!-- os_monotonic_time_source --> - <name name="system_info" arity="1" clause_i="51"/> <!-- os_system_time_source --> - <name name="system_info" arity="1" clause_i="63"/> <!-- start_time --> - <name name="system_info" arity="1" clause_i="68"/> <!-- time_correction --> - <name name="system_info" arity="1" clause_i="69"/> <!-- time_offset --> - <name name="system_info" arity="1" clause_i="70"/> <!-- time_warp_mode --> - <name name="system_info" arity="1" clause_i="71"/> <!-- tolerant_timeofday --> + anchor="system_info_time" since="OTP 18.0"/> <!-- end_time --> + <name name="system_info" arity="1" clause_i="50" since="OTP 18.0"/> <!-- os_monotonic_time_source --> + <name name="system_info" arity="1" clause_i="51" since="OTP 18.0"/> <!-- os_system_time_source --> + <name name="system_info" arity="1" clause_i="63" since="OTP 18.0"/> <!-- start_time --> + <name name="system_info" arity="1" clause_i="69" since="OTP 18.0"/> <!-- time_correction --> + <name name="system_info" arity="1" clause_i="70" since="OTP 18.0"/> <!-- time_offset --> + <name name="system_info" arity="1" clause_i="71" since="OTP 18.0"/> <!-- time_warp_mode --> + <name name="system_info" arity="1" clause_i="72" since="?"/> <!-- tolerant_timeofday --> <fsummary>Information about system time.</fsummary> <desc> <marker id="system_info_time_tags"/> @@ -8459,19 +8492,19 @@ ok <func> <name name="system_info" arity="1" clause_i="17" - anchor="system_info_scheduler"/> <!-- dirty_cpu_schedulers --> - <name name="system_info" arity="1" clause_i="18"/> <!-- dirty_cpu_schedulers_online --> - <name name="system_info" arity="1" clause_i="19"/> <!-- dirty_io_schedulers --> - <name name="system_info" arity="1" clause_i="45"/> <!-- multi_scheduling --> - <name name="system_info" arity="1" clause_i="46"/> <!-- multi_scheduling_blockers --> - <name name="system_info" arity="1" clause_i="49"/> <!-- normal_multi_scheduling_blockers --> - <name name="system_info" arity="1" clause_i="58"/> <!-- scheduler_bind_type --> - <name name="system_info" arity="1" clause_i="59"/> <!-- scheduler_bindings --> - <name name="system_info" arity="1" clause_i="60"/> <!-- scheduler_id --> - <name name="system_info" arity="1" clause_i="61"/> <!-- schedulers --> - <name name="system_info" arity="1" clause_i="62"/> <!-- smp_support --> - <name name="system_info" arity="1" clause_i="66"/> <!-- threads --> - <name name="system_info" arity="1" clause_i="67"/> <!-- thread_pool_size --> + anchor="system_info_scheduler" since="?"/> <!-- dirty_cpu_schedulers --> + <name name="system_info" arity="1" clause_i="18" since="?"/> <!-- dirty_cpu_schedulers_online --> + <name name="system_info" arity="1" clause_i="19" since="?"/> <!-- dirty_io_schedulers --> + <name name="system_info" arity="1" clause_i="45" since=""/> <!-- multi_scheduling --> + <name name="system_info" arity="1" clause_i="46" since=""/> <!-- multi_scheduling_blockers --> + <name name="system_info" arity="1" clause_i="49" since="?"/> <!-- normal_multi_scheduling_blockers --> + <name name="system_info" arity="1" clause_i="58" since=""/> <!-- scheduler_bind_type --> + <name name="system_info" arity="1" clause_i="59" since=""/> <!-- scheduler_bindings --> + <name name="system_info" arity="1" clause_i="60" since=""/> <!-- scheduler_id --> + <name name="system_info" arity="1" clause_i="61" since=""/> <!-- schedulers --> + <name name="system_info" arity="1" clause_i="62" since=""/> <!-- smp_support --> + <name name="system_info" arity="1" clause_i="67" since=""/> <!-- threads --> + <name name="system_info" arity="1" clause_i="68" since=""/> <!-- thread_pool_size --> <fsummary>Information about system schedulers.</fsummary> <desc> <marker id="system_info_scheduler_tags"/> @@ -8759,11 +8792,11 @@ ok <func> <name name="system_info" arity="1" clause_i="14" - anchor="system_info_dist"/> <!-- creation --> - <name name="system_info" arity="1" clause_i="16"/> <!-- delayed_node_table_gc --> - <name name="system_info" arity="1" clause_i="20"/> <!-- dist --> - <name name="system_info" arity="1" clause_i="21"/> <!-- dist_buf_busy_limit --> - <name name="system_info" arity="1" clause_i="22"/> <!-- dist_ctrl --> + anchor="system_info_dist" since=""/> <!-- creation --> + <name name="system_info" arity="1" clause_i="16" since="?"/> <!-- delayed_node_table_gc --> + <name name="system_info" arity="1" clause_i="20" since=""/> <!-- dist --> + <name name="system_info" arity="1" clause_i="21" since="?"/> <!-- dist_buf_busy_limit --> + <name name="system_info" arity="1" clause_i="22" since=""/> <!-- dist_ctrl --> <fsummary>Information about erlang distribution.</fsummary> <desc> <marker id="system_info_dist_tags"/> @@ -8837,14 +8870,14 @@ ok <!-- <name name="system_info" arity="1" clause_i="6"/> atom_count --> <!-- <name name="system_info" arity="1" clause_i="7"/> atom_limit --> <name name="system_info" arity="1" clause_i="8" - anchor="system_info_misc"/> <!-- build_type --> - <name name="system_info" arity="1" clause_i="9"/> <!-- c_compiler_used --> - <name name="system_info" arity="1" clause_i="10"/> <!-- check_io --> - <name name="system_info" arity="1" clause_i="11"/> <!-- compat_rel --> + anchor="system_info_misc" since="?"/> <!-- build_type --> + <name name="system_info" arity="1" clause_i="9" since=""/> <!-- c_compiler_used --> + <name name="system_info" arity="1" clause_i="10" since=""/> <!-- check_io --> + <name name="system_info" arity="1" clause_i="11" since=""/> <!-- compat_rel --> <!-- <name name="system_info" arity="1" clause_i="12"/> cpu_topology --> <!-- <name name="system_info" arity="1" clause_i="13"/> {cpu_topology, _} --> <!-- <name name="system_info" arity="1" clause_i="14"/> creation --> - <name name="system_info" arity="1" clause_i="15"/> <!-- debug_compiled --> + <name name="system_info" arity="1" clause_i="15" since=""/> <!-- debug_compiled --> <!-- <name name="system_info" arity="1" clause_i="16"/> delayed_node_table_gc --> <!-- <name name="system_info" arity="1" clause_i="17"/> dirty_cpu_schedulers --> <!-- <name name="system_info" arity="1" clause_i="18"/> dirty_cpu_schedulers_online --> @@ -8852,9 +8885,9 @@ ok <!-- <name name="system_info" arity="1" clause_i="20"/> dist --> <!-- <name name="system_info" arity="1" clause_i="21"/> dist_buf_busy_limit --> <!-- <name name="system_info" arity="1" clause_i="22"/> dist_ctrl --> - <name name="system_info" arity="1" clause_i="23"/> <!-- driver_version --> - <name name="system_info" arity="1" clause_i="24"/> <!-- dynamic_trace --> - <name name="system_info" arity="1" clause_i="25"/> <!-- dynamic_trace_probes --> + <name name="system_info" arity="1" clause_i="23" since=""/> <!-- driver_version --> + <name name="system_info" arity="1" clause_i="24" since="?"/> <!-- dynamic_trace --> + <name name="system_info" arity="1" clause_i="25" since="?"/> <!-- dynamic_trace_probes --> <!-- <name name="system_info" arity="1" clause_i="26"/> end_time --> <!-- <name name="system_info" arity="1" clause_i="27"/> elib_malloc --> <!-- <name name="system_info" arity="1" clause_i="28"/> eager_check_io, removed --> @@ -8864,24 +8897,24 @@ ok <!-- <name name="system_info" arity="1" clause_i="32"/> garbage_collection --> <!-- <name name="system_info" arity="1" clause_i="33"/> heap_sizes --> <!-- <name name="system_info" arity="1" clause_i="34"/> heap_type --> - <name name="system_info" arity="1" clause_i="35"/> <!-- info --> - <name name="system_info" arity="1" clause_i="36"/> <!-- kernel_poll --> - <name name="system_info" arity="1" clause_i="37"/> <!-- loaded --> + <name name="system_info" arity="1" clause_i="35" since=""/> <!-- info --> + <name name="system_info" arity="1" clause_i="36" since=""/> <!-- kernel_poll --> + <name name="system_info" arity="1" clause_i="37" since=""/> <!-- loaded --> <!-- <name name="system_info" arity="1" clause_i="38"/> logical_processors --> - <name name="system_info" arity="1" clause_i="39"/> <!-- machine --> + <name name="system_info" arity="1" clause_i="39" since=""/> <!-- machine --> <!-- <name name="system_info" arity="1" clause_i="40"/> max_heap_size --> <!-- <name name="system_info" arity="1" clause_i="41"/> message_queue_data --> <!-- <name name="system_info" arity="1" clause_i="42"/> min_heap_size --> <!-- <name name="system_info" arity="1" clause_i="43"/> min_bin_vheap_size --> - <name name="system_info" arity="1" clause_i="44"/> <!-- modified_timing_level --> + <name name="system_info" arity="1" clause_i="44" since=""/> <!-- modified_timing_level --> <!-- <name name="system_info" arity="1" clause_i="45"/> multi_scheduling --> <!-- <name name="system_info" arity="1" clause_i="46"/> multi_scheduling_blockers --> - <name name="system_info" arity="1" clause_i="47"/> <!-- nif_version --> + <name name="system_info" arity="1" clause_i="47" since="?"/> <!-- nif_version --> <!-- n<name name="system_info" arity="1" clause_i="48"/> ormal_multi_scheduling_blockers --> - <name name="system_info" arity="1" clause_i="49"/> <!-- otp_release --> + <name name="system_info" arity="1" clause_i="49" since=""/> <!-- otp_release --> <!-- <name name="system_info" arity="1" clause_i="50"/> os_monotonic_time_source --> <!-- <name name="system_info" arity="1" clause_i="51"/> os_system_time_source --> - <name name="system_info" arity="1" clause_i="52"/> <!-- port_parallelism --> + <name name="system_info" arity="1" clause_i="52" since="?"/> <!-- port_parallelism --> <!-- <name name="system_info" arity="1" clause_i="53"/> port_count --> <!-- <name name="system_info" arity="1" clause_i="54"/> port_limit --> <!-- <name name="system_info" arity="1" clause_i="55"/> process_count --> @@ -8893,19 +8926,20 @@ ok <!-- <name name="system_info" arity="1" clause_i="61"/> schedulers --> <!-- <name name="system_info" arity="1" clause_i="62"/> smp_support --> <!-- <name name="system_info" arity="1" clause_i="63"/> start_time --> - <name name="system_info" arity="1" clause_i="64"/> <!-- system_version --> - <name name="system_info" arity="1" clause_i="65"/> <!-- system_architecture --> - <!-- <name name="system_info" arity="1" clause_i="66"/> threads --> - <!-- <name name="system_info" arity="1" clause_i="67"/> thread_pool_size --> - <!-- <name name="system_info" arity="1" clause_i="68"/> time_correction --> - <!-- <name name="system_info" arity="1" clause_i="69"/> time_offset --> - <!-- <name name="system_info" arity="1" clause_i="70"/> time_warp_mode --> - <!-- <name name="system_info" arity="1" clause_i="71"/> tolerant_timeofday --> - <name name="system_info" arity="1" clause_i="72"/> <!-- trace_control_word --> - <!-- <name name="system_info" arity="1" clause_i="73"/> update_cpu_info --> - <name name="system_info" arity="1" clause_i="74"/> <!-- version --> - <name name="system_info" arity="1" clause_i="75"/> <!-- wordsize --> - <!-- <name name="system_info" arity="1" clause_i="76"/> overview --> + <name name="system_info" arity="1" clause_i="64" since=""/> <!-- system_architecture --> + <name name="system_info" arity="1" clause_i="65" since="OTP 21.3"/> <!-- system_logger --> + <name name="system_info" arity="1" clause_i="66" since=""/> <!-- system_version --> + <!-- <name name="system_info" arity="1" clause_i="67"/> threads --> + <!-- <name name="system_info" arity="1" clause_i="68"/> thread_pool_size --> + <!-- <name name="system_info" arity="1" clause_i="69"/> time_correction --> + <!-- <name name="system_info" arity="1" clause_i="70"/> time_offset --> + <!-- <name name="system_info" arity="1" clause_i="71"/> time_warp_mode --> + <!-- <name name="system_info" arity="1" clause_i="72"/> tolerant_timeofday --> + <name name="system_info" arity="1" clause_i="73" since=""/> <!-- trace_control_word --> + <!-- <name name="system_info" arity="1" clause_i="74"/> update_cpu_info --> + <name name="system_info" arity="1" clause_i="75" since=""/> <!-- version --> + <name name="system_info" arity="1" clause_i="76" since=""/> <!-- wordsize --> + <!-- <name name="system_info" arity="1" clause_i="77"/> overview --> <fsummary>Information about the system.</fsummary> <desc> <marker id="system_info_misc_tags"/> @@ -9061,18 +9095,24 @@ ok <seealso marker="erl#+spp"><c>+spp</c></seealso> in <c>erl(1)</c>.</p> </item> - <tag><marker id="system_info_system_version"/> - <c>system_version</c></tag> - <item> - <p>Returns a string containing version number and - some important properties, such as the number of schedulers.</p> - </item> <tag><marker id="system_info_system_architecture"/> <c>system_architecture</c></tag> <item> <p>Returns a string containing the processor and OS architecture the emulator is built for.</p> </item> + <tag><marker id="system_info_system_logger"/> + <c>system_logger</c></tag> + <item> + <p>Returns the current <c>system_logger</c> as set by + <seealso marker="#system_flag/2"><c>erlang:system_flag(system_logger, _)</c></seealso>.</p> + </item> + <tag><marker id="system_info_system_version"/> + <c>system_version</c></tag> + <item> + <p>Returns a string containing version number and + some important properties, such as the number of schedulers.</p> + </item> <tag><marker id="system_info_trace_control_word"/> <c>trace_control_word</c></tag> <item> @@ -9114,7 +9154,7 @@ ok </func> <func> - <name name="system_monitor" arity="0"/> + <name name="system_monitor" arity="0" since=""/> <fsummary>Current system performance monitoring settings.</fsummary> <type name="system_monitor_option"/> <desc> @@ -9128,7 +9168,7 @@ ok </func> <func> - <name name="system_monitor" arity="1"/> + <name name="system_monitor" arity="1" since=""/> <fsummary>Set or clear system performance monitoring options.</fsummary> <type name="system_monitor_option"/> <desc> @@ -9146,7 +9186,7 @@ ok </func> <func> - <name name="system_monitor" arity="2"/> + <name name="system_monitor" arity="2" since=""/> <fsummary>Set system performance monitoring options.</fsummary> <type name="system_monitor_option"/> <desc> @@ -9278,7 +9318,7 @@ ok </func> <func> - <name name="system_profile" arity="0"/> + <name name="system_profile" arity="0" since=""/> <fsummary>Current system profiling settings.</fsummary> <type name="system_profile_option"/> <desc> @@ -9293,7 +9333,7 @@ ok </func> <func> - <name name="system_profile" arity="2"/> + <name name="system_profile" arity="2" since=""/> <fsummary>Current system profiling settings.</fsummary> <type name="system_profile_option"/> <desc> @@ -9367,7 +9407,7 @@ ok </func> <func> - <name name="system_time" arity="0"/> + <name name="system_time" arity="0" since="OTP 18.0"/> <fsummary>Current Erlang system time.</fsummary> <desc> <p>Returns current @@ -9389,7 +9429,7 @@ ok </func> <func> - <name name="system_time" arity="1"/> + <name name="system_time" arity="1" since="OTP 18.0"/> <fsummary>Current Erlang system time.</fsummary> <desc> <p>Returns current @@ -9411,7 +9451,7 @@ ok </func> <func> - <name name="term_to_binary" arity="1"/> + <name name="term_to_binary" arity="1" since=""/> <fsummary>Encode a term to an Erlang external term format binary. </fsummary> <desc> @@ -9439,7 +9479,7 @@ hello </func> <func> - <name name="term_to_binary" arity="2"/> + <name name="term_to_binary" arity="2" since=""/> <fsummary>Encode a term to en Erlang external term format binary. </fsummary> <desc> @@ -9504,7 +9544,7 @@ hello </func> <func> - <name name="throw" arity="1"/> + <name name="throw" arity="1" since=""/> <fsummary>Throw an exception.</fsummary> <desc> <p>A non-local return from a function. If evaluated within a @@ -9518,7 +9558,7 @@ hello </func> <func> - <name name="time" arity="0"/> + <name name="time" arity="0" since=""/> <fsummary>Current time.</fsummary> <desc> <p>Returns the current time as <c>{Hour, Minute, Second}</c>.</p> @@ -9531,7 +9571,7 @@ hello </func> <func> - <name name="time_offset" arity="0"/> + <name name="time_offset" arity="0" since="OTP 18.0"/> <fsummary>Current time offset.</fsummary> <desc> <p>Returns the current time offset between @@ -9563,7 +9603,7 @@ hello </func> <func> - <name name="time_offset" arity="1"/> + <name name="time_offset" arity="1" since="OTP 18.0"/> <fsummary>Current time offset.</fsummary> <desc> <p>Returns the current time offset between @@ -9582,7 +9622,7 @@ hello </func> <func> - <name name="timestamp" arity="0"/> + <name name="timestamp" arity="0" since="OTP 18.0"/> <fsummary>Current Erlang System time.</fsummary> <type name="timestamp"/> <desc> @@ -9621,7 +9661,7 @@ timestamp() -> </func> <func> - <name name="tl" arity="1"/> + <name name="tl" arity="1" since=""/> <fsummary>Tail of a list.</fsummary> <desc> <p>Returns the tail of <c><anno>List</anno></c>, that is, @@ -9636,7 +9676,7 @@ timestamp() -> </func> <func> - <name name="trace" arity="3"/> + <name name="trace" arity="3" since=""/> <fsummary>Set trace flags for a process or processes.</fsummary> <type name="trace_flag"/> <desc> @@ -10292,7 +10332,7 @@ timestamp() -> </func> <func> - <name name="trace_delivered" arity="1"/> + <name name="trace_delivered" arity="1" since=""/> <fsummary>Notification when trace has been delivered.</fsummary> <desc> <p>The delivery of trace messages (generated by @@ -10347,7 +10387,7 @@ timestamp() -> </func> <func> - <name name="trace_info" arity="2"/> + <name name="trace_info" arity="2" since=""/> <fsummary>Trace information about a process or function.</fsummary> <type name="trace_info_return"/> <type name="trace_info_item_result"/> @@ -10483,7 +10523,7 @@ timestamp() -> </func> <func> - <name name="trace_pattern" arity="2" clause_i="1"/> + <name name="trace_pattern" arity="2" clause_i="1" since=""/> <fsummary>Set trace patterns for call, send, or 'receive' tracing. </fsummary> <type name="trace_pattern_mfa"/> @@ -10501,7 +10541,7 @@ timestamp() -> </func> <func> - <name name="trace_pattern" arity="3" clause_i="1"/> + <name name="trace_pattern" arity="3" clause_i="1" since="?"/> <fsummary>Set trace pattern for message sending.</fsummary> <type name="trace_match_spec"/> <type name="match_variable"/> @@ -10572,7 +10612,7 @@ timestamp() -> </func> <func> - <name name="trace_pattern" arity="3" clause_i="2"/> + <name name="trace_pattern" arity="3" clause_i="2" since="?"/> <fsummary>Set trace pattern for tracing of message receiving.</fsummary> <type name="trace_match_spec"/> <type name="match_variable"/> @@ -10644,7 +10684,7 @@ timestamp() -> </func> <func> - <name name="trace_pattern" arity="3" clause_i="3"/> + <name name="trace_pattern" arity="3" clause_i="3" since=""/> <fsummary>Set trace patterns for tracing of function calls.</fsummary> <type name="trace_pattern_mfa"/> <type name="trace_match_spec"/> @@ -10835,7 +10875,7 @@ timestamp() -> </func> <func> - <name name="trunc" arity="1"/> + <name name="trunc" arity="1" since=""/> <fsummary>Return an integer by truncating a number.</fsummary> <desc> <p>Returns an integer by truncating <c><anno>Number</anno></c>, @@ -10848,7 +10888,7 @@ timestamp() -> </func> <func> - <name name="tuple_size" arity="1"/> + <name name="tuple_size" arity="1" since=""/> <fsummary>Return the size of a tuple.</fsummary> <desc> <p>Returns an integer that is the number of elements in @@ -10861,7 +10901,7 @@ timestamp() -> </func> <func> - <name name="tuple_to_list" arity="1"/> + <name name="tuple_to_list" arity="1" since=""/> <fsummary>Convert a tuple to a list.</fsummary> <desc> <p>Returns a list corresponding to <c><anno>Tuple</anno></c>. @@ -10874,7 +10914,7 @@ timestamp() -> </func> <func> - <name name="unique_integer" arity="0"/> + <name name="unique_integer" arity="0" since="OTP 18.0"/> <fsummary>Get a unique integer value.</fsummary> <desc> <p>Generates and returns an @@ -10887,7 +10927,7 @@ timestamp() -> </func> <func> - <name name="unique_integer" arity="1"/> + <name name="unique_integer" arity="1" since="OTP 18.0"/> <fsummary>Get a unique integer value.</fsummary> <desc> <p>Generates and returns an @@ -10969,7 +11009,7 @@ timestamp() -> </func> <func> - <name name="universaltime" arity="0"/> + <name name="universaltime" arity="0" since=""/> <fsummary>Current date and time according to Universal Time Coordinated (UTC).</fsummary> <desc> @@ -10986,7 +11026,7 @@ timestamp() -> </func> <func> - <name name="universaltime_to_localtime" arity="1"/> + <name name="universaltime_to_localtime" arity="1" since=""/> <fsummary>Convert from Universal Time Coordinated (UTC) to local date and time.</fsummary> <desc> @@ -11005,7 +11045,7 @@ timestamp() -> </func> <func> - <name name="unlink" arity="1"/> + <name name="unlink" arity="1" since=""/> <fsummary>Remove a link to another process or port.</fsummary> <desc> <p>Removes the link, if there is one, between the calling @@ -11051,7 +11091,7 @@ end</code> </func> <func> - <name name="unregister" arity="1"/> + <name name="unregister" arity="1" since=""/> <fsummary>Remove the registered name for a process (or port).</fsummary> <desc> <p>Removes the registered name <c><anno>RegName</anno></c> @@ -11067,7 +11107,7 @@ true</pre> </func> <func> - <name name="whereis" arity="1"/> + <name name="whereis" arity="1" since=""/> <fsummary>Get the pid (or port) with a specified registered name. </fsummary> <desc> @@ -11081,7 +11121,7 @@ true</pre> </func> <func> - <name name="yield" arity="0"/> + <name name="yield" arity="0" since=""/> <fsummary>Let other processes get a chance to execute.</fsummary> <desc> <p>Voluntarily lets other processes (if any) get a chance to diff --git a/erts/doc/src/init.xml b/erts/doc/src/init.xml index c14f0a558d..c824e37976 100644 --- a/erts/doc/src/init.xml +++ b/erts/doc/src/init.xml @@ -29,7 +29,7 @@ <rev></rev> <file>init.xml</file> </header> - <module>init</module> + <module since="">init</module> <modulesummary>Coordination of system startup.</modulesummary> <description> <p>This module is preloaded and contains the code for @@ -50,7 +50,7 @@ <funcs> <func> - <name name="boot" arity="1"/> + <name name="boot" arity="1" since=""/> <fsummary>Start the Erlang runtime system.</fsummary> <desc> <p>Starts the Erlang runtime system. This function is called @@ -69,7 +69,7 @@ </func> <func> - <name name="get_argument" arity="1"/> + <name name="get_argument" arity="1" since=""/> <fsummary>Get the values associated with a command-line user flag. </fsummary> <desc> @@ -112,7 +112,7 @@ </func> <func> - <name name="get_arguments" arity="0"/> + <name name="get_arguments" arity="0" since=""/> <fsummary>Get all command-line user flags.</fsummary> <desc> <p>Returns all command-line flags and the system-defined flags, see @@ -121,7 +121,7 @@ </func> <func> - <name name="get_plain_arguments" arity="0"/> + <name name="get_plain_arguments" arity="0" since=""/> <fsummary>Get all non-flag command-line arguments.</fsummary> <desc> <p>Returns any plain command-line arguments as a list of strings @@ -130,7 +130,7 @@ </func> <func> - <name name="get_status" arity="0"/> + <name name="get_status" arity="0" since=""/> <fsummary>Get system status information.</fsummary> <type name="internal_status"/> <desc> @@ -146,7 +146,7 @@ </func> <func> - <name name="reboot" arity="0"/> + <name name="reboot" arity="0" since=""/> <fsummary>Take down and restart an Erlang node smoothly.</fsummary> <desc> <p>All applications are taken down smoothly, all code is @@ -162,7 +162,7 @@ </func> <func> - <name name="restart" arity="0"/> + <name name="restart" arity="0" since=""/> <fsummary>Restart the running Erlang node.</fsummary> <desc> <p>The system is restarted <em>inside</em> the running Erlang @@ -178,7 +178,7 @@ </func> <func> - <name name="script_id" arity="0"/> + <name name="script_id" arity="0" since=""/> <fsummary>Get the identity of the used boot script.</fsummary> <desc> <p>Gets the identity of the boot script used to boot the system. @@ -189,7 +189,7 @@ </func> <func> - <name name="stop" arity="0"/> + <name name="stop" arity="0" since=""/> <fsummary>Take down an Erlang node smoothly.</fsummary> <desc> <p>The same as @@ -198,7 +198,7 @@ </func> <func> - <name name="stop" arity="1"/> + <name name="stop" arity="1" since=""/> <fsummary>Take down an Erlang node smoothly.</fsummary> <desc> <p>All applications are taken down smoothly, all code is diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index ad31362064..8324871626 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -31,6 +31,262 @@ </header> <p>This document describes the changes made to the ERTS application.</p> +<section><title>Erts 10.2.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Fixed a crash when dangling files were closed after + <c>init:restart/0</c>.</p> + <p> + Own Id: OTP-15495 Aux Id: ERL-821 </p> + </item> + <item> + <p> + A bug that could cause dirty schedulers to become + unresponsive has been fixed.</p> + <p> + Own Id: OTP-15509 Aux Id: PR-2027, PR-2093 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 10.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed bug on big endian architectures when changing file + permissions or ownership with <c>file:change_mode</c>, + <c>change_owner</c>, <c>change_group</c> or + <c>write_file_info</c>. Bug exists since OTP-21.0.</p> + <p> + Own Id: OTP-15485</p> + </item> + <item> + <p> + Fixed bug in <c>atomics</c> with option + <c>{signed,false}</c> when returned values are <c>(1 bsl + 63)</c> or larger. Could cause heap corruption leading to + VM crash or other unpleasant symptoms. Bug exists since + OTP-21.2 when module <c>atomics</c> was introduced.</p> + <p> + Own Id: OTP-15486 Aux Id: PR-2061 </p> + </item> + <item> + <p> + Fixed bug in operator <c>band</c> of two negative + operands causing erroneous result if the absolute value + of one of the operands have the lowest <c>N*W</c> bits as + zero and the other absolute value is not larger than + <c>N*W</c> bits. <c>N</c> is an integer of 1 or larger + and <c>W</c> is 32 or 64 depending on word size.</p> + <p> + Own Id: OTP-15487 Aux Id: ERL-804 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 10.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + When a process was waiting for a TCP socket send + operation to complete, and another process closed the + socket during that send, the sending process could hang. + This bug has now been corrected.</p> + <p> + Own Id: OTP-12242 Aux Id: ERL-561 </p> + </item> + <item> + <p> + Document <c>bit_size</c> in match specifications and + allow it in <c>ets:fun2ms</c>.</p> + <p> + Own Id: OTP-15343 Aux Id: PR-1962 </p> + </item> + <item> + <p> + Fixed bug in <c>ets:select_replace</c> when called with a + fully bound key could cause a following call to + <c>ets:next</c> or <c>ets:prev</c> to crash the emulator + or return invalid result.</p> + <p> + Own Id: OTP-15346</p> + </item> + <item> + <p>When a module has been purged from memory, any + literals belonging to that module will be copied to all + processes that hold references to them. The max heap size + limit would be ignored in the garbage collection + initiated when copying literals to a process. If the max + heap size was exceeded, the process would typically be + terminated in the following garbage collection. Corrected + to terminate the process directly if copying a literal + would exceed the max heap size.</p> + <p> + Own Id: OTP-15360</p> + </item> + <item> + <p> + Fix compilation of run_erl on Solaris 11.4 and later.</p> + <p> + Own Id: OTP-15389</p> + </item> + <item> + <p>Fixed a bug where <c>lists:reverse/1-2</c> could use + far too many reductions. This bug was introduced in + <c>OTP 21.1</c>.</p> + <p> + Own Id: OTP-15436</p> + </item> + <item> + <p>Fixed a bug where a dirty scheduler could stay awake + forever if a distribution entry was removed as part of a + dirty GC.</p> + <p> + Own Id: OTP-15446 Aux Id: PR-2024 </p> + </item> + <item> + <p> + Fix microstate accounting handing in various places. Most + importantly the GC states when the GC is run on a dirty + scheduler are now managed correctly.</p> + <p> + Own Id: OTP-15450 Aux Id: ERIERL-229 </p> + </item> + <item> + <p> + Fixed bug in <c>file:sendfile</c> when the send operation + failed. For sockets in <c>active</c> modes it could cause + emulator crash or a hanging call. For sockets with + <c>{active,false}</c> an unexpected <c>{inet_reply, _, + _}</c> message could be sent to the calling process. The + bug exists since OTP-21.0.</p> + <p> + Own Id: OTP-15461 Aux Id: ERL-784 </p> + </item> + <item> + <p> + The erts configure script has been updated to reject any + CFLAGS that does not have <c>-O</c>. This in order to + prevent the common mistake of forgetting to add + <c>-O2</c> to custom CFLAGS.</p> + <p> + Own Id: OTP-15465</p> + </item> + <item> + <p> + Fix reduction count in lists:member/2</p> + <p> + Own Id: OTP-15474 Aux Id: ERIERL-229 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + New <c>counters</c> and <c>atomics</c> modules supplies + access to highly efficient operations on mutable fixed + word sized variables.</p> + <p> + Own Id: OTP-13468</p> + </item> + <item> + <p>There is a new module <c>persistent_term</c> that + implements a term storage suitable for terms that are + frequently used but never or infrequently updated. + Lookups are done in constant time without copying the + terms.</p> + <p> + Own Id: OTP-14669 Aux Id: PR-1989 </p> + </item> + <item> + <p> + A function <c>inet:getifaddrs/1</c> that takes a list + with a namespace option has been added, for platforms + that support that feature, for example Linux (only?).</p> + <p> + Own Id: OTP-15121 Aux Id: ERIERL-189, PR-1974 </p> + </item> + <item> + <p>Added the <c>nopush</c> option for TCP sockets, which + corresponds to <c>TCP_NOPUSH</c> on *BSD and + <c>TCP_CORK</c> on Linux.</p> + <p>This is also used internally in <c>file:sendfile</c> + to reduce latency on subsequent send operations.</p> + <p> + Own Id: OTP-15357 Aux Id: ERL-698 </p> + </item> + <item> + <p>List subtraction (The <c>--</c> operator) will now + yield properly on large inputs.</p> + <p> + Own Id: OTP-15371</p> + </item> + <item> + <p> + Optimize handling of send_delay for tcp sockes to better + work with the new pollthread implementation introduced in + OTP-21.</p> + <p> + Own Id: OTP-15471 Aux Id: ERIERL-229 </p> + </item> + <item> + <p> + Optimize driver_set_timer with a zero timeout to + short-circuit and not create any timer structure, but + instead schedule the timer immediately.</p> + <p> + Own Id: OTP-15472 Aux Id: ERIERL-229 </p> + </item> + <item> + <p> + Add <c>erl_xcomp_code_model_small</c> as a cross + configure variable in order to let the emulator be build + with the assumption that a small code model will be used + on the target machine.</p> + <p> + Own Id: OTP-15473 Aux Id: ERIERL-229 </p> + </item> + <item> + <p> + Add a new pollset that is made to handle sockets that use + <c>{active, true}</c> or <c>{active, N}</c>. The new + pollset will not be polled by a pollthread, but instead + polled by a normal scheduler.</p> + <p> + This change was made because of the overhead associated + with constantly having to re-apply the ONESHOT mechanism + on fds that all input events were interesting.</p> + <p> + The new pollset is only active on platforms that support + concurrent kernel poll updates, i.e. Linux and BSD.</p> + <p> + Own Id: OTP-15475 Aux Id: ERIERL-229 </p> + </item> + <item> + <p> + Fix bug where emulator would segfault if a literal + message was sent when sequence tracing was enabled.</p> + <p> + Own Id: OTP-15478 Aux Id: ERL-741 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 10.1.3</title> <section><title>Improvements and New Features</title> @@ -1391,6 +1647,42 @@ </section> +<section><title>Erts 9.3.3.8</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A bug that could cause dirty schedulers to become + unresponsive has been fixed.</p> + <p> + Own Id: OTP-15509 Aux Id: PR-2027, PR-2093 </p> + </item> + </list> + </section> + +</section> + +<section><title>Erts 9.3.3.7</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed bug in operator <c>band</c> of two negative + operands causing erroneous result if the absolute value + of one of the operands have the lowest <c>N*W</c> bits as + zero and the other absolute value is not larger than + <c>N*W</c> bits. <c>N</c> is an integer of 1 or larger + and <c>W</c> is 32 or 64 depending on word size.</p> + <p> + Own Id: OTP-15487 Aux Id: ERL-804 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 9.3.3.6</title> <section><title>Improvements and New Features</title> @@ -1752,6 +2044,22 @@ </section> +<section><title>Erts 9.2.0.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Extra internal consistency checks wrt communication with + erl_child_setup process.</p> + <p> + Own Id: OTP-15488 Aux Id: ERIERL-231 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 9.2</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -2950,6 +3258,43 @@ </section> +<section><title>Erts 8.3.5.7</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed bug in operator <c>band</c> of two negative + operands causing erroneous result if the absolute value + of one of the operands have the lowest <c>N*W</c> bits as + zero and the other absolute value is not larger than + <c>N*W</c> bits. <c>N</c> is an integer of 1 or larger + and <c>W</c> is 32 or 64 depending on word size.</p> + <p> + Own Id: OTP-15487 Aux Id: ERL-804 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>Added an optional <c>./configure</c> flag to compile + the emulator with spectre mitigation: + <c>--with-spectre-mitigation</c></p> + <p>Note that this requires a recent version of GCC with + support for spectre mitigation and the + <c>--mindirect-branch=thunk</c> flag, such as + <c>8.1</c>.</p> + <p> + Own Id: OTP-15430 Aux Id: ERIERL-237 </p> + </item> + </list> + </section> + +</section> + <section><title>Erts 8.3.5.6</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/doc/src/persistent_term.xml b/erts/doc/src/persistent_term.xml index 29a6c67051..1eda7f8d76 100644 --- a/erts/doc/src/persistent_term.xml +++ b/erts/doc/src/persistent_term.xml @@ -29,7 +29,7 @@ <rev></rev> <file>persistent_term.xml</file> </header> - <module>persistent_term</module> + <module since="OTP 21.2">persistent_term</module> <modulesummary>Persistent terms.</modulesummary> <description> <p>This module is similar to <seealso @@ -213,7 +213,7 @@ will be slower as the number of persistent terms increases.</pre> <funcs> <func> - <name name="erase" arity="1"/> + <name name="erase" arity="1" since="OTP 21.2"/> <fsummary>Erase the name for a persistent term.</fsummary> <desc> <p>Erase the name for the persistent term with key @@ -229,7 +229,7 @@ will be slower as the number of persistent terms increases.</pre> </func> <func> - <name name="get" arity="0"/> + <name name="get" arity="0" since="OTP 21.2"/> <fsummary>Get all persistent terms.</fsummary> <desc> <p>Retrieve the keys and values for all persistent terms. @@ -239,7 +239,7 @@ will be slower as the number of persistent terms increases.</pre> </func> <func> - <name name="get" arity="1"/> + <name name="get" arity="1" since="OTP 21.2"/> <fsummary>Get the value for a persistent term.</fsummary> <desc> <p>Retrieve the value for the persistent term associated with @@ -256,7 +256,7 @@ will be slower as the number of persistent terms increases.</pre> </func> <func> - <name name="info" arity="0"/> + <name name="info" arity="0" since="OTP 21.2"/> <fsummary>Get information about persistent terms.</fsummary> <desc> <p>Return information about persistent terms in a map. The map @@ -272,7 +272,7 @@ will be slower as the number of persistent terms increases.</pre> </func> <func> - <name name="put" arity="2"/> + <name name="put" arity="2" since="OTP 21.2"/> <fsummary>Store a term.</fsummary> <desc> <p>Store the value <c><anno>Value</anno></c> as a persistent term and diff --git a/erts/doc/src/ref_man.xml b/erts/doc/src/ref_man.xml index a78aaa449e..811d299f06 100644 --- a/erts/doc/src/ref_man.xml +++ b/erts/doc/src/ref_man.xml @@ -31,26 +31,26 @@ </header> <description> </description> - <xi:include href="erl_prim_loader.xml"/> - <xi:include href="erlang.xml"/> - <xi:include href="init.xml"/> - <xi:include href="persistent_term.xml"/> - <xi:include href="zlib.xml"/> + <xi:include href="atomics.xml"/> + <xi:include href="counters.xml"/> + <xi:include href="driver_entry.xml"/> <xi:include href="epmd.xml"/> <xi:include href="erl.xml"/> + <xi:include href="erlang.xml"/> <xi:include href="erlc.xml"/> - <xi:include href="werl.xml"/> - <xi:include href="escript.xml"/> - <xi:include href="erlsrv.xml"/> - <xi:include href="start_erl.xml"/> - <xi:include href="run_erl.xml"/> - <xi:include href="start.xml"/> <xi:include href="erl_driver.xml"/> - <xi:include href="driver_entry.xml"/> - <xi:include href="erts_alloc.xml"/> <xi:include href="erl_nif.xml"/> + <xi:include href="erl_prim_loader.xml"/> + <xi:include href="erlsrv.xml"/> <xi:include href="erl_tracer.xml"/> - <xi:include href="atomics.xml"/> - <xi:include href="counters.xml"/> + <xi:include href="erts_alloc.xml"/> + <xi:include href="escript.xml"/> + <xi:include href="init.xml"/> + <xi:include href="persistent_term.xml"/> + <xi:include href="run_erl.xml"/> + <xi:include href="start.xml"/> + <xi:include href="start_erl.xml"/> + <xi:include href="werl.xml"/> + <xi:include href="zlib.xml"/> </application> diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml index 6f4c42da27..38229456c9 100644 --- a/erts/doc/src/zlib.xml +++ b/erts/doc/src/zlib.xml @@ -29,7 +29,7 @@ <rev></rev> <file>zlib.xml</file> </header> - <module>zlib</module> + <module since="">zlib</module> <modulesummary>zlib compression interface.</modulesummary> <description> <p>This module provides an API for the zlib library @@ -120,7 +120,7 @@ list_to_binary([Compressed|Last])</pre> <funcs> <func> - <name name="adler32" arity="2"/> + <name name="adler32" arity="2" since=""/> <fsummary>Calculate the Adler checksum.</fsummary> <desc> <p>Calculates the Adler-32 checksum for <c><anno>Data</anno></c>.</p> @@ -133,7 +133,7 @@ list_to_binary([Compressed|Last])</pre> </func> <func> - <name name="adler32" arity="3"/> + <name name="adler32" arity="3" since=""/> <fsummary>Calculate the Adler checksum.</fsummary> <desc> <p>Updates a running Adler-32 checksum for <c><anno>Data</anno></c>. @@ -153,7 +153,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="adler32_combine" arity="4"/> + <name name="adler32_combine" arity="4" since=""/> <fsummary>Combine two Adler-32 checksums.</fsummary> <desc> <p>Combines two Adler-32 checksums into one. For two binaries or @@ -172,7 +172,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="close" arity="1"/> + <name name="close" arity="1" since=""/> <fsummary>Close a stream.</fsummary> <desc> <p>Closes the stream referenced by <c><anno>Z</anno></c>.</p> @@ -180,7 +180,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="compress" arity="1"/> + <name name="compress" arity="1" since=""/> <fsummary>Compress data with standard zlib functionality.</fsummary> <desc> <p>Compresses data with zlib headers and checksum.</p> @@ -188,7 +188,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="crc32" arity="1"/> + <name name="crc32" arity="1" since=""/> <fsummary>Get current CRC.</fsummary> <desc> <p>Gets the current calculated CRC checksum.</p> @@ -202,7 +202,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="crc32" arity="2"/> + <name name="crc32" arity="2" since=""/> <fsummary>Calculate CRC.</fsummary> <desc> <p>Calculates the CRC checksum for <c><anno>Data</anno></c>.</p> @@ -215,7 +215,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="crc32" arity="3"/> + <name name="crc32" arity="3" since=""/> <fsummary>Calculate CRC.</fsummary> <desc> <p>Updates a running CRC checksum for <c><anno>Data</anno></c>. @@ -235,7 +235,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="crc32_combine" arity="4"/> + <name name="crc32_combine" arity="4" since=""/> <fsummary>Combine two CRCs.</fsummary> <desc> <p>Combines two CRC checksums into one. For two binaries or iolists, @@ -254,7 +254,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="deflate" arity="2"/> + <name name="deflate" arity="2" since=""/> <fsummary>Compress data.</fsummary> <desc> <p>Same as <c>deflate(<anno>Z</anno>, <anno>Data</anno>, none)</c>.</p> @@ -262,7 +262,7 @@ Crc = lists:foldl(fun(Data,Crc0) -> </func> <func> - <name name="deflate" arity="3"/> + <name name="deflate" arity="3" since=""/> <fsummary>Compress data.</fsummary> <desc> <p>Compresses as much data as possible, and @@ -300,7 +300,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="deflateEnd" arity="1"/> + <name name="deflateEnd" arity="1" since=""/> <fsummary>End deflate session.</fsummary> <desc> <p>Ends the deflate session and cleans all data used. Notice that this @@ -311,7 +311,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="deflateInit" arity="1"/> + <name name="deflateInit" arity="1" since=""/> <fsummary>Initialize a session for compression.</fsummary> <desc> <p>Same as <c>zlib:deflateInit(<anno>Z</anno>, default)</c>.</p> @@ -319,7 +319,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="deflateInit" arity="2"/> + <name name="deflateInit" arity="2" since=""/> <fsummary>Initialize a session for compression.</fsummary> <desc> <p>Initializes a zlib stream for compression.</p> @@ -334,7 +334,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="deflateInit" arity="6"/> + <name name="deflateInit" arity="6" since=""/> <fsummary>Initialize a session for compression.</fsummary> <desc> <p>Initiates a zlib stream for compression.</p> @@ -410,7 +410,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="deflateParams" arity="3"/> + <name name="deflateParams" arity="3" since=""/> <fsummary>Dynamicly update deflate parameters.</fsummary> <desc> <p>Dynamically updates the compression level and compression @@ -432,7 +432,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="deflateReset" arity="1"/> + <name name="deflateReset" arity="1" since=""/> <fsummary>Reset the deflate session.</fsummary> <desc> <p>Equivalent to @@ -446,7 +446,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="deflateSetDictionary" arity="2"/> + <name name="deflateSetDictionary" arity="2" since=""/> <fsummary>Initialize the compression dictionary.</fsummary> <desc> <p>Initializes the compression dictionary from the specified byte @@ -464,7 +464,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="getBufSize" arity="1"/> + <name name="getBufSize" arity="1" since=""/> <fsummary>Get buffer size.</fsummary> <desc> <p>Gets the size of the intermediate buffer.</p> @@ -476,7 +476,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="gunzip" arity="1"/> + <name name="gunzip" arity="1" since=""/> <fsummary>Uncompress data with gz header.</fsummary> <desc> <p>Uncompresses data with gz headers and checksum.</p> @@ -484,7 +484,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="gzip" arity="1"/> + <name name="gzip" arity="1" since=""/> <fsummary>Compress data with gz header.</fsummary> <desc> <p>Compresses data with gz headers and checksum.</p> @@ -492,7 +492,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="inflate" arity="2"/> + <name name="inflate" arity="2" since=""/> <fsummary>Decompress data.</fsummary> <desc> <p>Equivalent to @@ -502,7 +502,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="inflate" arity="3"/> + <name name="inflate" arity="3" since="OTP 20.1"/> <fsummary>Decompress data.</fsummary> <desc> <p>Decompresses as much data as possible. It can introduce some output @@ -524,7 +524,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="inflateChunk" arity="1"/> + <name name="inflateChunk" arity="1" since="OTP 18.0"/> <fsummary>Read next uncompressed chunk.</fsummary> <desc> <warning> @@ -540,7 +540,7 @@ list_to_binary([B1,B2])</pre> </func> <func> - <name name="inflateChunk" arity="2"/> + <name name="inflateChunk" arity="2" since="OTP 18.0"/> <fsummary>Decompress data with limited output size.</fsummary> <desc> <warning> @@ -584,7 +584,7 @@ loop(Z, Handler, Uncompressed) -> </func> <func> - <name name="inflateEnd" arity="1"/> + <name name="inflateEnd" arity="1" since=""/> <fsummary>End inflate session.</fsummary> <desc> <p>Ends the inflate session and cleans all data used. Notice @@ -595,7 +595,7 @@ loop(Z, Handler, Uncompressed) -> </func> <func> - <name name="inflateGetDictionary" arity="1"/> + <name name="inflateGetDictionary" arity="1" since="OTP 20.0"/> <fsummary>Return the decompression dictionary.</fsummary> <desc> <p>Returns the decompression dictionary currently in use @@ -607,7 +607,7 @@ loop(Z, Handler, Uncompressed) -> </func> <func> - <name name="inflateInit" arity="1"/> + <name name="inflateInit" arity="1" since=""/> <fsummary>Initialize a session for decompression.</fsummary> <desc> <p>Initializes a zlib stream for decompression.</p> @@ -615,7 +615,7 @@ loop(Z, Handler, Uncompressed) -> </func> <func> - <name name="inflateInit" arity="2"/> + <name name="inflateInit" arity="2" since=""/> <fsummary>Initialize a session for decompression.</fsummary> <desc> <p>Initializes a decompression session on zlib stream.</p> @@ -634,7 +634,7 @@ loop(Z, Handler, Uncompressed) -> </func> <func> - <name name="inflateReset" arity="1"/> + <name name="inflateReset" arity="1" since=""/> <fsummary>>Reset the inflate session.</fsummary> <desc> <p>Equivalent to @@ -648,7 +648,7 @@ loop(Z, Handler, Uncompressed) -> </func> <func> - <name name="inflateSetDictionary" arity="2"/> + <name name="inflateSetDictionary" arity="2" since=""/> <fsummary>Initialize the decompression dictionary.</fsummary> <desc> <p>Initializes the decompression dictionary from the specified @@ -688,7 +688,7 @@ new_unpack(Z, Compressed, Dict) -> </func> <func> - <name name="open" arity="0"/> + <name name="open" arity="0" since=""/> <fsummary>Open a stream and return a stream reference.</fsummary> <desc> <p>Opens a zlib stream.</p> @@ -696,7 +696,7 @@ new_unpack(Z, Compressed, Dict) -> </func> <func> - <name name="safeInflate" arity="2"/> + <name name="safeInflate" arity="2" since="OTP 20.1"/> <fsummary>Decompress data with limited output size.</fsummary> <desc> <p>Like <seealso marker="#inflate/2"><c>inflate/2</c></seealso>, @@ -733,7 +733,7 @@ loop(Z, Handler, {finished, Output}) -> </func> <func> - <name name="setBufSize" arity="2"/> + <name name="setBufSize" arity="2" since=""/> <fsummary>Set buffer size.</fsummary> <desc> <p>Sets the intermediate buffer size.</p> @@ -745,7 +745,7 @@ loop(Z, Handler, {finished, Output}) -> </func> <func> - <name name="set_controlling_process" arity="2"/> + <name name="set_controlling_process" arity="2" since="OTP 20.1.3"/> <fsummary>Transfers ownership of a zlib stream.</fsummary> <desc> <p>Changes the controlling process of <c><anno>Z</anno></c> to @@ -754,7 +754,7 @@ loop(Z, Handler, {finished, Output}) -> </func> <func> - <name name="uncompress" arity="1"/> + <name name="uncompress" arity="1" since=""/> <fsummary>Uncompress data with standard zlib functionality.</fsummary> <desc> <p>Uncompresses data with zlib headers and checksum.</p> @@ -762,7 +762,7 @@ loop(Z, Handler, {finished, Output}) -> </func> <func> - <name name="unzip" arity="1"/> + <name name="unzip" arity="1" since=""/> <fsummary>Uncompress data without the zlib headers.</fsummary> <desc> <p>Uncompresses data without zlib headers and checksum.</p> @@ -770,7 +770,7 @@ loop(Z, Handler, {finished, Output}) -> </func> <func> - <name name="zip" arity="1"/> + <name name="zip" arity="1" since=""/> <fsummary>Compress data without the zlib headers.</fsummary> <desc> <p>Compresses data without zlib headers and checksum.</p> diff --git a/erts/emulator/beam/arith_instrs.tab b/erts/emulator/beam/arith_instrs.tab index b828e86788..574fceec5b 100644 --- a/erts/emulator/beam/arith_instrs.tab +++ b/erts/emulator/beam/arith_instrs.tab @@ -19,21 +19,22 @@ // %CopyrightEnd% // -OUTLINED_ARITH_2(Fail, Live, Name, BIF, Op1, Op2, Dst) { +OUTLINED_ARITH_2(Fail, Name, BIF, Op1, Op2, Dst) { Eterm result; - Uint live = $Live; - HEAVY_SWAPOUT; - reg[live] = $Op1; - reg[live+1] = $Op2; - result = erts_gc_$Name (c_p, reg, live); - HEAVY_SWAPIN; +#ifdef DEBUG + Eterm* orig_htop = HTOP; + Eterm* orig_stop = E; +#endif + DEBUG_SWAPOUT; + result = erts_$Name (c_p, $Op1, $Op2); + DEBUG_SWAPIN; + ASSERT(orig_htop == HTOP && orig_stop == E); ERTS_HOLE_CHECK(c_p); if (ERTS_LIKELY(is_value(result))) { - $REFRESH_GEN_DEST(); $Dst = result; $NEXT0(); } - $BIF_ERROR_ARITY_2($Fail, $BIF, reg[live], reg[live+1]); + $BIF_ERROR_ARITY_2($Fail, $BIF, $Op1, $Op2); } @@ -48,7 +49,7 @@ plus.fetch(Op1, Op2) { PlusOp2 = $Op2; } -plus.execute(Fail, Live, Dst) { +plus.execute(Fail, Dst) { if (ERTS_LIKELY(is_both_small(PlusOp1, PlusOp2))) { Sint i = signed_val(PlusOp1) + signed_val(PlusOp2); if (ERTS_LIKELY(IS_SSMALL(i))) { @@ -56,7 +57,7 @@ plus.execute(Fail, Live, Dst) { $NEXT0(); } } - $OUTLINED_ARITH_2($Fail, $Live, mixed_plus, BIF_splus_2, PlusOp1, PlusOp2, $Dst); + $OUTLINED_ARITH_2($Fail, mixed_plus, BIF_splus_2, PlusOp1, PlusOp2, $Dst); } i_minus := minus.fetch.execute; @@ -70,7 +71,7 @@ minus.fetch(Op1, Op2) { MinusOp2 = $Op2; } -minus.execute(Fail, Live, Dst) { +minus.execute(Fail, Dst) { if (ERTS_LIKELY(is_both_small(MinusOp1, MinusOp2))) { Sint i = signed_val(MinusOp1) - signed_val(MinusOp2); if (ERTS_LIKELY(IS_SSMALL(i))) { @@ -78,7 +79,7 @@ minus.execute(Fail, Live, Dst) { $NEXT0(); } } - $OUTLINED_ARITH_2($Fail, $Live, mixed_minus, BIF_sminus_2, MinusOp1, MinusOp2, $Dst); + $OUTLINED_ARITH_2($Fail, mixed_minus, BIF_sminus_2, MinusOp1, MinusOp2, $Dst); } i_increment := increment.fetch.execute; @@ -91,9 +92,8 @@ increment.fetch(Src) { increment_reg_val = $Src; } -increment.execute(IncrementVal, Live, Dst) { +increment.execute(IncrementVal, Dst) { Eterm increment_val = $IncrementVal; - Uint live; Eterm result; if (ERTS_LIKELY(is_small(increment_reg_val))) { @@ -103,15 +103,9 @@ increment.execute(IncrementVal, Live, Dst) { $NEXT0(); } } - live = $Live; - HEAVY_SWAPOUT; - reg[live] = increment_reg_val; - reg[live+1] = make_small(increment_val); - result = erts_gc_mixed_plus(c_p, reg, live); - HEAVY_SWAPIN; + result = erts_mixed_plus(c_p, increment_reg_val, make_small(increment_val)); ERTS_HOLE_CHECK(c_p); if (ERTS_LIKELY(is_value(result))) { - $REFRESH_GEN_DEST(); $Dst = result; $NEXT0(); } @@ -119,19 +113,19 @@ increment.execute(IncrementVal, Live, Dst) { goto find_func_info; } -i_times(Fail, Live, Op1, Op2, Dst) { +i_times(Fail, Op1, Op2, Dst) { Eterm op1 = $Op1; Eterm op2 = $Op2; - $OUTLINED_ARITH_2($Fail, $Live, mixed_times, BIF_stimes_2, op1, op2, $Dst); + $OUTLINED_ARITH_2($Fail, mixed_times, BIF_stimes_2, op1, op2, $Dst); } -i_m_div(Fail, Live, Op1, Op2, Dst) { +i_m_div(Fail, Op1, Op2, Dst) { Eterm op1 = $Op1; Eterm op2 = $Op2; - $OUTLINED_ARITH_2($Fail, $Live, mixed_div, BIF_div_2, op1, op2, $Dst); + $OUTLINED_ARITH_2($Fail, mixed_div, BIF_div_2, op1, op2, $Dst); } -i_int_div(Fail, Live, Op1, Op2, Dst) { +i_int_div(Fail, Op1, Op2, Dst) { Eterm op1 = $Op1; Eterm op2 = $Op2; if (ERTS_UNLIKELY(op2 == SMALL_ZERO)) { @@ -144,7 +138,7 @@ i_int_div(Fail, Live, Op1, Op2, Dst) { $NEXT0(); } } - $OUTLINED_ARITH_2($Fail, $Live, int_div, BIF_intdiv_2, op1, op2, $Dst); + $OUTLINED_ARITH_2($Fail, int_div, BIF_intdiv_2, op1, op2, $Dst); } i_rem := rem.fetch.execute; @@ -158,7 +152,7 @@ rem.fetch(Src1, Src2) { RemOp2 = $Src2; } -rem.execute(Fail, Live, Dst) { +rem.execute(Fail, Dst) { if (ERTS_UNLIKELY(RemOp2 == SMALL_ZERO)) { c_p->freason = BADARITH; $BIF_ERROR_ARITY_2($Fail, BIF_rem_2, RemOp1, RemOp2); @@ -166,7 +160,7 @@ rem.execute(Fail, Live, Dst) { $Dst = make_small(signed_val(RemOp1) % signed_val(RemOp2)); $NEXT0(); } else { - $OUTLINED_ARITH_2($Fail, $Live, int_rem, BIF_rem_2, RemOp1, RemOp2, $Dst); + $OUTLINED_ARITH_2($Fail, int_rem, BIF_rem_2, RemOp1, RemOp2, $Dst); } } @@ -181,7 +175,7 @@ band.fetch(Src1, Src2) { BandOp2 = $Src2; } -band.execute(Fail, Live, Dst) { +band.execute(Fail, Dst) { if (ERTS_LIKELY(is_both_small(BandOp1, BandOp2))) { /* * No need to untag -- TAG & TAG == TAG. @@ -189,10 +183,10 @@ band.execute(Fail, Live, Dst) { $Dst = BandOp1 & BandOp2; $NEXT0(); } - $OUTLINED_ARITH_2($Fail, $Live, band, BIF_band_2, BandOp1, BandOp2, $Dst); + $OUTLINED_ARITH_2($Fail, band, BIF_band_2, BandOp1, BandOp2, $Dst); } -i_bor(Fail, Live, Src1, Src2, Dst) { +i_bor(Fail, Src1, Src2, Dst) { if (ERTS_LIKELY(is_both_small($Src1, $Src2))) { /* * No need to untag -- TAG | TAG == TAG. @@ -200,10 +194,10 @@ i_bor(Fail, Live, Src1, Src2, Dst) { $Dst = $Src1 | $Src2; $NEXT0(); } - $OUTLINED_ARITH_2($Fail, $Live, bor, BIF_bor_2, $Src1, $Src2, $Dst); + $OUTLINED_ARITH_2($Fail, bor, BIF_bor_2, $Src1, $Src2, $Dst); } -i_bxor(Fail, Live, Src1, Src2, Dst) { +i_bxor(Fail, Src1, Src2, Dst) { if (ERTS_LIKELY(is_both_small($Src1, $Src2))) { /* * TAG ^ TAG == 0. @@ -214,7 +208,7 @@ i_bxor(Fail, Live, Src1, Src2, Dst) { $Dst = ($Src1 ^ $Src2) | make_small(0); $NEXT0(); } - $OUTLINED_ARITH_2($Fail, $Live, bxor, BIF_bxor_2, $Src1, $Src2, $Dst); + $OUTLINED_ARITH_2($Fail, bxor, BIF_bxor_2, $Src1, $Src2, $Dst); } i_bsl := shift.setup_bsl.execute; @@ -265,7 +259,7 @@ shift.setup_bsl(Src1, Src2) { } } -shift.execute(Fail, Live, Dst) { +shift.execute(Fail, Dst) { Uint big_words_needed; if (ERTS_LIKELY(is_small(Op1))) { @@ -320,7 +314,9 @@ shift.execute(Fail, Live, Dst) { } { Eterm tmp_big[2]; - Sint big_need_size = BIG_NEED_SIZE(big_words_needed+1); + Sint big_need_size = 1 + BIG_NEED_SIZE(big_words_needed+1); + Eterm* hp; + Eterm* hp_end; /* * Slightly conservative check the size to avoid @@ -331,15 +327,16 @@ shift.execute(Fail, Live, Dst) { if (big_need_size-8 > BIG_ARITY_MAX) { $SYSTEM_LIMIT($Fail); } - $GC_TEST_PRESERVE(big_need_size+1, $Live, Op1); + hp = HeapFragOnlyAlloc(c_p, big_need_size); if (is_small(Op1)) { Op1 = small_to_big(signed_val(Op1), tmp_big); } - Op1 = big_lshift(Op1, shift_left_count, HTOP); + Op1 = big_lshift(Op1, shift_left_count, hp); + hp_end = hp + big_need_size; if (is_big(Op1)) { - HTOP += bignum_header_arity(*HTOP) + 1; + hp += bignum_header_arity(*hp) + 1; } - HEAP_SPACE_VERIFIED(0); + HRelease(c_p, hp_end, hp); if (ERTS_UNLIKELY(is_nil(Op1))) { /* * This result must have been only slighty larger @@ -349,7 +346,6 @@ shift.execute(Fail, Live, Dst) { $SYSTEM_LIMIT($Fail); } ERTS_HOLE_CHECK(c_p); - $REFRESH_GEN_DEST(); $Dst = Op1; $NEXT0(); } @@ -366,31 +362,28 @@ shift.execute(Fail, Live, Dst) { reg[0] = Op1; reg[1] = Op2; SWAPOUT; - if (IsOpCode(I[0], i_bsl_ssjtd)) { + if (IsOpCode(I[0], i_bsl_ssjd)) { I = handle_error(c_p, I, reg, &bif_export[BIF_bsl_2]->info.mfa); } else { - ASSERT(IsOpCode(I[0], i_bsr_ssjtd)); + ASSERT(IsOpCode(I[0], i_bsr_ssjd)); I = handle_error(c_p, I, reg, &bif_export[BIF_bsr_2]->info.mfa); } goto post_error_handling; } } -i_int_bnot(Fail, Src, Live, Dst) { +i_int_bnot(Fail, Src, Dst) { Eterm bnot_val = $Src; + Eterm result; + if (ERTS_LIKELY(is_small(bnot_val))) { - bnot_val = make_small(~signed_val(bnot_val)); + result = make_small(~signed_val(bnot_val)); } else { - Uint live = $Live; - HEAVY_SWAPOUT; - reg[live] = bnot_val; - bnot_val = erts_gc_bnot(c_p, reg, live); - HEAVY_SWAPIN; + result = erts_bnot(c_p, bnot_val); ERTS_HOLE_CHECK(c_p); - if (ERTS_UNLIKELY(is_nil(bnot_val))) { - $BIF_ERROR_ARITY_1($Fail, BIF_bnot_1, reg[live]); + if (ERTS_UNLIKELY(is_nil(result))) { + $BIF_ERROR_ARITY_1($Fail, BIF_bnot_1, bnot_val); } - $REFRESH_GEN_DEST(); } - $Dst = bnot_val; + $Dst = result; } diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 3f7aa0983c..f81082a698 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -546,6 +546,7 @@ atom reload atom rem atom report_errors atom reset +atom reset_seq_trace atom restart atom return_from atom return_to diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index b33aab7eee..f71efd708f 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -558,23 +558,6 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr) case 'I': case 'W': switch (op) { - case op_i_gc_bif1_jWstd: - case op_i_gc_bif2_jWtssd: - case op_i_gc_bif3_jWtssd: - { - const ErtsGcBif* p; - BifFunction gcf = (BifFunction) *ap; - for (p = erts_gc_bifs; p->bif != 0; p++) { - if (p->gc_bif == gcf) { - print_bif_name(to, to_arg, p->bif); - break; - } - } - if (p->bif == 0) { - erts_print(to, to_arg, "%d", (Uint)gcf); - } - break; - } case op_i_make_fun_Wt: if (*sign == 'W') { ErlFunEntry* fe = (ErlFunEntry *) *ap; diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 2e4d1f8f48..6b34024a5a 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -206,8 +206,12 @@ void** beam_ops; #ifdef DEBUG # /* The stack pointer is used in an assertion. */ # define LIGHT_SWAPOUT SWAPOUT +# define DEBUG_SWAPOUT SWAPOUT +# define DEBUG_SWAPIN SWAPIN #else # define LIGHT_SWAPOUT HEAP_TOP(c_p) = HTOP +# define DEBUG_SWAPOUT +# define DEBUG_SWAPIN #endif /* @@ -386,7 +390,6 @@ do { \ */ static void init_emulator_finish(void) NOINLINE; static ErtsCodeMFA *ubif2mfa(void* uf) NOINLINE; -static ErtsCodeMFA *gcbif2mfa(void* gcf) NOINLINE; static BeamInstr* handle_error(Process* c_p, BeamInstr* pc, Eterm* reg, ErtsCodeMFA* bif_mfa) NOINLINE; static BeamInstr* call_error_handler(Process* p, ErtsCodeMFA* mfa, @@ -1301,18 +1304,6 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) } static ErtsCodeMFA * -gcbif2mfa(void* gcf) -{ - int i; - for (i = 0; erts_gc_bifs[i].bif; i++) { - if (erts_gc_bifs[i].gc_bif == gcf) - return &bif_export[erts_gc_bifs[i].exp_ix]->info.mfa; - } - erts_exit(ERTS_ERROR_EXIT, "bad gc bif"); - return NULL; -} - -static ErtsCodeMFA * ubif2mfa(void* uf) { int i; @@ -1320,7 +1311,7 @@ ubif2mfa(void* uf) if (erts_u_bifs[i].bif == uf) return &bif_export[erts_u_bifs[i].exp_ix]->info.mfa; } - erts_exit(ERTS_ERROR_EXIT, "bad u bif"); + erts_exit(ERTS_ERROR_EXIT, "bad u bif: %p\n", uf); return NULL; } diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index c28a5c57e6..400a58a75c 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -3580,38 +3580,36 @@ gen_skip_bits2(LoaderState* stp, GenOpArg Fail, GenOpArg Ms, } static GenOp* -gen_increment(LoaderState* stp, GenOpArg Reg, GenOpArg Integer, - GenOpArg Live, GenOpArg Dst) +gen_increment(LoaderState* stp, GenOpArg Reg, + GenOpArg Integer, GenOpArg Dst) { GenOp* op; NEW_GENOP(stp, op); - op->op = genop_i_increment_4; - op->arity = 4; + op->op = genop_i_increment_3; + op->arity = 3; op->next = NULL; op->a[0] = Reg; op->a[1].type = TAG_u; op->a[1].val = Integer.val; - op->a[2] = Live; - op->a[3] = Dst; + op->a[2] = Dst; return op; } static GenOp* -gen_increment_from_minus(LoaderState* stp, GenOpArg Reg, GenOpArg Integer, - GenOpArg Live, GenOpArg Dst) +gen_increment_from_minus(LoaderState* stp, GenOpArg Reg, + GenOpArg Integer, GenOpArg Dst) { GenOp* op; NEW_GENOP(stp, op); - op->op = genop_i_increment_4; - op->arity = 4; + op->op = genop_i_increment_3; + op->arity = 3; op->next = NULL; op->a[0] = Reg; op->a[1].type = TAG_u; op->a[1].val = -Integer.val; - op->a[2] = Live; - op->a[3] = Dst; + op->a[2] = Dst; return op; } @@ -4300,98 +4298,6 @@ gen_make_fun2(LoaderState* stp, GenOpArg idx) } static GenOp* -translate_gc_bif(LoaderState* stp, GenOp* op, GenOpArg Bif) -{ - const ErtsGcBif* p; - BifFunction bf; - - bf = stp->import[Bif.val].bf; - for (p = erts_gc_bifs; p->bif != 0; p++) { - if (p->bif == bf) { - op->a[1].type = TAG_u; - op->a[1].val = (BeamInstr) p->gc_bif; - return op; - } - } - - op->op = genop_unsupported_guard_bif_3; - op->arity = 3; - op->a[0].type = TAG_a; - op->a[0].val = stp->import[Bif.val].module; - op->a[1].type = TAG_a; - op->a[1].val = stp->import[Bif.val].function; - op->a[2].type = TAG_u; - op->a[2].val = stp->import[Bif.val].arity; - return op; -} - -/* - * Rewrite gc_bifs with one parameter (the common case). - */ -static GenOp* -gen_guard_bif1(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif, - GenOpArg Src, GenOpArg Dst) -{ - GenOp* op; - - NEW_GENOP(stp, op); - op->next = NULL; - op->op = genop_i_gc_bif1_5; - op->arity = 5; - op->a[0] = Fail; - /* op->a[1] is set by translate_gc_bif() */ - op->a[2] = Src; - op->a[3] = Live; - op->a[4] = Dst; - return translate_gc_bif(stp, op, Bif); -} - -/* - * This is used by the ops.tab rule that rewrites gc_bifs with two parameters. - */ -static GenOp* -gen_guard_bif2(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif, - GenOpArg S1, GenOpArg S2, GenOpArg Dst) -{ - GenOp* op; - - NEW_GENOP(stp, op); - op->next = NULL; - op->op = genop_i_gc_bif2_6; - op->arity = 6; - op->a[0] = Fail; - /* op->a[1] is set by translate_gc_bif() */ - op->a[2] = Live; - op->a[3] = S1; - op->a[4] = S2; - op->a[5] = Dst; - return translate_gc_bif(stp, op, Bif); -} - -/* - * This is used by the ops.tab rule that rewrites gc_bifs with three parameters. - */ -static GenOp* -gen_guard_bif3(LoaderState* stp, GenOpArg Fail, GenOpArg Live, GenOpArg Bif, - GenOpArg S1, GenOpArg S2, GenOpArg S3, GenOpArg Dst) -{ - GenOp* op; - - NEW_GENOP(stp, op); - op->next = NULL; - op->op = genop_ii_gc_bif3_7; - op->arity = 7; - op->a[0] = Fail; - /* op->a[1] is set by translate_gc_bif() */ - op->a[2] = Live; - op->a[3] = S1; - op->a[4] = S2; - op->a[5] = S3; - op->a[6] = Dst; - return translate_gc_bif(stp, op, Bif); -} - -static GenOp* tuple_append_put5(LoaderState* stp, GenOpArg Arity, GenOpArg Dst, GenOpArg* Puts, GenOpArg S1, GenOpArg S2, GenOpArg S3, GenOpArg S4, GenOpArg S5) diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index 4741d7451d..ff7db0e742 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -2812,38 +2812,110 @@ BIF_RETTYPE list_to_existing_atom_1(BIF_ALIST_1) /* convert an integer to a list of ascii integers */ -BIF_RETTYPE integer_to_list_1(BIF_ALIST_1) +static Eterm integer_to_list(Process *c_p, Eterm num, int base) { - Eterm* hp; + Eterm *hp; + Eterm res; Uint need; + if (is_small(num)) { + char s[128]; + char *c = s; + Uint digits; + + digits = Sint_to_buf(signed_val(num), base, &c, sizeof(s)); + need = 2 * digits; + + hp = HAlloc(c_p, need); + res = buf_to_intlist(&hp, c, digits, NIL); + } else { + const int DIGITS_PER_RED = 16; + Eterm *hp_end; + Uint digits; + + digits = big_integer_estimate(num, base); + + if ((digits / DIGITS_PER_RED) > ERTS_BIF_REDS_LEFT(c_p)) { + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + + /* This could take a very long time, tell the caller to reschedule + * us to a dirty CPU scheduler if we aren't already on one. */ + if (esdp->type == ERTS_SCHED_NORMAL) { + return THE_NON_VALUE; + } + } else { + BUMP_REDS(c_p, digits / DIGITS_PER_RED); + } + + need = 2 * digits; + + hp = HAlloc(c_p, need); + hp_end = hp + need; + + res = erts_big_to_list(num, base, &hp); + HRelease(c_p, hp_end, hp); + } + + return res; +} + +BIF_RETTYPE integer_to_list_1(BIF_ALIST_1) +{ + Eterm res; + if (is_not_integer(BIF_ARG_1)) { - BIF_ERROR(BIF_P, BADARG); + BIF_ERROR(BIF_P, BADARG); } - if (is_small(BIF_ARG_1)) { - char *c; - int n; - struct Sint_buf ibuf; + res = integer_to_list(BIF_P, BIF_ARG_1, 10); - c = Sint_to_buf(signed_val(BIF_ARG_1), &ibuf); - n = sys_strlen(c); - need = 2*n; - hp = HAlloc(BIF_P, need); - BIF_RET(buf_to_intlist(&hp, c, n, NIL)); + if (is_non_value(res)) { + Eterm args[1]; + args[0] = BIF_ARG_1; + return erts_schedule_bif(BIF_P, + args, + BIF_I, + integer_to_list_1, + ERTS_SCHED_DIRTY_CPU, + am_erlang, + am_integer_to_list, + 1); } - else { - int n = big_decimal_estimate(BIF_ARG_1); - Eterm res; - Eterm* hp_end; - need = 2*n; - hp = HAlloc(BIF_P, need); - hp_end = hp + need; - res = erts_big_to_list(BIF_ARG_1, &hp); - HRelease(BIF_P,hp_end,hp); - BIF_RET(res); + return res; +} + +BIF_RETTYPE integer_to_list_2(BIF_ALIST_2) +{ + Eterm res; + SWord base; + + if (is_not_integer(BIF_ARG_1) || is_not_small(BIF_ARG_2)) { + BIF_ERROR(BIF_P, BADARG); + } + + base = signed_val(BIF_ARG_2); + if (base < 2 || base > 36) { + BIF_ERROR(BIF_P, BADARG); + } + + res = integer_to_list(BIF_P, BIF_ARG_1, base); + + if (is_non_value(res)) { + Eterm args[2]; + args[0] = BIF_ARG_1; + args[1] = BIF_ARG_2; + return erts_schedule_bif(BIF_P, + args, + BIF_I, + integer_to_list_2, + ERTS_SCHED_DIRTY_CPU, + am_erlang, + am_integer_to_list, + 2); } + + return res; } /**********************************************************************/ @@ -4485,11 +4557,12 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) ERTS_TRACER_CLEAR(&old_seq_tracer); BIF_RET(ret); - } else if (BIF_ARG_1 == make_small(1)) { + } else if (BIF_ARG_1 == am_reset_seq_trace) { int i, max; - erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); - erts_thr_progress_block(); + erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_thr_progress_block(); + max = erts_ptab_max(&erts_proc); for (i = 0; i < max; i++) { Process *p = erts_pix2proc(i); @@ -4501,13 +4574,14 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) #endif p->seq_trace_clock = 0; p->seq_trace_lastcnt = 0; - + erts_proc_lock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_MSGQ); erts_proc_sig_clear_seq_trace_tokens(p); + erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_MSGQ); } } - erts_thr_progress_unblock(); - erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); + erts_thr_progress_unblock(); + erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN); BIF_RET(am_true); } else if (BIF_ARG_1 == am_scheduler_wall_time) { @@ -4622,6 +4696,9 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) return erts_bind_schedulers(BIF_P, BIF_ARG_2); } else if (ERTS_IS_ATOM_STR("erts_alloc", BIF_ARG_1)) { return erts_alloc_set_dyn_param(BIF_P, BIF_ARG_2); + } else if (ERTS_IS_ATOM_STR("system_logger", BIF_ARG_1)) { + Eterm res = erts_set_system_logger(BIF_ARG_2); + if (is_value(res)) BIF_RET(res); } error: BIF_ERROR(BIF_P, BADARG); @@ -5124,55 +5201,6 @@ erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm * return exiting; } - - -#ifdef HARDDEBUG -/* -You'll need this line in bif.tab to be able to use this debug bif - -bif erlang:send_to_logger/2 - -*/ -BIF_RETTYPE send_to_logger_2(BIF_ALIST_2) -{ - byte *buf; - ErlDrvSizeT len; - if (!is_atom(BIF_ARG_1) || !(is_list(BIF_ARG_2) || - is_nil(BIF_ARG_1))) { - BIF_ERROR(BIF_P,BADARG); - } - if (erts_iolist_size(BIF_ARG_2, &len) != 0) - BIF_ERROR(BIF_P,BADARG); - else if (len == 0) - buf = ""; - else { - ErlDrvSizeT len2; - buf = (byte *) erts_alloc(ERTS_ALC_T_TMP, len+1); - len2 = - erts_iolist_to_buf(BIF_ARG_2, buf, len); - ASSERT(len2 == len); (void)len2; - buf[len] = '\0'; - switch (BIF_ARG_1) { - case am_info: - erts_send_info_to_logger(BIF_P->group_leader, buf, len); - break; - case am_warning: - erts_send_warning_to_logger(BIF_P->group_leader, buf, len); - break; - case am_error: - erts_send_error_to_logger(BIF_P->group_leader, buf, len); - break; - default: - { - BIF_ERROR(BIF_P,BADARG); - } - } - erts_free(ERTS_ALC_T_TMP, (void *) buf); - } - BIF_RET(am_true); -} -#endif /* HARDDEBUG */ - BIF_RETTYPE get_module_info_1(BIF_ALIST_1) { Eterm ret = erts_module_info_0(BIF_P, BIF_ARG_1); diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 35e10f3e63..18586b5ede 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -41,7 +41,7 @@ # -gcbif erlang:abs/1 +ubif erlang:abs/1 bif erlang:adler32/1 bif erlang:adler32/2 bif erlang:adler32_combine/3 @@ -66,7 +66,7 @@ bif erlang:exit/2 bif erlang:exit_signal/2 bif erlang:external_size/1 bif erlang:external_size/2 -gcbif erlang:float/1 +ubif erlang:float/1 bif erlang:float_to_list/1 bif erlang:float_to_list/2 bif erlang:fun_info/2 @@ -84,7 +84,7 @@ bif erlang:phash2/2 ubif erlang:hd/1 bif erlang:integer_to_list/1 bif erlang:is_alive/0 -gcbif erlang:length/1 +ubif erlang:length/1 bif erlang:link/1 bif erlang:list_to_atom/1 bif erlang:list_to_binary/1 @@ -133,10 +133,10 @@ bif erlang:processes/0 bif erlang:put/2 bif erlang:register/2 bif erlang:registered/0 -gcbif erlang:round/1 +ubif erlang:round/1 ubif erlang:self/0 bif erlang:setelement/3 -gcbif erlang:size/1 +ubif erlang:size/1 bif erlang:spawn/3 bif erlang:spawn_link/3 bif erlang:split_binary/2 @@ -146,7 +146,7 @@ bif erlang:term_to_binary/2 bif erlang:throw/1 bif erlang:time/0 ubif erlang:tl/1 -gcbif erlang:trunc/1 +ubif erlang:trunc/1 bif erlang:tuple_to_list/1 bif erlang:universaltime/0 bif erlang:universaltime_to_localtime/1 @@ -481,8 +481,8 @@ bif erlang:list_to_existing_atom/1 # ubif erlang:is_bitstring/1 ubif erlang:tuple_size/1 -gcbif erlang:byte_size/1 -gcbif erlang:bit_size/1 +ubif erlang:byte_size/1 +ubif erlang:bit_size/1 bif erlang:list_to_bitstring/1 bif erlang:bitstring_to_list/1 @@ -534,8 +534,8 @@ bif erlang:binary_to_term/2 # # The searching/splitting/substituting thingies # -gcbif erlang:binary_part/2 -gcbif erlang:binary_part/3 +ubif erlang:binary_part/2 +ubif erlang:binary_part/3 bif binary:compile_pattern/1 bif binary:match/2 @@ -623,7 +623,7 @@ bif io:printable_range/0 bif re:inspect/2 ubif erlang:is_map/1 -gcbif erlang:map_size/1 +ubif erlang:map_size/1 bif maps:find/2 bif maps:get/2 bif maps:from_list/1 @@ -671,8 +671,8 @@ bif maps:take/2 # New in 20.0 # -gcbif erlang:floor/1 -gcbif erlang:ceil/1 +ubif erlang:floor/1 +ubif erlang:ceil/1 bif math:floor/1 bif math:ceil/1 bif math:fmod/2 @@ -724,3 +724,16 @@ bif erts_internal:counters_get/2 bif erts_internal:counters_add/3 bif erts_internal:counters_put/3 bif erts_internal:counters_info/1 + +# +# New in 21.2.3 +# + +bif erts_internal:spawn_system_process/3 + +# +# New in 21.3 +# + +bif erlang:integer_to_list/2 +bif erlang:integer_to_binary/2 diff --git a/erts/emulator/beam/bif_instrs.tab b/erts/emulator/beam/bif_instrs.tab index 00854471a9..418bbe2b23 100644 --- a/erts/emulator/beam/bif_instrs.tab +++ b/erts/emulator/beam/bif_instrs.tab @@ -31,13 +31,20 @@ CALL_GUARD_BIF(BF, TmpReg, Dst) { Eterm result; +#ifdef DEBUG + Eterm* orig_htop = HTOP; + Eterm* orig_stop = E; +#endif ERTS_DBG_CHK_REDS(c_p, FCALLS); c_p->fcalls = FCALLS; PROCESS_MAIN_CHK_LOCKS(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); ERTS_CHK_MBUF_SZ(c_p); + DEBUG_SWAPOUT; result = (*$BF)(c_p, $TmpReg, I); + DEBUG_SWAPIN; + ASSERT(orig_htop == HTOP && orig_stop == E); ERTS_CHK_MBUF_SZ(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); @@ -51,222 +58,154 @@ CALL_GUARD_BIF(BF, TmpReg, Dst) { } } -// Guard BIF in head. On failure, ignore the error and jump -// to the code for the next clause. We don't support tracing +// Guard BIF in head. On failure, ignore the error and jump +// to the code for the next clause. We don't support tracing // of guard BIFs. -bif1(Fail, Bif, Src, Dst) { +i_bif1 := i_bif.fetch0.call; +i_bif2 := i_bif.fetch1.fetch0.call; +i_bif3 := i_bif.fetch2.fetch1.fetch0.call; + +i_bif.head() { ErtsBifFunc bf; - Eterm tmp_reg[1]; + Eterm tmp_reg[3]; +} +i_bif.fetch0(Src) { tmp_reg[0] = $Src; - bf = (BifFunction) $Bif; - $CALL_GUARD_BIF(bf, tmp_reg, $Dst); - - $FAIL($Fail); } -// -// Guard BIF in body. It can fail like any BIF. No trace support. -// +i_bif.fetch1(Src) { + tmp_reg[1] = $Src; +} -bif1_body(Bif, Src, Dst) { - ErtsBifFunc bf; - Eterm tmp_reg[1]; +i_bif.fetch2(Src) { + tmp_reg[2] = $Src; +} - tmp_reg[0] = $Src; +i_bif.call(Fail, Bif, Dst) { bf = (BifFunction) $Bif; $CALL_GUARD_BIF(bf, tmp_reg, $Dst); - reg[0] = tmp_reg[0]; - SWAPOUT; - I = handle_error(c_p, I, reg, ubif2mfa((void *) bf)); - goto post_error_handling; + $FAIL($Fail); } // -// Guard bif in guard with two arguments ('and'/2, 'or'/2, 'xor'/2). +// Guard BIF in body. It can fail like any BIF. No trace support. // -i_bif2(Fail, Bif, Src1, Src2, Dst) { - Eterm tmp_reg[2]; +i_bif1_body := i_bif_body.fetch0.call; +i_bif2_body := i_bif_body.fetch1.fetch0.call; +i_bif3_body := i_bif_body.fetch2.fetch1.fetch0.call; + +i_bif_body.head() { ErtsBifFunc bf; + Eterm tmp_reg[3]; +} - tmp_reg[0] = $Src1; - tmp_reg[1] = $Src2; - bf = (ErtsBifFunc) $Bif; - $CALL_GUARD_BIF(bf, tmp_reg, $Dst); - $FAIL($Fail); +i_bif_body.fetch0(Src) { + tmp_reg[0] = $Src; } -// -// Guard bif in body with two arguments ('and'/2, 'or'/2, 'xor'/2). -// +i_bif_body.fetch1(Src) { + tmp_reg[1] = $Src; +} -i_bif2_body(Bif, Src1, Src2, Dst) { - Eterm tmp_reg[2]; - ErtsBifFunc bf; +i_bif_body.fetch2(Src) { + tmp_reg[2] = $Src; +} - tmp_reg[0] = $Src1; - tmp_reg[1] = $Src2; - bf = (ErtsBifFunc) $Bif; +i_bif_body.call(Bif, Dst) { + bf = (BifFunction) $Bif; $CALL_GUARD_BIF(bf, tmp_reg, $Dst); + reg[0] = tmp_reg[0]; reg[1] = tmp_reg[1]; + reg[2] = tmp_reg[2]; SWAPOUT; I = handle_error(c_p, I, reg, ubif2mfa((void *) bf)); goto post_error_handling; } // -// Garbage-collecting BIF with one argument in either guard or body. +// length/1 is the only guard BIF that does not execute in constant +// time. Here follows special instructions to allow the calculation of +// the list length to be broken in several chunks to avoid hogging +// the scheduler for a long time. // -i_gc_bif1(Fail, Bif, Src, Live, Dst) { - typedef Eterm (*GcBifFunction)(Process*, Eterm*, Uint); - GcBifFunction bf; - Eterm result; - Uint live = (Uint) $Live; +i_length_setup(Live, Src) { + Uint live = $Live; + Eterm src = $Src; - x(live) = $Src; - bf = (GcBifFunction) $Bif; - ERTS_DBG_CHK_REDS(c_p, FCALLS); - c_p->fcalls = FCALLS; - SWAPOUT; - PROCESS_MAIN_CHK_LOCKS(c_p); - ERTS_UNREQ_PROC_MAIN_LOCK(c_p); - ERTS_CHK_MBUF_SZ(c_p); - result = (*bf)(c_p, reg, live); - ERTS_CHK_MBUF_SZ(c_p); - ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); - ERTS_REQ_PROC_MAIN_LOCK(c_p); - PROCESS_MAIN_CHK_LOCKS(c_p); - SWAPIN; - ERTS_HOLE_CHECK(c_p); - FCALLS = c_p->fcalls; - ERTS_DBG_CHK_REDS(c_p, FCALLS); - if (ERTS_LIKELY(is_value(result))) { - $REFRESH_GEN_DEST(); - $Dst = result; - $NEXT0(); - } - if (ERTS_LIKELY($Fail != 0)) { /* Handle error in guard. */ - $JUMP($Fail); - } + reg[live] = src; + reg[live+1] = make_small(0); + reg[live+2] = src; - /* Handle error in body. */ - x(0) = x(live); - I = handle_error(c_p, I, reg, gcbif2mfa((void *) bf)); - goto post_error_handling; + /* This instruction is always followed by i_length */ + SET_I($NEXT_INSTRUCTION); + goto i_length_start__; + //| -no_next } // -// Garbage-collecting BIF with two arguments in either guard or body. +// This instruction can be executed one or more times. When entering +// this instruction, the X registers have the following contents: +// +// reg[live+0] The remainder of the list. +// reg[live+1] The length so far (tagged integer). +// reg[live+2] The original list. Only used if an error is generated +// (if the final tail of the list is not []). // -i_gc_bif2(Fail, Bif, Live, Src1, Src2, Dst) { - typedef Eterm (*GcBifFunction)(Process*, Eterm*, Uint); - GcBifFunction bf; - Eterm result; - Uint live = (Uint) $Live; - - /* - * XXX This calling convention does not make sense. 'live' - * should point out the first argument, not the second - * (i.e. 'live' should not be incremented below). - */ - x(live) = $Src1; - x(live+1) = $Src2; - live++; - - bf = (GcBifFunction) $Bif; - ERTS_DBG_CHK_REDS(c_p, FCALLS); - c_p->fcalls = FCALLS; - SWAPOUT; - PROCESS_MAIN_CHK_LOCKS(c_p); - ERTS_UNREQ_PROC_MAIN_LOCK(c_p); - ERTS_CHK_MBUF_SZ(c_p); - result = (*bf)(c_p, reg, live); - ERTS_CHK_MBUF_SZ(c_p); - ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); - ERTS_REQ_PROC_MAIN_LOCK(c_p); - PROCESS_MAIN_CHK_LOCKS(c_p); - SWAPIN; - ERTS_HOLE_CHECK(c_p); - FCALLS = c_p->fcalls; - ERTS_DBG_CHK_REDS(c_p, FCALLS); - if (ERTS_LIKELY(is_value(result))) { - $REFRESH_GEN_DEST(); - $Dst = result; - $NEXT0(); - } - - if (ERTS_LIKELY($Fail != 0)) { /* Handle error in guard. */ - $JUMP($Fail); - } +i_length := i_length.start.execute; - /* Handle error in body. */ - live--; - x(0) = x(live); - x(1) = x(live+1); - I = handle_error(c_p, I, reg, gcbif2mfa((void *) bf)); - goto post_error_handling; +i_length.start() { + i_length_start__: + ; } -// -// Garbage-collecting BIF with three arguments in either guard or body. -// - -i_gc_bif3(Fail, Bif, Live, Src2, Src3, Dst) { - typedef Eterm (*GcBifFunction)(Process*, Eterm*, Uint); - GcBifFunction bf; +i_length.execute(Fail, Live, Dst) { Eterm result; - Uint live = (Uint) $Live; + Uint live; - /* - * XXX This calling convention does not make sense. 'live' - * should point out the first argument, not the third - * (i.e. 'live' should not be incremented below). - */ - x(live) = x(SCRATCH_X_REG); - x(live+1) = $Src2; - x(live+2) = $Src3; - live += 2; - - bf = (GcBifFunction) $Bif; ERTS_DBG_CHK_REDS(c_p, FCALLS); c_p->fcalls = FCALLS; - SWAPOUT; PROCESS_MAIN_CHK_LOCKS(c_p); - ERTS_UNREQ_PROC_MAIN_LOCK(c_p); + ASSERT(!ERTS_PROC_IS_EXITING(c_p)); ERTS_CHK_MBUF_SZ(c_p); - result = (*bf)(c_p, reg, live); + DEBUG_SWAPOUT; + + live = $Live; + result = erts_trapping_length_1(c_p, reg+live); + + DEBUG_SWAPIN; ERTS_CHK_MBUF_SZ(c_p); + ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result)); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); - ERTS_REQ_PROC_MAIN_LOCK(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); - SWAPIN; ERTS_HOLE_CHECK(c_p); FCALLS = c_p->fcalls; ERTS_DBG_CHK_REDS(c_p, FCALLS); if (ERTS_LIKELY(is_value(result))) { + /* Successful calculation of the list length. */ $REFRESH_GEN_DEST(); $Dst = result; $NEXT0(); + } else if (c_p->freason == TRAP) { + /* + * Good so far, but there is more work to do. Yield. + */ + $SET_CP_I_ABS(I); + SWAPOUT; + c_p->arity = live + 3; + c_p->current = NULL; + goto context_switch3; + } else { + /* Error. */ + $BIF_ERROR_ARITY_1($Fail, BIF_length_1, reg[live+2]); } - - /* Handle error in guard. */ - if (ERTS_LIKELY($Fail != 0)) { - $JUMP($Fail); - } - - /* Handle error in body. */ - live -= 2; - x(0) = x(live); - x(1) = x(live+1); - x(2) = x(live+2); - I = handle_error(c_p, I, reg, gcbif2mfa((void *) bf)); - goto post_error_handling; + //| -no_next } // diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c index 84338769e0..522f50287a 100644 --- a/erts/emulator/beam/big.c +++ b/erts/emulator/beam/big.c @@ -429,6 +429,7 @@ static const byte digits_per_sint_lookup[36-1]; static const byte digits_per_small_lookup[36-1]; static const Sint largest_power_of_base_lookup[36-1]; +static const double lg2_lookup[36-1]; static ERTS_INLINE byte get_digits_per_signed_int(Uint base) { return digits_per_sint_lookup[base-2]; @@ -442,6 +443,10 @@ static ERTS_INLINE Sint get_largest_power_of_base(Uint base) { return largest_power_of_base_lookup[base-2]; } +static ERTS_INLINE double lookup_log2(Uint base) { + return lg2_lookup[base - 2]; +} + /* ** compare two number vectors */ @@ -668,27 +673,25 @@ static dsize_t I_mul(ErtsDigit* x, dsize_t xl, ErtsDigit* y, dsize_t yl, ErtsDig static dsize_t I_sqr(ErtsDigit* x, dsize_t xl, ErtsDigit* r) { - ErtsDigit d_next = *x; ErtsDigit d; ErtsDigit* r0 = r; ErtsDigit* s = r; if ((r + xl) == x) /* "Inline" operation */ *x = 0; - x++; while(xl--) { - ErtsDigit* y = x; + ErtsDigit* y; ErtsDigit y_0 = 0, y_1 = 0, y_2 = 0, y_3 = 0; ErtsDigit b0, b1; ErtsDigit z0, z1, z2; ErtsDigit t; dsize_t y_l = xl; - + + d = *x; + x++; + y = x; s = r; - d = d_next; - d_next = *x; - x++; DMUL(d, d, b1, b0); DSUMc(*s, b0, y_3, t); @@ -1159,8 +1162,11 @@ static dsize_t I_band(ErtsDigit* x, dsize_t xl, short xsgn, *r++ = ~c1 & ~c2; x++; y++; } - while(xl--) - *r++ = ~*x++; + while(xl--) { + DSUBb(*x,0,b1,c1); + *r++ = ~c1; + x++; + } } } return I_btrail(r0, r, sign); @@ -1719,23 +1725,23 @@ double_to_big(double x, Eterm *heap, Uint hsz) /* - ** Estimate the number of decimal digits (include sign) + ** Estimate the number of digits in given base (include sign) */ -int big_decimal_estimate(Wterm x) +int big_integer_estimate(Wterm x, Uint base) { Eterm* xp = big_val(x); int lg = I_lg(BIG_V(xp), BIG_SIZE(xp)); - int lg10 = ((lg+1)*28/93)+1; + int lgBase = ((lg + 1) / lookup_log2(base)) + 1; - if (BIG_SIGN(xp)) lg10++; /* add sign */ - return lg10+1; /* add null */ + if (BIG_SIGN(xp)) lgBase++; /* add sign */ + return lgBase + 1; /* add null */ } /* -** Convert a bignum into a string of decimal numbers +** Convert a bignum into a string of numbers in given base */ - -static Uint write_big(Wterm x, void (*write_func)(void *, char), void *arg) +static Uint write_big(Wterm x, int base, void (*write_func)(void *, char), + void *arg) { Eterm* xp = big_val(x); ErtsDigit* dx = BIG_V(xp); @@ -1743,48 +1749,72 @@ static Uint write_big(Wterm x, void (*write_func)(void *, char), void *arg) short sign = BIG_SIGN(xp); ErtsDigit rem; Uint n = 0; - const Uint digits_per_Sint = get_digits_per_signed_int(10); - const Sint largest_pow_of_base = get_largest_power_of_base(10); + const Uint digits_per_Sint = get_digits_per_signed_int(base); + const Sint largest_pow_of_base = get_largest_power_of_base(base); if (xl == 1 && *dx < largest_pow_of_base) { - rem = *dx; - if (rem == 0) { - (*write_func)(arg, '0'); n++; - } else { - while(rem) { - (*write_func)(arg, (rem % 10) + '0'); n++; - rem /= 10; - } - } + rem = *dx; + if (rem == 0) { + (*write_func)(arg, '0'); n++; + } else { + while(rem) { + int digit = rem % base; + + if (digit < 10) { + (*write_func)(arg, digit + '0'); n++; + } else { + (*write_func)(arg, 'A' + (digit - 10)); n++; + } + + rem /= base; + } + } } else { - ErtsDigit* tmp = (ErtsDigit*) erts_alloc(ERTS_ALC_T_TMP, - sizeof(ErtsDigit)*xl); - dsize_t tmpl = xl; + ErtsDigit* tmp = (ErtsDigit*) erts_alloc(ERTS_ALC_T_TMP, + sizeof(ErtsDigit) * xl); + dsize_t tmpl = xl; - MOVE_DIGITS(tmp, dx, xl); + MOVE_DIGITS(tmp, dx, xl); - while(1) { + while(1) { tmpl = D_div(tmp, tmpl, largest_pow_of_base, tmp, &rem); - if (tmpl == 1 && *tmp == 0) { - while(rem) { - (*write_func)(arg, (rem % 10)+'0'); n++; - rem /= 10; - } - break; - } else { + + if (tmpl == 1 && *tmp == 0) { + while(rem) { + int digit = rem % base; + + if (digit < 10) { + (*write_func)(arg, digit + '0'); n++; + } else { + (*write_func)(arg, 'A' + (digit - 10)); n++; + } + + rem /= base; + } + break; + } else { Uint i = digits_per_Sint; - while(i--) { - (*write_func)(arg, (rem % 10)+'0'); n++; - rem /= 10; - } - } - } - erts_free(ERTS_ALC_T_TMP, (void *) tmp); + + while(i--) { + int digit = rem % base; + + if (digit < 10) { + (*write_func)(arg, digit + '0'); n++; + } else { + (*write_func)(arg, 'A' + (digit - 10)); n++; + } + + rem /= base; + } + } + } + erts_free(ERTS_ALC_T_TMP, (void *) tmp); } if (sign) { - (*write_func)(arg, '-'); n++; + (*write_func)(arg, '-'); n++; } + return n; } @@ -1801,12 +1831,12 @@ write_list(void *arg, char c) blp->hp += 2; } -Eterm erts_big_to_list(Eterm x, Eterm **hpp) +Eterm erts_big_to_list(Eterm x, int base, Eterm **hpp) { struct big_list__ bl; bl.hp = *hpp; bl.res = NIL; - write_big(x, write_list, (void *) &bl); + write_big(x, base, write_list, (void *) &bl); *hpp = bl.hp; return bl.res; } @@ -1817,11 +1847,11 @@ write_string(void *arg, char c) *(--(*((char **) arg))) = c; } -char *erts_big_to_string(Wterm x, char *buf, Uint buf_sz) +char *erts_big_to_string(Wterm x, int base, char *buf, Uint buf_sz) { char *big_str = buf + buf_sz - 1; *big_str = '\0'; - write_big(x, write_string, (void *) &big_str); + write_big(x, base, write_string, (void*)&big_str); ASSERT(buf <= big_str && big_str <= buf + buf_sz - 1); return big_str; } @@ -1830,11 +1860,11 @@ char *erts_big_to_string(Wterm x, char *buf, Uint buf_sz) * e.g. 1 bsl 64 -> "18446744073709551616" */ -Uint erts_big_to_binary_bytes(Eterm x, char *buf, Uint buf_sz) +Uint erts_big_to_binary_bytes(Eterm x, int base, char *buf, Uint buf_sz) { char *big_str = buf + buf_sz; Uint n; - n = write_big(x, write_string, (void *) &big_str); + n = write_big(x, base, write_string, (void *) &big_str); ASSERT(buf <= big_str && big_str <= buf + buf_sz); return n; } @@ -2577,9 +2607,6 @@ static const double lg2_lookup[36-1] = { 4.32193, 4.39232, 4.45943, 4.52356, 4.58496, 4.64386, 4.70044, 4.75489, 4.80735, 4.85798, 4.90689, 4.9542, 5.0, 5.04439, 5.08746, 5.12928, 5.16993 }; -static ERTS_INLINE double lookup_log2(Uint base) { - return lg2_lookup[base - 2]; -} /* * How many digits can fit into a signed int (Sint) for given base, we take diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h index a1ad75708c..da4dc84d10 100644 --- a/erts/emulator/beam/big.h +++ b/erts/emulator/beam/big.h @@ -119,10 +119,10 @@ typedef Uint dsize_t; /* Vector size type */ #endif -int big_decimal_estimate(Wterm); -Eterm erts_big_to_list(Eterm, Eterm**); -char *erts_big_to_string(Wterm x, char *buf, Uint buf_sz); -Uint erts_big_to_binary_bytes(Eterm x, char *buf, Uint buf_sz); +int big_integer_estimate(Wterm, Uint base); +Eterm erts_big_to_list(Eterm, int base, Eterm**); +char *erts_big_to_string(Wterm x, int base, char *buf, Uint buf_sz); +Uint erts_big_to_binary_bytes(Eterm x, int base, char *buf, Uint buf_sz); Eterm small_times(Sint, Sint, Eterm*); diff --git a/erts/emulator/beam/binary.c b/erts/emulator/beam/binary.c index 6a349764b2..a18228b84a 100644 --- a/erts/emulator/beam/binary.c +++ b/erts/emulator/beam/binary.c @@ -322,40 +322,102 @@ BIF_RETTYPE binary_to_integer_2(BIF_ALIST_2) } +static Eterm integer_to_binary(Process *c_p, Eterm num, int base) +{ + Eterm res; + + if (is_small(num)) { + char s[128]; + char *c = s; + Uint digits; + + digits = Sint_to_buf(signed_val(num), base, &c, sizeof(s)); + res = new_binary(c_p, (byte*)c, digits); + } else { + const int DIGITS_PER_RED = 16; + Uint digits, n; + byte *bytes; + + digits = big_integer_estimate(num, base); + + if ((digits / DIGITS_PER_RED) > ERTS_BIF_REDS_LEFT(c_p)) { + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + + /* This could take a very long time, tell the caller to reschedule + * us to a dirty CPU scheduler if we aren't already on one. */ + if (esdp->type == ERTS_SCHED_NORMAL) { + return THE_NON_VALUE; + } + } else { + BUMP_REDS(c_p, digits / DIGITS_PER_RED); + } + + bytes = (byte*)erts_alloc(ERTS_ALC_T_TMP, sizeof(byte) * digits); + n = erts_big_to_binary_bytes(num, base, (char*)bytes, digits); + res = new_binary(c_p, bytes + digits - n, n); + erts_free(ERTS_ALC_T_TMP, (void*)bytes); + } + + return res; +} + BIF_RETTYPE integer_to_binary_1(BIF_ALIST_1) -{ - Uint size; +{ Eterm res; if (is_not_integer(BIF_ARG_1)) { - BIF_ERROR(BIF_P, BADARG); + BIF_ERROR(BIF_P, BADARG); } - if (is_small(BIF_ARG_1)) { - char *c; - struct Sint_buf ibuf; + res = integer_to_binary(BIF_P, BIF_ARG_1, 10); + + if (is_non_value(res)) { + Eterm args[1]; + args[0] = BIF_ARG_1; + return erts_schedule_bif(BIF_P, + args, + BIF_I, + integer_to_binary_1, + ERTS_SCHED_DIRTY_CPU, + am_erlang, + am_integer_to_binary, + 1); + } - /* Enhancement: If we can calculate the buffer size exactly - * we could avoid an unnecessary copy of buffers. - * Useful if size determination is faster than a copy. - */ - c = Sint_to_buf(signed_val(BIF_ARG_1), &ibuf); - size = sys_strlen(c); - res = new_binary(BIF_P, (byte *)c, size); - } else { - byte* bytes; - Uint n = 0; + return res; +} - /* Here we also have multiple copies of buffers - * due to new_binary interface - */ - size = big_decimal_estimate(BIF_ARG_1) - 1; /* remove null */ - bytes = (byte*) erts_alloc(ERTS_ALC_T_TMP, sizeof(byte)*size); - n = erts_big_to_binary_bytes(BIF_ARG_1, (char *)bytes, size); - res = new_binary(BIF_P, bytes + size - n, n); - erts_free(ERTS_ALC_T_TMP, (void *) bytes); +BIF_RETTYPE integer_to_binary_2(BIF_ALIST_2) +{ + Eterm res; + SWord base; + + if (is_not_integer(BIF_ARG_1) || is_not_small(BIF_ARG_2)) { + BIF_ERROR(BIF_P, BADARG); + } + + base = signed_val(BIF_ARG_2); + if (base < 2 || base > 36) { + BIF_ERROR(BIF_P, BADARG); } - BIF_RET(res); + + res = integer_to_binary(BIF_P, BIF_ARG_1, base); + + if (is_non_value(res)) { + Eterm args[2]; + args[0] = BIF_ARG_1; + args[1] = BIF_ARG_2; + return erts_schedule_bif(BIF_P, + args, + BIF_I, + integer_to_binary_2, + ERTS_SCHED_DIRTY_CPU, + am_erlang, + am_integer_to_binary, + 2); + } + + return res; } #define ERTS_B2L_BYTES_PER_REDUCTION 256 diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index 81531f6cc8..273b598562 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -82,7 +82,7 @@ process_info(fmtfn_t to, void *to_arg) * they are most likely just created and has invalid data */ if (!ERTS_PROC_IS_EXITING(p) && p->heap != NULL) - print_process_info(to, to_arg, p); + print_process_info(to, to_arg, p, 0); } } @@ -101,7 +101,7 @@ process_killer(void) rp = erts_pix2proc(i); if (rp && rp->i != ENULL) { int br; - print_process_info(ERTS_PRINT_STDOUT, NULL, rp); + print_process_info(ERTS_PRINT_STDOUT, NULL, rp, 0); erts_printf("(k)ill (n)ext (r)eturn:\n"); while(1) { if ((j = sys_get_key(0)) <= 0) @@ -200,13 +200,14 @@ static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext) /* Display info about an individual Erlang process */ void -print_process_info(fmtfn_t to, void *to_arg, Process *p) +print_process_info(fmtfn_t to, void *to_arg, Process *p, ErtsProcLocks orig_locks) { int garbing = 0; int running = 0; Sint len; struct saved_calls *scb; erts_aint32_t state; + ErtsProcLocks locks = orig_locks; /* display the PID */ erts_print(to, to_arg, "=proc:%T\n", p->common.id); @@ -223,6 +224,22 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p) | ERTS_PSFLG_DIRTY_RUNNING)) running = 1; + if (!(locks & ERTS_PROC_LOCK_MAIN)) { + locks |= ERTS_PROC_LOCK_MAIN; + if (ERTS_IS_CRASH_DUMPING && running) { + if (erts_proc_trylock(p, locks)) { + /* crash dumping and main lock taken, this probably means that + the process is doing a GC on a dirty-scheduler... so we cannot + do erts_proc_sig_fetch as that would potentially cause a segfault */ + locks = 0; + } + } else { + erts_proc_lock(p, locks); + } + } else { + ERTS_ASSERT(locks == ERTS_PROC_LOCK_MAIN && "Only main lock should be held"); + } + /* * If the process is registered as a global process, display the * registered name @@ -252,13 +269,19 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p) erts_print(to, to_arg, "Spawned by: %T\n", p->parent); - erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ); - len = erts_proc_sig_fetch(p); - erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ); + if (locks & ERTS_PROC_LOCK_MAIN) { + erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ); + len = erts_proc_sig_fetch(p); + erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ); + } else { + len = p->sig_qs.len; + } erts_print(to, to_arg, "Message queue length: %d\n", len); - /* display the message queue only if there is anything in it */ - if (!ERTS_IS_CRASH_DUMPING && p->sig_qs.first != NULL && !garbing) { + /* display the message queue only if there is anything in it + and we can do it safely */ + if (!ERTS_IS_CRASH_DUMPING && p->sig_qs.first != NULL && !garbing + && (locks & ERTS_PROC_LOCK_MAIN)) { erts_print(to, to_arg, "Message queue: ["); ERTS_FOREACH_SIG_PRIVQS( p, mp, @@ -358,6 +381,8 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p) /* Display all states */ erts_print(to, to_arg, "Internal State: "); erts_dump_extended_process_state(to, to_arg, state); + + erts_proc_unlock(p, locks & ~orig_locks); } static void diff --git a/erts/emulator/beam/erl_afit_alloc.c b/erts/emulator/beam/erl_afit_alloc.c index 38289ea78a..f07137c883 100644 --- a/erts/emulator/beam/erl_afit_alloc.c +++ b/erts/emulator/beam/erl_afit_alloc.c @@ -102,6 +102,8 @@ erts_afalc_start(AFAllctr_t *afallctr, allctr->add_mbc = NULL; allctr->remove_mbc = NULL; allctr->largest_fblk_in_mbc = NULL; + allctr->first_fblk_in_mbc = NULL; + allctr->next_fblk_in_mbc = NULL; allctr->init_atoms = init_atoms; #ifdef ERTS_ALLOC_UTIL_HARD_DEBUG diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index b7a8b9c2d0..d238d38d27 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -42,6 +42,7 @@ #include "global.h" #include "big.h" +#include "erl_mmap.h" #include "erl_mtrace.h" #define GET_ERL_ALLOC_UTIL_IMPL #include "erl_alloc_util.h" @@ -90,6 +91,8 @@ static int initialized = 0; #define SYS_ALLOC_CARRIER_FLOOR(X) ((X) & SYS_ALLOC_CARRIER_MASK) #define SYS_ALLOC_CARRIER_CEILING(X) \ SYS_ALLOC_CARRIER_FLOOR((X) + INV_SYS_ALLOC_CARRIER_MASK) +#define SYS_PAGE_SIZE (sys_page_size) +#define SYS_PAGE_SZ_MASK ((UWord)(SYS_PAGE_SIZE - 1)) #if 0 /* Can be useful for debugging */ @@ -98,6 +101,8 @@ static int initialized = 0; /* alloc_util global parameters */ static Uint sys_alloc_carrier_size; +static Uint sys_page_size; + #if HAVE_ERTS_MSEG static Uint max_mseg_carriers; #endif @@ -872,6 +877,8 @@ static ERTS_INLINE void clr_bit(UWord* map, Uint ix) &= ~((UWord)1 << (ix % ERTS_VSPACE_WORD_BITS)); } +#ifdef DEBUG + static ERTS_INLINE int is_bit_set(UWord* map, Uint ix) { ASSERT(ix / ERTS_VSPACE_WORD_BITS < VSPACE_MAP_SZ); @@ -879,6 +886,8 @@ static ERTS_INLINE int is_bit_set(UWord* map, Uint ix) & ((UWord)1 << (ix % ERTS_VSPACE_WORD_BITS)); } +#endif + UWord erts_literal_vspace_map[VSPACE_MAP_SZ]; static void set_literal_range(void* start, Uint size) @@ -2540,9 +2549,155 @@ mbc_alloc(Allctr_t *allctr, Uint size) return BLK2UMEM(blk); } +typedef struct { + char *ptr; + UWord size; +} ErtsMemDiscardRegion; + +/* Construct a discard region for the user memory of a free block, letting the + * OS reclaim its physical memory when required. + * + * Note that we're ignoring both the footer and everything that comes before + * the minimum block size as the allocator uses those areas to manage the + * block. */ +static void ERTS_INLINE +mem_discard_start(Allctr_t *allocator, Block_t *block, + ErtsMemDiscardRegion *out) +{ + UWord size = BLK_SZ(block); + + ASSERT(size >= allocator->min_block_size); + + if (size > (allocator->min_block_size + FBLK_FTR_SZ)) { + out->size = size - allocator->min_block_size - FBLK_FTR_SZ; + } else { + out->size = 0; + } + + out->ptr = (char*)block + allocator->min_block_size; +} + +/* Expands a discard region into a neighboring free block, allowing us to + * discard the block header and first page. + * + * This is very important in small-allocation scenarios where no single block + * is large enough to be discarded on its own. */ +static void ERTS_INLINE +mem_discard_coalesce(Allctr_t *allocator, Block_t *neighbor, + ErtsMemDiscardRegion *region) +{ + char *neighbor_start; + + ASSERT(IS_FREE_BLK(neighbor)); + + neighbor_start = (char*)neighbor; + + if (region->ptr >= neighbor_start) { + char *region_start_page; + + region_start_page = region->ptr - SYS_PAGE_SIZE; + region_start_page = (char*)((UWord)region_start_page & ~SYS_PAGE_SZ_MASK); + + /* Expand if our first page begins within the previous free block's + * unused data. */ + if (region_start_page >= (neighbor_start + allocator->min_block_size)) { + region->size += (region->ptr - region_start_page) - FBLK_FTR_SZ; + region->ptr = region_start_page; + } + } else { + char *region_end_page; + UWord neighbor_size; + + ASSERT(region->ptr <= neighbor_start); + + region_end_page = region->ptr + region->size + SYS_PAGE_SIZE; + region_end_page = (char*)((UWord)region_end_page & ~SYS_PAGE_SZ_MASK); + + neighbor_size = BLK_SZ(neighbor) - FBLK_FTR_SZ; + + /* Expand if our last page ends anywhere within the next free block, + * sans the footer we'll inherit. */ + if (region_end_page < neighbor_start + neighbor_size) { + region->size += region_end_page - (region->ptr + region->size); + } + } +} + +static void ERTS_INLINE +mem_discard_finish(Allctr_t *allocator, Block_t *block, + ErtsMemDiscardRegion *region) +{ +#ifdef DEBUG + char *block_start, *block_end; + UWord block_size; + + block_size = BLK_SZ(block); + + /* Ensure that the region is completely covered by the legal area of the + * free block. This must hold even when the region is too small to be + * discarded. */ + if (region->size > 0) { + ASSERT(block_size > allocator->min_block_size + FBLK_FTR_SZ); + + block_start = (char*)block + allocator->min_block_size; + block_end = (char*)block + block_size - FBLK_FTR_SZ; + + ASSERT(region->size == 0 || + (region->ptr + region->size <= block_end && + region->ptr >= block_start && + region->size <= block_size)); + } +#else + (void)allocator; + (void)block; +#endif + + if (region->size > SYS_PAGE_SIZE) { + UWord align_offset, size; + char *ptr; + + align_offset = SYS_PAGE_SIZE - ((UWord)region->ptr & SYS_PAGE_SZ_MASK); + + size = (region->size - align_offset) & ~SYS_PAGE_SZ_MASK; + ptr = region->ptr + align_offset; + + if (size > 0) { + ASSERT(!((UWord)ptr & SYS_PAGE_SZ_MASK)); + ASSERT(!(size & SYS_PAGE_SZ_MASK)); + + erts_mem_discard(ptr, size); + } + } +} + +static void +carrier_mem_discard_free_blocks(Allctr_t *allocator, Carrier_t *carrier) +{ + static const int MAX_BLOCKS_TO_DISCARD = 100; + Block_t *block; + int i; + + block = allocator->first_fblk_in_mbc(allocator, carrier); + i = 0; + + while (block != NULL && i < MAX_BLOCKS_TO_DISCARD) { + ErtsMemDiscardRegion region; + + ASSERT(IS_FREE_BLK(block)); + + mem_discard_start(allocator, block, ®ion); + mem_discard_finish(allocator, block, ®ion); + + block = allocator->next_fblk_in_mbc(allocator, carrier, block); + i++; + } +} + static void mbc_free(Allctr_t *allctr, ErtsAlcType_t type, void *p, Carrier_t **busy_pcrr_pp) { + ErtsMemDiscardRegion discard_region = {0}; + int discard; Uint is_first_blk; Uint is_last_blk; Uint blk_sz; @@ -2558,6 +2713,21 @@ mbc_free(Allctr_t *allctr, ErtsAlcType_t type, void *p, Carrier_t **busy_pcrr_pp ASSERT(IS_MBC_BLK(blk)); ASSERT(blk_sz >= allctr->min_block_size); +#ifndef DEBUG + /* We want to mark freed blocks as reclaimable to the OS, but it's a fairly + * expensive operation which doesn't do much good if we use it again soon + * after, so we limit it to deallocations on pooled carriers. */ + discard = busy_pcrr_pp && *busy_pcrr_pp; +#else + /* Always discard in debug mode, regardless of whether we're in the pool or + * not. */ + discard = 1; +#endif + + if (discard) { + mem_discard_start(allctr, blk, &discard_region); + } + HARD_CHECK_BLK_CARRIER(allctr, blk); crr = ABLK_TO_MBC(blk); @@ -2575,6 +2745,10 @@ mbc_free(Allctr_t *allctr, ErtsAlcType_t type, void *p, Carrier_t **busy_pcrr_pp blk = PREV_BLK(blk); (*allctr->unlink_free_block)(allctr, blk); + if (discard) { + mem_discard_coalesce(allctr, blk, &discard_region); + } + blk_sz += MBC_FBLK_SZ(blk); is_first_blk = IS_MBC_FIRST_FBLK(allctr, blk); SET_MBC_FBLK_SZ(blk, blk_sz); @@ -2590,6 +2764,11 @@ mbc_free(Allctr_t *allctr, ErtsAlcType_t type, void *p, Carrier_t **busy_pcrr_pp if (IS_FREE_BLK(nxt_blk)) { /* Coalesce with next block... */ (*allctr->unlink_free_block)(allctr, nxt_blk); + + if (discard) { + mem_discard_coalesce(allctr, nxt_blk, &discard_region); + } + blk_sz += MBC_FBLK_SZ(nxt_blk); SET_MBC_FBLK_SZ(blk, blk_sz); @@ -2625,10 +2804,16 @@ mbc_free(Allctr_t *allctr, ErtsAlcType_t type, void *p, Carrier_t **busy_pcrr_pp else { (*allctr->link_free_block)(allctr, blk); HARD_CHECK_BLK_CARRIER(allctr, blk); - if (busy_pcrr_pp && *busy_pcrr_pp) + + if (discard) { + mem_discard_finish(allctr, blk, &discard_region); + } + + if (busy_pcrr_pp && *busy_pcrr_pp) { update_pooled_tree(allctr, crr, blk_sz); - else + } else { check_abandon_carrier(allctr, blk, busy_pcrr_pp); + } } } @@ -3781,6 +3966,9 @@ abandon_carrier(Allctr_t *allctr, Carrier_t *crr) unlink_carrier(&allctr->mbc_list, crr); allctr->remove_mbc(allctr, crr); + /* Mark our free blocks as unused and reclaimable to the OS. */ + carrier_mem_discard_free_blocks(allctr, crr); + cpool_insert(allctr, crr); @@ -6471,6 +6659,12 @@ erts_alcu_start(Allctr_t *allctr, AllctrInit_t *init) erts_atomic_init_nob(&allctr->cpool.stat.carriers_size, 0); erts_atomic_init_nob(&allctr->cpool.stat.no_carriers, 0); if (!init->ts && init->acul && init->acnl) { + ASSERT(allctr->add_mbc); + ASSERT(allctr->remove_mbc); + ASSERT(allctr->largest_fblk_in_mbc); + ASSERT(allctr->first_fblk_in_mbc); + ASSERT(allctr->next_fblk_in_mbc); + allctr->cpool.util_limit = init->acul; allctr->cpool.in_pool_limit = init->acnl; allctr->cpool.fblk_min_limit = init->acfml; @@ -6676,6 +6870,8 @@ erts_alcu_init(AlcUInit_t *init) #endif allow_sys_alloc_carriers = init->sac; + sys_page_size = erts_sys_get_page_size(); + #ifdef DEBUG carrier_alignment = sizeof(Unit_t); #endif diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index 9ab8589bf3..ea1afe8f58 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -684,10 +684,12 @@ struct Allctr_t_ { void (*creating_mbc) (Allctr_t *, Carrier_t *); void (*destroying_mbc) (Allctr_t *, Carrier_t *); - /* The three callbacks below are needed to support carrier migration */ + /* The five callbacks below are needed to support carrier migration. */ void (*add_mbc) (Allctr_t *, Carrier_t *); void (*remove_mbc) (Allctr_t *, Carrier_t *); UWord (*largest_fblk_in_mbc) (Allctr_t *, Carrier_t *); + Block_t * (*first_fblk_in_mbc) (Allctr_t *, Carrier_t *); + Block_t * (*next_fblk_in_mbc) (Allctr_t *, Carrier_t *, Block_t *); #if HAVE_ERTS_MSEG void* (*mseg_alloc)(Allctr_t*, Uint *size_p, Uint flags); diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.c b/erts/emulator/beam/erl_ao_firstfit_alloc.c index 0e3e4c890a..f2ad2f6532 100644 --- a/erts/emulator/beam/erl_ao_firstfit_alloc.c +++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c @@ -241,6 +241,9 @@ static void aoff_add_mbc(Allctr_t*, Carrier_t*); static void aoff_remove_mbc(Allctr_t*, Carrier_t*); static UWord aoff_largest_fblk_in_mbc(Allctr_t*, Carrier_t*); +static Block_t *aoff_first_fblk_in_mbc(Allctr_t *, Carrier_t *); +static Block_t *aoff_next_fblk_in_mbc(Allctr_t *, Carrier_t *, Block_t *); + /* Generic tree functions used by both carrier and block trees. */ static void rbt_delete(AOFF_RBTree_t** root, AOFF_RBTree_t* del); static void rbt_insert(enum AOFFSortOrder, AOFF_RBTree_t** root, AOFF_RBTree_t* blk); @@ -326,6 +329,8 @@ erts_aoffalc_start(AOFFAllctr_t *alc, allctr->add_mbc = aoff_add_mbc; allctr->remove_mbc = aoff_remove_mbc; allctr->largest_fblk_in_mbc = aoff_largest_fblk_in_mbc; + allctr->first_fblk_in_mbc = aoff_first_fblk_in_mbc; + allctr->next_fblk_in_mbc = aoff_next_fblk_in_mbc; allctr->init_atoms = init_atoms; #ifdef ERTS_ALLOC_UTIL_HARD_DEBUG @@ -1058,6 +1063,62 @@ static UWord aoff_largest_fblk_in_mbc(Allctr_t* allctr, Carrier_t* carrier) return crr->rbt_node.hdr.bhdr; } +static Block_t *aoff_first_fblk_in_mbc(Allctr_t *allctr, Carrier_t *carrier) +{ + AOFF_Carrier_t *crr = (AOFF_Carrier_t*)carrier; + + (void)allctr; + + if (crr->root) { + AOFF_RBTree_t *blk; + + /* Descend to the rightmost block of the tree. */ + for (blk = crr->root; blk->right; blk = blk->right); + + return (Block_t*)blk; + } + + return NULL; +} + +static Block_t *aoff_next_fblk_in_mbc(Allctr_t *allctr, Carrier_t *carrier, + Block_t *block) +{ + AOFF_RBTree_t *parent, *blk; + + (void)allctr; + (void)carrier; + + blk = (AOFF_RBTree_t*)block; + + if (blk->left) { + /* Descend to the rightmost block of the left subtree. */ + for (blk = blk->left; blk->right; blk = blk->right); + + return (Block_t*)blk; + } + + while (blk->parent) { + parent = blk->parent; + + /* If we ascend from the right we know we haven't visited our parent + * yet, because we always descend as far as we can to the right when + * entering a subtree. */ + if (parent->right == blk) { + ASSERT(parent->left != blk); + return (Block_t*)parent; + } + + /* If we ascend from the left we know we've already visited our + * parent, and will need to keep ascending until we do so from the + * right or reach the end of the tree. */ + ASSERT(parent->left == blk); + blk = parent; + } + + return NULL; +} + /* * info_options() */ diff --git a/erts/emulator/beam/erl_arith.c b/erts/emulator/beam/erl_arith.c index 144fb56ea5..68d1cd989e 100644 --- a/erts/emulator/beam/erl_arith.c +++ b/erts/emulator/beam/erl_arith.c @@ -52,19 +52,11 @@ static ERTS_INLINE void maybe_shrink(Process* p, Eterm* hp, Eterm res, Uint allo Uint actual; if (is_immed(res)) { - if (p->heap <= hp && hp < p->htop) { - p->htop = hp; - } - else { - erts_heap_frag_shrink(p, hp); - } + ASSERT(!(p->heap <= hp && hp < p->htop)); + erts_heap_frag_shrink(p, hp); } else if ((actual = bignum_header_arity(*hp)+1) < alloc) { - if (p->heap <= hp && hp < p->htop) { - p->htop = hp+actual; - } - else { - erts_heap_frag_shrink(p, hp+actual); - } + ASSERT(!(p->heap <= hp && hp < p->htop)); + erts_heap_frag_shrink(p, hp+actual); } } @@ -246,7 +238,7 @@ shift(Process* p, Eterm arg1, Eterm arg2, int right) BIF_ERROR(p, SYSTEM_LIMIT); } need = BIG_NEED_SIZE(ires+1); - bigp = HAlloc(p, need); + bigp = HeapFragOnlyAlloc(p, need); arg1 = big_lshift(arg1, i, bigp); maybe_shrink(p, bigp, arg1, need); if (is_nil(arg1)) { @@ -298,7 +290,7 @@ BIF_RETTYPE bnot_1(BIF_ALIST_1) ret = make_small(~signed_val(BIF_ARG_1)); } else if (is_big(BIF_ARG_1)) { Uint need = BIG_NEED_SIZE(big_size(BIF_ARG_1)+1); - Eterm* bigp = HAlloc(BIF_P, need); + Eterm* bigp = HeapFragOnlyAlloc(BIF_P, need); ret = big_bnot(BIF_ARG_1, bigp); maybe_shrink(BIF_P, bigp, ret, need); @@ -343,7 +335,7 @@ erts_mixed_plus(Process* p, Eterm arg1, Eterm arg2) if (IS_SSMALL(ires)) { return make_small(ires); } else { - hp = HAlloc(p, 2); + hp = HeapFragOnlyAlloc(p, 2); res = small_to_big(ires, hp); return res; } @@ -400,7 +392,7 @@ erts_mixed_plus(Process* p, Eterm arg1, Eterm arg2) sz2 = big_size(arg2); sz = MAX(sz1, sz2)+1; need_heap = BIG_NEED_SIZE(sz); - hp = HAlloc(p, need_heap); + hp = HeapFragOnlyAlloc(p, need_heap); res = big_plus(arg1, arg2, hp); maybe_shrink(p, hp, res, need_heap); if (is_nil(res)) { @@ -446,7 +438,7 @@ erts_mixed_plus(Process* p, Eterm arg1, Eterm arg2) do_float: f1.fd = f1.fd + f2.fd; ERTS_FP_ERROR(p, f1.fd, goto badarith); - hp = HAlloc(p, FLOAT_SIZE_OBJECT); + hp = HeapFragOnlyAlloc(p, FLOAT_SIZE_OBJECT); res = make_float(hp); PUT_DOUBLE(f1, hp); return res; @@ -488,7 +480,7 @@ erts_mixed_minus(Process* p, Eterm arg1, Eterm arg2) if (IS_SSMALL(ires)) { return make_small(ires); } else { - hp = HAlloc(p, 2); + hp = HeapFragOnlyAlloc(p, 2); res = small_to_big(ires, hp); return res; } @@ -534,7 +526,7 @@ erts_mixed_minus(Process* p, Eterm arg1, Eterm arg2) sz2 = big_size(arg2); sz = MAX(sz1, sz2)+1; need_heap = BIG_NEED_SIZE(sz); - hp = HAlloc(p, need_heap); + hp = HeapFragOnlyAlloc(p, need_heap); res = big_minus(arg1, arg2, hp); maybe_shrink(p, hp, res, need_heap); if (is_nil(res)) { @@ -589,7 +581,7 @@ erts_mixed_minus(Process* p, Eterm arg1, Eterm arg2) do_float: f1.fd = f1.fd - f2.fd; ERTS_FP_ERROR(p, f1.fd, goto badarith); - hp = HAlloc(p, FLOAT_SIZE_OBJECT); + hp = HeapFragOnlyAlloc(p, FLOAT_SIZE_OBJECT); res = make_float(hp); PUT_DOUBLE(f1, hp); return res; @@ -657,7 +649,7 @@ erts_mixed_times(Process* p, Eterm arg1, Eterm arg2) hdr = big_res[0]; arity = bignum_header_arity(hdr); ASSERT(arity == 1 || arity == 2); - hp = HAlloc(p, arity+1); + hp = HeapFragOnlyAlloc(p, arity+1); res = make_big(hp); *hp++ = hdr; *hp++ = big_res[1]; @@ -726,7 +718,7 @@ erts_mixed_times(Process* p, Eterm arg1, Eterm arg2) do_big: need_heap = BIG_NEED_SIZE(sz); - hp = HAlloc(p, need_heap); + hp = HeapFragOnlyAlloc(p, need_heap); res = big_times(arg1, arg2, hp); /* @@ -779,7 +771,7 @@ erts_mixed_times(Process* p, Eterm arg1, Eterm arg2) do_float: f1.fd = f1.fd * f2.fd; ERTS_FP_ERROR(p, f1.fd, goto badarith); - hp = HAlloc(p, FLOAT_SIZE_OBJECT); + hp = HeapFragOnlyAlloc(p, FLOAT_SIZE_OBJECT); res = make_float(hp); PUT_DOUBLE(f1, hp); return res; @@ -905,7 +897,7 @@ erts_mixed_div(Process* p, Eterm arg1, Eterm arg2) do_float: f1.fd = f1.fd / f2.fd; ERTS_FP_ERROR(p, f1.fd, goto badarith); - hp = HAlloc(p, FLOAT_SIZE_OBJECT); + hp = HeapFragOnlyAlloc(p, FLOAT_SIZE_OBJECT); PUT_DOUBLE(f1, hp); return make_float(hp); default: @@ -957,7 +949,7 @@ erts_int_div(Process* p, Eterm arg1, Eterm arg2) ires = big_size(arg2); need = BIG_NEED_SIZE(i-ires+1) + BIG_NEED_SIZE(i); - hp = HAlloc(p, need); + hp = HeapFragOnlyAlloc(p, need); arg1 = big_div(arg1, arg2, hp); maybe_shrink(p, hp, arg1, need); if (is_nil(arg1)) { @@ -1004,7 +996,7 @@ erts_int_rem(Process* p, Eterm arg1, Eterm arg2) arg1 = SMALL_ZERO; } else if (ires > 0) { Uint need = BIG_NEED_SIZE(big_size(arg1)); - Eterm* hp = HAlloc(p, need); + Eterm* hp = HeapFragOnlyAlloc(p, need); arg1 = big_rem(arg1, arg2, hp); maybe_shrink(p, hp, arg1, need); @@ -1041,7 +1033,7 @@ Eterm erts_band(Process* p, Eterm arg1, Eterm arg2) return THE_NON_VALUE; } need = BIG_NEED_SIZE(MAX(big_size(arg1), big_size(arg2)) + 1); - hp = HAlloc(p, need); + hp = HeapFragOnlyAlloc(p, need); arg1 = big_band(arg1, arg2, hp); ASSERT(is_not_nil(arg1)); maybe_shrink(p, hp, arg1, need); @@ -1069,7 +1061,7 @@ Eterm erts_bor(Process* p, Eterm arg1, Eterm arg2) return THE_NON_VALUE; } need = BIG_NEED_SIZE(MAX(big_size(arg1), big_size(arg2)) + 1); - hp = HAlloc(p, need); + hp = HeapFragOnlyAlloc(p, need); arg1 = big_bor(arg1, arg2, hp); ASSERT(is_not_nil(arg1)); maybe_shrink(p, hp, arg1, need); @@ -1097,7 +1089,7 @@ Eterm erts_bxor(Process* p, Eterm arg1, Eterm arg2) return THE_NON_VALUE; } need = BIG_NEED_SIZE(MAX(big_size(arg1), big_size(arg2)) + 1); - hp = HAlloc(p, need); + hp = HeapFragOnlyAlloc(p, need); arg1 = big_bxor(arg1, arg2, hp); ASSERT(is_not_nil(arg1)); maybe_shrink(p, hp, arg1, need); @@ -1110,7 +1102,7 @@ Eterm erts_bnot(Process* p, Eterm arg) if (is_big(arg)) { Uint need = BIG_NEED_SIZE(big_size(arg)+1); - Eterm* bigp = HAlloc(p, need); + Eterm* bigp = HeapFragOnlyAlloc(p, need); ret = big_bnot(arg, bigp); maybe_shrink(p, bigp, ret, need); @@ -1125,924 +1117,6 @@ Eterm erts_bnot(Process* p, Eterm arg) return ret; } -#define ERTS_NEED_GC(p, need) ((HEAP_LIMIT((p)) - HEAP_TOP((p))) <= (need)) - -static ERTS_INLINE void -trim_heap(Process* p, Eterm* hp, Eterm res) -{ - if (is_immed(res)) { - ASSERT(p->heap <= hp && hp <= p->htop); - p->htop = hp; - } else { - Eterm* new_htop; - ASSERT(is_big(res)); - new_htop = hp + bignum_header_arity(*hp) + 1; - ASSERT(p->heap <= new_htop && new_htop <= p->htop); - p->htop = new_htop; - } - ASSERT(p->heap <= p->htop && p->htop <= p->stop); -} - -/* - * The functions that follow are called from the emulator loop. - * They are not allowed to allocate heap fragments, but must do - * a garbage collection if there is insufficient heap space. - */ - -#define erts_heap_frag_shrink horrible error -#define maybe_shrink horrible error - -Eterm -erts_gc_mixed_plus(Process* p, Eterm* reg, Uint live) -{ - Eterm arg1; - Eterm arg2; - DECLARE_TMP(tmp_big1,0,p); - DECLARE_TMP(tmp_big2,1,p); - Eterm res; - Eterm hdr; - FloatDef f1, f2; - dsize_t sz1, sz2, sz; - int need_heap; - Eterm* hp; - Sint ires; - - arg1 = reg[live]; - arg2 = reg[live+1]; - ERTS_FP_CHECK_INIT(p); - switch (arg1 & _TAG_PRIMARY_MASK) { - case TAG_PRIMARY_IMMED1: - switch ((arg1 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - switch (arg2 & _TAG_PRIMARY_MASK) { - case TAG_PRIMARY_IMMED1: - switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - ires = signed_val(arg1) + signed_val(arg2); - if (IS_SSMALL(ires)) { - return make_small(ires); - } else { - if (ERTS_NEED_GC(p, 2)) { - erts_garbage_collect(p, 2, reg, live); - } - hp = p->htop; - p->htop += 2; - res = small_to_big(ires, hp); - return res; - } - default: - badarith: - p->freason = BADARITH; - return THE_NON_VALUE; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg2); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - if (arg1 == SMALL_ZERO) { - return arg2; - } - arg1 = small_to_big(signed_val(arg1), tmp_big1); - goto do_big; - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - f1.fd = signed_val(arg1); - GET_DOUBLE(arg2, f2); - goto do_float; - default: - goto badarith; - } - } - default: - goto badarith; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg1); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - switch (arg2 & _TAG_PRIMARY_MASK) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - if (arg2 == SMALL_ZERO) { - return arg1; - } - arg2 = small_to_big(signed_val(arg2), tmp_big2); - goto do_big; - default: - goto badarith; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg2); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - do_big: - sz1 = big_size(arg1); - sz2 = big_size(arg2); - sz = MAX(sz1, sz2)+1; - need_heap = BIG_NEED_SIZE(sz); - if (ERTS_NEED_GC(p, need_heap)) { - erts_garbage_collect(p, need_heap, reg, live+2); - if (ARG_IS_NOT_TMP(arg1,tmp_big1)) { - arg1 = reg[live]; - } - if (ARG_IS_NOT_TMP(arg2,tmp_big2)) { - arg2 = reg[live+1]; - } - } - hp = p->htop; - p->htop += need_heap; - res = big_plus(arg1, arg2, hp); - trim_heap(p, hp, res); - if (is_nil(res)) { - p->freason = SYSTEM_LIMIT; - return THE_NON_VALUE; - } - return res; - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - if (big_to_double(arg1, &f1.fd) < 0) { - goto badarith; - } - GET_DOUBLE(arg2, f2); - goto do_float; - default: - goto badarith; - } - } - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - switch (arg2 & _TAG_PRIMARY_MASK) { - case TAG_PRIMARY_IMMED1: - switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - GET_DOUBLE(arg1, f1); - f2.fd = signed_val(arg2); - goto do_float; - default: - goto badarith; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg2); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - GET_DOUBLE(arg1, f1); - if (big_to_double(arg2, &f2.fd) < 0) { - goto badarith; - } - goto do_float; - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - GET_DOUBLE(arg1, f1); - GET_DOUBLE(arg2, f2); - - do_float: - f1.fd = f1.fd + f2.fd; - ERTS_FP_ERROR(p, f1.fd, goto badarith); - if (ERTS_NEED_GC(p, FLOAT_SIZE_OBJECT)) { - erts_garbage_collect(p, FLOAT_SIZE_OBJECT, reg, live); - } - hp = p->htop; - p->htop += FLOAT_SIZE_OBJECT; - res = make_float(hp); - PUT_DOUBLE(f1, hp); - return res; - default: - goto badarith; - } - default: - goto badarith; - } - } - default: - goto badarith; - } -} - -Eterm -erts_gc_mixed_minus(Process* p, Eterm* reg, Uint live) -{ - Eterm arg1; - Eterm arg2; - DECLARE_TMP(tmp_big1,0,p); - DECLARE_TMP(tmp_big2,1,p); - Eterm hdr; - Eterm res; - FloatDef f1, f2; - dsize_t sz1, sz2, sz; - int need_heap; - Eterm* hp; - Sint ires; - - arg1 = reg[live]; - arg2 = reg[live+1]; - ERTS_FP_CHECK_INIT(p); - switch (arg1 & _TAG_PRIMARY_MASK) { - case TAG_PRIMARY_IMMED1: - switch ((arg1 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - switch (arg2 & _TAG_PRIMARY_MASK) { - case TAG_PRIMARY_IMMED1: - switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - ires = signed_val(arg1) - signed_val(arg2); - if (IS_SSMALL(ires)) { - return make_small(ires); - } else { - if (ERTS_NEED_GC(p, 2)) { - erts_garbage_collect(p, 2, reg, live); - } - hp = p->htop; - p->htop += 2; - res = small_to_big(ires, hp); - return res; - } - default: - badarith: - p->freason = BADARITH; - return THE_NON_VALUE; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg2); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - arg1 = small_to_big(signed_val(arg1), tmp_big1); - goto do_big; - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - f1.fd = signed_val(arg1); - GET_DOUBLE(arg2, f2); - goto do_float; - default: - goto badarith; - } - } - default: - goto badarith; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg1); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - switch (arg2 & _TAG_PRIMARY_MASK) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - if (arg2 == SMALL_ZERO) { - return arg1; - } - arg2 = small_to_big(signed_val(arg2), tmp_big2); - - do_big: - sz1 = big_size(arg1); - sz2 = big_size(arg2); - sz = MAX(sz1, sz2)+1; - need_heap = BIG_NEED_SIZE(sz); - if (ERTS_NEED_GC(p, need_heap)) { - erts_garbage_collect(p, need_heap, reg, live+2); - if (ARG_IS_NOT_TMP(arg1,tmp_big1)) { - arg1 = reg[live]; - } - if (ARG_IS_NOT_TMP(arg2,tmp_big2)) { - arg2 = reg[live+1]; - } - } - hp = p->htop; - p->htop += need_heap; - res = big_minus(arg1, arg2, hp); - trim_heap(p, hp, res); - if (is_nil(res)) { - p->freason = SYSTEM_LIMIT; - return THE_NON_VALUE; - } - return res; - default: - goto badarith; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg2); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - goto do_big; - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - if (big_to_double(arg1, &f1.fd) < 0) { - goto badarith; - } - GET_DOUBLE(arg2, f2); - goto do_float; - default: - goto badarith; - } - } - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - switch (arg2 & _TAG_PRIMARY_MASK) { - case TAG_PRIMARY_IMMED1: - switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - GET_DOUBLE(arg1, f1); - f2.fd = signed_val(arg2); - goto do_float; - default: - goto badarith; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg2); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - GET_DOUBLE(arg1, f1); - if (big_to_double(arg2, &f2.fd) < 0) { - goto badarith; - } - goto do_float; - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - GET_DOUBLE(arg1, f1); - GET_DOUBLE(arg2, f2); - - do_float: - f1.fd = f1.fd - f2.fd; - ERTS_FP_ERROR(p, f1.fd, goto badarith); - if (ERTS_NEED_GC(p, FLOAT_SIZE_OBJECT)) { - erts_garbage_collect(p, FLOAT_SIZE_OBJECT, reg, live); - } - hp = p->htop; - p->htop += FLOAT_SIZE_OBJECT; - res = make_float(hp); - PUT_DOUBLE(f1, hp); - return res; - default: - goto badarith; - } - default: - goto badarith; - } - } - default: - goto badarith; - } -} - -Eterm -erts_gc_mixed_times(Process* p, Eterm* reg, Uint live) -{ - Eterm arg1; - Eterm arg2; - DECLARE_TMP(tmp_big1,0,p); - DECLARE_TMP(tmp_big2,1,p); - Eterm hdr; - Eterm res; - FloatDef f1, f2; - dsize_t sz1, sz2, sz; - int need_heap; - Eterm* hp; - - arg1 = reg[live]; - arg2 = reg[live+1]; - ERTS_FP_CHECK_INIT(p); - switch (arg1 & _TAG_PRIMARY_MASK) { - case TAG_PRIMARY_IMMED1: - switch ((arg1 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - switch (arg2 & _TAG_PRIMARY_MASK) { - case TAG_PRIMARY_IMMED1: - switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - if ((arg1 == SMALL_ZERO) || (arg2 == SMALL_ZERO)) { - return(SMALL_ZERO); - } else if (arg1 == SMALL_ONE) { - return(arg2); - } else if (arg2 == SMALL_ONE) { - return(arg1); - } else { - DeclareTmpHeap(big_res,3,p); - UseTmpHeap(3,p); - - /* - * The following code is optimized for the case that - * result is small (which should be the most common case - * in practice). - */ - res = small_times(signed_val(arg1), signed_val(arg2), - big_res); - if (is_small(res)) { - UnUseTmpHeap(3,p); - return res; - } else { - /* - * The result is a a big number. - * Allocate a heap fragment and copy the result. - * Be careful to allocate exactly what we need - * to not leave any holes. - */ - Uint arity; - Uint need; - - ASSERT(is_big(res)); - hdr = big_res[0]; - arity = bignum_header_arity(hdr); - ASSERT(arity == 1 || arity == 2); - need = arity + 1; - if (ERTS_NEED_GC(p, need)) { - erts_garbage_collect(p, need, reg, live); - } - hp = p->htop; - p->htop += need; - res = make_big(hp); - *hp++ = hdr; - *hp++ = big_res[1]; - if (arity > 1) { - *hp = big_res[2]; - } - UnUseTmpHeap(3,p); - return res; - } - } - default: - badarith: - p->freason = BADARITH; - return THE_NON_VALUE; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg2); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - if (arg1 == SMALL_ZERO) - return(SMALL_ZERO); - if (arg1 == SMALL_ONE) - return(arg2); - arg1 = small_to_big(signed_val(arg1), tmp_big1); - sz = 2 + big_size(arg2); - goto do_big; - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - f1.fd = signed_val(arg1); - GET_DOUBLE(arg2, f2); - goto do_float; - default: - goto badarith; - } - } - default: - goto badarith; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg1); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - switch (arg2 & _TAG_PRIMARY_MASK) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - if (arg2 == SMALL_ZERO) - return(SMALL_ZERO); - if (arg2 == SMALL_ONE) - return(arg1); - arg2 = small_to_big(signed_val(arg2), tmp_big2); - sz = 2 + big_size(arg1); - goto do_big; - default: - goto badarith; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg2); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - sz1 = big_size(arg1); - sz2 = big_size(arg2); - sz = sz1 + sz2; - - do_big: - need_heap = BIG_NEED_SIZE(sz); - if (ERTS_NEED_GC(p, need_heap)) { - erts_garbage_collect(p, need_heap, reg, live+2); - if (ARG_IS_NOT_TMP(arg1,tmp_big1)) { - arg1 = reg[live]; - } - if (ARG_IS_NOT_TMP(arg2,tmp_big2)) { - arg2 = reg[live+1]; - } - } - hp = p->htop; - p->htop += need_heap; - res = big_times(arg1, arg2, hp); - trim_heap(p, hp, res); - - /* - * Note that the result must be big in this case, since - * at least one operand was big to begin with, and - * the absolute value of the other is > 1. - */ - - if (is_nil(res)) { - p->freason = SYSTEM_LIMIT; - return THE_NON_VALUE; - } - return res; - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - if (big_to_double(arg1, &f1.fd) < 0) { - goto badarith; - } - GET_DOUBLE(arg2, f2); - goto do_float; - default: - goto badarith; - } - } - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - switch (arg2 & _TAG_PRIMARY_MASK) { - case TAG_PRIMARY_IMMED1: - switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - GET_DOUBLE(arg1, f1); - f2.fd = signed_val(arg2); - goto do_float; - default: - goto badarith; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg2); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - GET_DOUBLE(arg1, f1); - if (big_to_double(arg2, &f2.fd) < 0) { - goto badarith; - } - goto do_float; - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - GET_DOUBLE(arg1, f1); - GET_DOUBLE(arg2, f2); - - do_float: - f1.fd = f1.fd * f2.fd; - ERTS_FP_ERROR(p, f1.fd, goto badarith); - if (ERTS_NEED_GC(p, FLOAT_SIZE_OBJECT)) { - erts_garbage_collect(p, FLOAT_SIZE_OBJECT, reg, live); - } - hp = p->htop; - p->htop += FLOAT_SIZE_OBJECT; - res = make_float(hp); - PUT_DOUBLE(f1, hp); - return res; - default: - goto badarith; - } - default: - goto badarith; - } - } - default: - goto badarith; - } -} - -Eterm -erts_gc_mixed_div(Process* p, Eterm* reg, Uint live) -{ - Eterm arg1; - Eterm arg2; - FloatDef f1, f2; - Eterm* hp; - Eterm hdr; - - arg1 = reg[live]; - arg2 = reg[live+1]; - ERTS_FP_CHECK_INIT(p); - switch (arg1 & _TAG_PRIMARY_MASK) { - case TAG_PRIMARY_IMMED1: - switch ((arg1 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - switch (arg2 & _TAG_PRIMARY_MASK) { - case TAG_PRIMARY_IMMED1: - switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - f1.fd = signed_val(arg1); - f2.fd = signed_val(arg2); - goto do_float; - default: - badarith: - p->freason = BADARITH; - return THE_NON_VALUE; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg2); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - f1.fd = signed_val(arg1); - if (big_to_double(arg2, &f2.fd) < 0) { - goto badarith; - } - goto do_float; - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - f1.fd = signed_val(arg1); - GET_DOUBLE(arg2, f2); - goto do_float; - default: - goto badarith; - } - } - default: - goto badarith; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg1); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - switch (arg2 & _TAG_PRIMARY_MASK) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - if (big_to_double(arg1, &f1.fd) < 0) { - goto badarith; - } - f2.fd = signed_val(arg2); - goto do_float; - default: - goto badarith; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg2); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - if (big_to_double(arg1, &f1.fd) < 0 || - big_to_double(arg2, &f2.fd) < 0) { - goto badarith; - } - goto do_float; - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - if (big_to_double(arg1, &f1.fd) < 0) { - goto badarith; - } - GET_DOUBLE(arg2, f2); - goto do_float; - default: - goto badarith; - } - } - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - switch (arg2 & _TAG_PRIMARY_MASK) { - case TAG_PRIMARY_IMMED1: - switch ((arg2 & _TAG_IMMED1_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_IMMED1_SMALL >> _TAG_PRIMARY_SIZE): - GET_DOUBLE(arg1, f1); - f2.fd = signed_val(arg2); - goto do_float; - default: - goto badarith; - } - case TAG_PRIMARY_BOXED: - hdr = *boxed_val(arg2); - switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { - case (_TAG_HEADER_POS_BIG >> _TAG_PRIMARY_SIZE): - case (_TAG_HEADER_NEG_BIG >> _TAG_PRIMARY_SIZE): - GET_DOUBLE(arg1, f1); - if (big_to_double(arg2, &f2.fd) < 0) { - goto badarith; - } - goto do_float; - case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - GET_DOUBLE(arg1, f1); - GET_DOUBLE(arg2, f2); - - do_float: - f1.fd = f1.fd / f2.fd; - ERTS_FP_ERROR(p, f1.fd, goto badarith); - if (ERTS_NEED_GC(p, FLOAT_SIZE_OBJECT)) { - erts_garbage_collect(p, FLOAT_SIZE_OBJECT, reg, live); - } - hp = p->htop; - p->htop += FLOAT_SIZE_OBJECT; - PUT_DOUBLE(f1, hp); - return make_float(hp); - default: - goto badarith; - } - default: - goto badarith; - } - } - default: - goto badarith; - } -} - -Eterm -erts_gc_int_div(Process* p, Eterm* reg, Uint live) -{ - Eterm arg1; - Eterm arg2; - DECLARE_TMP(tmp_big1,0,p); - DECLARE_TMP(tmp_big2,1,p); - int ires; - - arg1 = reg[live]; - arg2 = reg[live+1]; - switch (NUMBER_CODE(arg1, arg2)) { - case SMALL_SMALL: - /* This case occurs if the most negative fixnum is divided by -1. */ - ASSERT(arg2 == make_small(-1)); - arg1 = small_to_big(signed_val(arg1), tmp_big1); - /*FALLTHROUGH*/ - case BIG_SMALL: - arg2 = small_to_big(signed_val(arg2), tmp_big2); - goto L_big_div; - case SMALL_BIG: - if (arg1 != make_small(MIN_SMALL)) { - return SMALL_ZERO; - } - arg1 = small_to_big(signed_val(arg1), tmp_big1); - /*FALLTHROUGH*/ - case BIG_BIG: - L_big_div: - ires = big_ucomp(arg1, arg2); - if (ires < 0) { - arg1 = SMALL_ZERO; - } else if (ires == 0) { - arg1 = (big_sign(arg1) == big_sign(arg2)) ? - SMALL_ONE : SMALL_MINUS_ONE; - } else { - Eterm* hp; - int i = big_size(arg1); - Uint need; - - ires = big_size(arg2); - need = BIG_NEED_SIZE(i-ires+1) + BIG_NEED_SIZE(i); - if (ERTS_NEED_GC(p, need)) { - erts_garbage_collect(p, need, reg, live+2); - if (ARG_IS_NOT_TMP(arg1,tmp_big1)) { - arg1 = reg[live]; - } - if (ARG_IS_NOT_TMP(arg2,tmp_big2)) { - arg2 = reg[live+1]; - } - } - hp = p->htop; - p->htop += need; - arg1 = big_div(arg1, arg2, hp); - trim_heap(p, hp, arg1); - if (is_nil(arg1)) { - p->freason = SYSTEM_LIMIT; - return THE_NON_VALUE; - } - } - return arg1; - default: - p->freason = BADARITH; - return THE_NON_VALUE; - } -} - -Eterm -erts_gc_int_rem(Process* p, Eterm* reg, Uint live) -{ - Eterm arg1; - Eterm arg2; - DECLARE_TMP(tmp_big1,0,p); - DECLARE_TMP(tmp_big2,1,p); - int ires; - - arg1 = reg[live]; - arg2 = reg[live+1]; - switch (NUMBER_CODE(arg1, arg2)) { - case BIG_SMALL: - arg2 = small_to_big(signed_val(arg2), tmp_big2); - goto L_big_rem; - case SMALL_BIG: - if (arg1 != make_small(MIN_SMALL)) { - return arg1; - } else { - Eterm tmp; - tmp = small_to_big(signed_val(arg1), tmp_big1); - if ((ires = big_ucomp(tmp, arg2)) == 0) { - return SMALL_ZERO; - } else { - ASSERT(ires < 0); - return arg1; - } - } - /* All paths returned */ - case BIG_BIG: - L_big_rem: - ires = big_ucomp(arg1, arg2); - if (ires == 0) { - arg1 = SMALL_ZERO; - } else if (ires > 0) { - Eterm* hp; - Uint need = BIG_NEED_SIZE(big_size(arg1)); - - if (ERTS_NEED_GC(p, need)) { - erts_garbage_collect(p, need, reg, live+2); - if (ARG_IS_NOT_TMP(arg1,tmp_big1)) { - arg1 = reg[live]; - } - if (ARG_IS_NOT_TMP(arg2,tmp_big2)) { - arg2 = reg[live+1]; - } - } - hp = p->htop; - p->htop += need; - arg1 = big_rem(arg1, arg2, hp); - trim_heap(p, hp, arg1); - if (is_nil(arg1)) { - p->freason = SYSTEM_LIMIT; - return THE_NON_VALUE; - } - } - return arg1; - default: - p->freason = BADARITH; - return THE_NON_VALUE; - } -} - -#define DEFINE_GC_LOGIC_FUNC(func) \ -Eterm erts_gc_##func(Process* p, Eterm* reg, Uint live) \ -{ \ - Eterm arg1; \ - Eterm arg2; \ - DECLARE_TMP(tmp_big1,0,p); \ - DECLARE_TMP(tmp_big2,1,p); \ - Eterm* hp; \ - int need; \ - \ - arg1 = reg[live]; \ - arg2 = reg[live+1]; \ - switch (NUMBER_CODE(arg1, arg2)) { \ - case SMALL_BIG: \ - arg1 = small_to_big(signed_val(arg1), tmp_big1); \ - need = BIG_NEED_SIZE(big_size(arg2) + 1); \ - if (ERTS_NEED_GC(p, need)) { \ - erts_garbage_collect(p, need, reg, live+2); \ - arg2 = reg[live+1]; \ - } \ - break; \ - case BIG_SMALL: \ - arg2 = small_to_big(signed_val(arg2), tmp_big2); \ - need = BIG_NEED_SIZE(big_size(arg1) + 1); \ - if (ERTS_NEED_GC(p, need)) { \ - erts_garbage_collect(p, need, reg, live+2); \ - arg1 = reg[live]; \ - } \ - break; \ - case BIG_BIG: \ - need = BIG_NEED_SIZE(MAX(big_size(arg1), big_size(arg2)) + 1); \ - if (ERTS_NEED_GC(p, need)) { \ - erts_garbage_collect(p, need, reg, live+2); \ - arg1 = reg[live]; \ - arg2 = reg[live+1]; \ - } \ - break; \ - default: \ - p->freason = BADARITH; \ - return THE_NON_VALUE; \ - } \ - hp = p->htop; \ - p->htop += need; \ - arg1 = big_##func(arg1, arg2, hp); \ - trim_heap(p, hp, arg1); \ - return arg1; \ -} - -DEFINE_GC_LOGIC_FUNC(band) -DEFINE_GC_LOGIC_FUNC(bor) -DEFINE_GC_LOGIC_FUNC(bxor) - -Eterm erts_gc_bnot(Process* p, Eterm* reg, Uint live) -{ - Eterm result; - Eterm arg; - Uint need; - Eterm* bigp; - - arg = reg[live]; - if (is_not_big(arg)) { - p->freason = BADARITH; - return NIL; - } else { - need = BIG_NEED_SIZE(big_size(arg)+1); - if (ERTS_NEED_GC(p, need)) { - erts_garbage_collect(p, need, reg, live+1); - arg = reg[live]; - } - bigp = p->htop; - p->htop += need; - result = big_bnot(arg, bigp); - trim_heap(p, bigp, result); - if (is_nil(result)) { - p->freason = SYSTEM_LIMIT; - return NIL; - } - } - return result; -} - /* Needed to remove compiler optimization */ double erts_get_positive_zero_float() { return 0.0f; diff --git a/erts/emulator/beam/erl_async.c b/erts/emulator/beam/erl_async.c index 605a2b3461..44655ad5df 100644 --- a/erts/emulator/beam/erl_async.c +++ b/erts/emulator/beam/erl_async.c @@ -336,7 +336,7 @@ static ERTS_INLINE ErtsAsync *async_get(ErtsThrQ_t *q, case ERTS_THR_Q_NEED_THR_PRGR: { ErtsThrPrgrVal prgr = erts_thr_q_need_thr_progress(q); - erts_thr_progress_wakeup(NULL, prgr); + erts_thr_progress_wakeup(erts_thr_prgr_data(NULL), prgr); /* * We do no dequeue finalizing in hope that a new async * job will arrive before we are woken due to thread diff --git a/erts/emulator/beam/erl_bestfit_alloc.c b/erts/emulator/beam/erl_bestfit_alloc.c index 9cb1199c2a..ca81c14b96 100644 --- a/erts/emulator/beam/erl_bestfit_alloc.c +++ b/erts/emulator/beam/erl_bestfit_alloc.c @@ -209,6 +209,8 @@ erts_bfalc_start(BFAllctr_t *bfallctr, allctr->add_mbc = NULL; allctr->remove_mbc = NULL; allctr->largest_fblk_in_mbc = NULL; + allctr->first_fblk_in_mbc = NULL; + allctr->next_fblk_in_mbc = NULL; allctr->init_atoms = init_atoms; #ifdef ERTS_ALLOC_UTIL_HARD_DEBUG diff --git a/erts/emulator/beam/erl_bif_atomics.c b/erts/emulator/beam/erl_bif_atomics.c index 092dbb3bd3..029831bd95 100644 --- a/erts/emulator/beam/erl_bif_atomics.c +++ b/erts/emulator/beam/erl_bif_atomics.c @@ -133,7 +133,7 @@ static ERTS_INLINE Eterm bld_atomic(Process* proc, AtomicsRef* p, if ((Uint64)val <= MAX_SMALL) return make_small((Sint) val); else { - Uint hsz = ERTS_UINT64_HEAP_SIZE(val); + Uint hsz = ERTS_UINT64_HEAP_SIZE((Uint64)val); Eterm* hp = HAlloc(proc, hsz); return erts_uint64_to_big(val, &hp); } diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index d465f6c6b9..ca1ba55b22 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -1970,9 +1970,7 @@ BIF_RETTYPE erts_binary_part(Process *p, Eterm binary, Eterm epos, Eterm elen) goto badarg; } - - - hp = HAlloc(p, ERL_SUB_BIN_SIZE); + hp = HeapFragOnlyAlloc(p, ERL_SUB_BIN_SIZE); ERTS_GET_REAL_BIN(binary, orig, offset, bit_offset, bit_size); sb = (ErlSubBin *) hp; @@ -1990,100 +1988,6 @@ BIF_RETTYPE erts_binary_part(Process *p, Eterm binary, Eterm epos, Eterm elen) BIF_ERROR(p, BADARG); } -#define ERTS_NEED_GC(p, need) ((HEAP_LIMIT((p)) - HEAP_TOP((p))) <= (need)) - -BIF_RETTYPE erts_gc_binary_part(Process *p, Eterm *reg, Eterm live, int range_is_tuple) -{ - Uint pos; - Sint len; - size_t orig_size; - Eterm orig; - Uint offset; - Uint bit_offset; - Uint bit_size; - Eterm* hp; - ErlSubBin* sb; - Eterm binary; - Eterm *tp; - Eterm epos, elen; - int extra_args; - - - if (range_is_tuple) { - Eterm tpl = reg[live]; - extra_args = 1; - if (is_not_tuple(tpl)) { - goto badarg; - } - tp = tuple_val(tpl); - if (arityval(*tp) != 2) { - goto badarg; - } - - epos = tp[1]; - elen = tp[2]; - } else { - extra_args = 2; - epos = reg[live-1]; - elen = reg[live]; - } - binary = reg[live-extra_args]; - - if (is_not_binary(binary)) { - goto badarg; - } - if (!term_to_Uint(epos, &pos)) { - goto badarg; - } - if (!term_to_Sint(elen, &len)) { - goto badarg; - } - if (len < 0) { - Uint lentmp = -(Uint)len; - /* overflow */ - if ((Sint)lentmp < 0) { - goto badarg; - } - len = lentmp; - if (len > pos) { - goto badarg; - } - pos -= len; - } - /* overflow */ - if ((pos + len) < pos || (len > 0 && (pos + len) == pos)) { - goto badarg; - } - if ((orig_size = binary_size(binary)) < pos || - orig_size < (pos + len)) { - goto badarg; - } - - if (ERTS_NEED_GC(p, ERL_SUB_BIN_SIZE)) { - erts_garbage_collect(p, ERL_SUB_BIN_SIZE, reg, live+1-extra_args); /* I don't need the tuple - or indices any more */ - binary = reg[live-extra_args]; - } - - hp = p->htop; - p->htop += ERL_SUB_BIN_SIZE; - - ERTS_GET_REAL_BIN(binary, orig, offset, bit_offset, bit_size); - - sb = (ErlSubBin *) hp; - sb->thing_word = HEADER_SUB_BIN; - sb->size = len; - sb->offs = offset + pos; - sb->orig = orig; - sb->bitoffs = bit_offset; - sb->bitsize = 0; - sb->is_writable = 0; - - BIF_RET(make_binary(sb)); - - badarg: - BIF_ERROR(p, BADARG); -} /************************************************************* * The actual guard BIFs are in erl_bif_guard.c * but the implementation of both the non-gc and the gc diff --git a/erts/emulator/beam/erl_bif_guard.c b/erts/emulator/beam/erl_bif_guard.c index 8a5c6ada6c..c921b66a7e 100644 --- a/erts/emulator/beam/erl_bif_guard.c +++ b/erts/emulator/beam/erl_bif_guard.c @@ -19,7 +19,12 @@ */ /* - * Numeric guard BIFs. + * This file implements the former GC BIFs. They used to do a GC when + * they needed heap space. Because of changes to the implementation of + * literals, those BIFs are now allowed to allocate heap fragments + * (using HeapFragOnlyAlloc()). Note that they must NOT call HAlloc(), + * because the caller does not do any SWAPIN / SWAPOUT (that is, + * HEAP_TOP(p) and HEAP_LIMIT(p) contain stale values). */ #ifdef HAVE_CONFIG_H @@ -36,14 +41,16 @@ #include "erl_binary.h" #include "erl_map.h" -static Eterm gc_double_to_integer(Process* p, double x, Eterm* reg, Uint live); - static Eterm double_to_integer(Process* p, double x); +static BIF_RETTYPE erlang_length_trap(BIF_ALIST_3); +static Export erlang_length_export; -/* - * Guard BIFs called using apply/3 and guard BIFs that never build - * anything on the heap. - */ +void erts_init_bif_guard(void) +{ + erts_init_trap_export(&erlang_length_export, + am_erlang, am_length, 3, + &erlang_length_trap); +} BIF_RETTYPE abs_1(BIF_ALIST_1) { @@ -56,7 +63,7 @@ BIF_RETTYPE abs_1(BIF_ALIST_1) i0 = signed_val(BIF_ARG_1); i = ERTS_SMALL_ABS(i0); if (i0 == MIN_SMALL) { - hp = HAlloc(BIF_P, BIG_UINT_HEAP_SIZE); + hp = HeapFragOnlyAlloc(BIF_P, BIG_UINT_HEAP_SIZE); BIF_RET(uint_to_big(i, hp)); } else { BIF_RET(make_small(i)); @@ -68,7 +75,7 @@ BIF_RETTYPE abs_1(BIF_ALIST_1) int sz = big_arity(BIF_ARG_1) + 1; Uint* x; - hp = HAlloc(BIF_P, sz); /* See note at beginning of file */ + hp = HeapFragOnlyAlloc(BIF_P, sz); /* See note at beginning of file */ sz--; res = make_big(hp); x = big_val(BIF_ARG_1); @@ -83,7 +90,7 @@ BIF_RETTYPE abs_1(BIF_ALIST_1) GET_DOUBLE(BIF_ARG_1, f); if (f.fd < 0.0) { - hp = HAlloc(BIF_P, FLOAT_SIZE_OBJECT); + hp = HeapFragOnlyAlloc(BIF_P, FLOAT_SIZE_OBJECT); f.fd = fabs(f.fd); res = make_float(hp); PUT_DOUBLE(f, hp); @@ -116,7 +123,7 @@ BIF_RETTYPE float_1(BIF_ALIST_1) } else if (big_to_double(BIF_ARG_1, &f.fd) < 0) { goto badarg; } - hp = HAlloc(BIF_P, FLOAT_SIZE_OBJECT); + hp = HeapFragOnlyAlloc(BIF_P, FLOAT_SIZE_OBJECT); res = make_float(hp); PUT_DOUBLE(f, hp); BIF_RET(res); @@ -194,26 +201,113 @@ BIF_RETTYPE round_1(BIF_ALIST_1) BIF_RET(res); } +/* + * This version of length/1 is called from native code and apply/3. + */ + BIF_RETTYPE length_1(BIF_ALIST_1) { + Eterm args[3]; + + /* + * Arrange argument registers the way expected by + * erts_trapping_length_1(). We save the original argument in + * args[2] in case an error should signaled. + */ + + args[0] = BIF_ARG_1; + args[1] = make_small(0); + args[2] = BIF_ARG_1; + return erlang_length_trap(BIF_P, args, A__I); +} + +static BIF_RETTYPE erlang_length_trap(BIF_ALIST_3) +{ + Eterm res; + + res = erts_trapping_length_1(BIF_P, BIF__ARGS); + if (is_value(res)) { /* Success. */ + BIF_RET(res); + } else { /* Trap or error. */ + if (BIF_P->freason == TRAP) { + /* + * The available reductions were exceeded. Trap. + */ + BIF_TRAP3(&erlang_length_export, BIF_P, BIF_ARG_1, BIF_ARG_2, BIF_ARG_3); + } else { + /* + * Signal an error. The original argument was tucked away in BIF_ARG_3. + */ + ERTS_BIF_ERROR_TRAPPED1(BIF_P, BIF_P->freason, + bif_export[BIF_length_1], BIF_ARG_3); + } + } +} + +/* + * Trappable helper function for calculating length/1. + * + * When calling this function, entries in args[] should be set up as + * follows: + * + * args[0] = List to calculate length for. + * args[1] = Length accumulator (tagged integer). + * + * If the return value is a tagged integer, the length was calculated + * successfully. + * + * Otherwise, if return value is THE_NON_VALUE and p->freason is TRAP, + * the available reductions were exceeded and this function must be called + * again after rescheduling. args[0] and args[1] have been updated to + * contain the next part of the list and length so far, respectively. + * + * Otherwise, if return value is THE_NON_VALUE, the list did not end + * in an empty list (and p->freason is BADARG). + */ + +Eterm erts_trapping_length_1(Process* p, Eterm* args) +{ Eterm list; Uint i; - - if (is_nil(BIF_ARG_1)) - BIF_RET(SMALL_ZERO); - if (is_not_list(BIF_ARG_1)) { - BIF_ERROR(BIF_P, BADARG); - } - list = BIF_ARG_1; - i = 0; - while (is_list(list)) { - i++; + Uint max_iter; + Uint saved_max_iter; + +#if defined(DEBUG) || defined(VALGRIND) + max_iter = 50; +#else + max_iter = ERTS_BIF_REDS_LEFT(p) * 16; +#endif + saved_max_iter = max_iter; + ASSERT(max_iter > 0); + + list = args[0]; + i = unsigned_val(args[1]); + while (is_list(list) && max_iter != 0) { list = CDR(list_val(list)); + i++, max_iter--; + } + + if (is_list(list)) { + /* + * We have exceeded the alloted number of iterations. + * Save the result so far and signal a trap. + */ + args[0] = list; + args[1] = make_small(i); + p->freason = TRAP; + BUMP_ALL_REDS(p); + return THE_NON_VALUE; + } else if (is_not_nil(list)) { + /* Error. Should be NIL. */ + BIF_ERROR(p, BADARG); } - if (is_not_nil(list)) { - BIF_ERROR(BIF_P, BADARG); - } - BIF_RET(make_small(i)); + + /* + * We reached the end of the list successfully. Bump reductions + * and return result. + */ + BUMP_REDS(p, saved_max_iter / 16); + return make_small(i); } /* returns the size of a tuple or a binary */ @@ -229,7 +323,7 @@ BIF_RETTYPE size_1(BIF_ALIST_1) if (IS_USMALL(0, sz)) { return make_small(sz); } else { - Eterm* hp = HAlloc(BIF_P, BIG_UINT_HEAP_SIZE); + Eterm* hp = HeapFragOnlyAlloc(BIF_P, BIG_UINT_HEAP_SIZE); BIF_RET(uint_to_big(sz, hp)); } } @@ -252,12 +346,12 @@ BIF_RETTYPE bit_size_1(BIF_ALIST_1) if (IS_USMALL(0,low_bits)) { BIF_RET(make_small(low_bits)); } else { - Eterm* hp = HAlloc(BIF_P, BIG_UINT_HEAP_SIZE); + Eterm* hp = HeapFragOnlyAlloc(BIF_P, BIG_UINT_HEAP_SIZE); BIF_RET(uint_to_big(low_bits, hp)); } } else { Uint sz = BIG_UINT_HEAP_SIZE+1; - Eterm* hp = HAlloc(BIF_P, sz); + Eterm* hp = HeapFragOnlyAlloc(BIF_P, sz); hp[0] = make_pos_bignum_header(sz-1); BIG_DIGIT(hp,0) = low_bits; BIG_DIGIT(hp,1) = high_bits; @@ -281,7 +375,7 @@ BIF_RETTYPE byte_size_1(BIF_ALIST_1) if (IS_USMALL(0, bytesize)) { BIF_RET(make_small(bytesize)); } else { - Eterm* hp = HAlloc(BIF_P, BIG_UINT_HEAP_SIZE); + Eterm* hp = HeapFragOnlyAlloc(BIF_P, BIG_UINT_HEAP_SIZE); BIF_RET(uint_to_big(bytesize, hp)); } } else { @@ -325,7 +419,7 @@ double_to_integer(Process* p, double x) } sz = BIG_NEED_SIZE(ds); /* number of words including arity */ - hp = HAlloc(p, sz); + hp = HeapFragOnlyAlloc(p, sz); res = make_big(hp); xp = (ErtsDigit*) (hp + 1); @@ -371,389 +465,3 @@ BIF_RETTYPE binary_part_2(BIF_ALIST_2) badarg: BIF_ERROR(BIF_P,BADARG); } - - -/* - * The following code is used when a guard that may build on the - * heap is called directly. They must not use HAlloc(), but must - * do a garbage collection if there is insufficient heap space. - * - * Important note: All error checking MUST be done before doing - * a garbage collection. The compiler assumes that all registers - * are still valid if a guard BIF generates an exception. - */ - -#define ERTS_NEED_GC(p, need) ((HEAP_LIMIT((p)) - HEAP_TOP((p))) <= (need)) - -Eterm erts_gc_length_1(Process* p, Eterm* reg, Uint live) -{ - Eterm list = reg[live]; - int i; - - if (is_nil(list)) - return SMALL_ZERO; - i = 0; - while (is_list(list)) { - i++; - list = CDR(list_val(list)); - } - if (is_not_nil(list)) { - BIF_ERROR(p, BADARG); - } - return make_small(i); -} - -Eterm erts_gc_size_1(Process* p, Eterm* reg, Uint live) -{ - Eterm arg = reg[live]; - if (is_tuple(arg)) { - Eterm* tupleptr = tuple_val(arg); - return make_small(arityval(*tupleptr)); - } else if (is_binary(arg)) { - Uint sz = binary_size(arg); - if (IS_USMALL(0, sz)) { - return make_small(sz); - } else { - Eterm* hp; - if (ERTS_NEED_GC(p, BIG_UINT_HEAP_SIZE)) { - erts_garbage_collect(p, BIG_UINT_HEAP_SIZE, reg, live); - } - hp = p->htop; - p->htop += BIG_UINT_HEAP_SIZE; - return uint_to_big(sz, hp); - } - } - BIF_ERROR(p, BADARG); -} - -Eterm erts_gc_bit_size_1(Process* p, Eterm* reg, Uint live) -{ - Eterm arg = reg[live]; - if (is_binary(arg)) { - Uint low_bits; - Uint bytesize; - Uint high_bits; - bytesize = binary_size(arg); - high_bits = bytesize >> ((sizeof(Uint) * 8)-3); - low_bits = (bytesize << 3) + binary_bitsize(arg); - if (high_bits == 0) { - if (IS_USMALL(0,low_bits)) { - return make_small(low_bits); - } else { - Eterm* hp; - if (ERTS_NEED_GC(p, BIG_UINT_HEAP_SIZE)) { - erts_garbage_collect(p, BIG_UINT_HEAP_SIZE, reg, live); - } - hp = p->htop; - p->htop += BIG_UINT_HEAP_SIZE; - return uint_to_big(low_bits, hp); - } - } else { - Uint sz = BIG_UINT_HEAP_SIZE+1; - Eterm* hp; - if (ERTS_NEED_GC(p, sz)) { - erts_garbage_collect(p, sz, reg, live); - } - hp = p->htop; - p->htop += sz; - hp[0] = make_pos_bignum_header(sz-1); - BIG_DIGIT(hp,0) = low_bits; - BIG_DIGIT(hp,1) = high_bits; - return make_big(hp); - } - } else { - BIF_ERROR(p, BADARG); - } -} - -Eterm erts_gc_byte_size_1(Process* p, Eterm* reg, Uint live) -{ - Eterm arg = reg[live]; - if (is_binary(arg)) { - Uint bytesize = binary_size(arg); - if (binary_bitsize(arg) > 0) { - bytesize++; - } - if (IS_USMALL(0, bytesize)) { - return make_small(bytesize); - } else { - Eterm* hp; - if (ERTS_NEED_GC(p, BIG_UINT_HEAP_SIZE)) { - erts_garbage_collect(p, BIG_UINT_HEAP_SIZE, reg, live); - } - hp = p->htop; - p->htop += BIG_UINT_HEAP_SIZE; - return uint_to_big(bytesize, hp); - } - } else { - BIF_ERROR(p, BADARG); - } -} - -Eterm erts_gc_map_size_1(Process* p, Eterm* reg, Uint live) -{ - Eterm arg = reg[live]; - if (is_flatmap(arg)) { - flatmap_t *mp = (flatmap_t*)flatmap_val(arg); - return make_small(flatmap_get_size(mp)); - } else if (is_hashmap(arg)) { - Eterm* hp; - Uint size; - size = hashmap_size(arg); - if (IS_USMALL(0, size)) { - return make_small(size); - } - if (ERTS_NEED_GC(p, BIG_UINT_HEAP_SIZE)) { - erts_garbage_collect(p, BIG_UINT_HEAP_SIZE, reg, live); - } - hp = p->htop; - p->htop += BIG_UINT_HEAP_SIZE; - return uint_to_big(size, hp); - } - p->fvalue = arg; - BIF_ERROR(p, BADMAP); -} - -Eterm erts_gc_abs_1(Process* p, Eterm* reg, Uint live) -{ - Eterm arg; - Eterm res; - Sint i0, i; - Eterm* hp; - - arg = reg[live]; - - /* integer arguments */ - if (is_small(arg)) { - i0 = signed_val(arg); - i = ERTS_SMALL_ABS(i0); - if (i0 == MIN_SMALL) { - if (ERTS_NEED_GC(p, BIG_UINT_HEAP_SIZE)) { - erts_garbage_collect(p, BIG_UINT_HEAP_SIZE, reg, live+1); - arg = reg[live]; - } - hp = p->htop; - p->htop += BIG_UINT_HEAP_SIZE; - return uint_to_big(i, hp); - } else { - return make_small(i); - } - } else if (is_big(arg)) { - if (!big_sign(arg)) { - return arg; - } else { - int sz = big_arity(arg) + 1; - Uint* x; - - if (ERTS_NEED_GC(p, sz)) { - erts_garbage_collect(p, sz, reg, live+1); - arg = reg[live]; - } - hp = p->htop; - p->htop += sz; - sz--; - res = make_big(hp); - x = big_val(arg); - *hp++ = make_pos_bignum_header(sz); - x++; /* skip thing */ - while(sz--) - *hp++ = *x++; - return res; - } - } else if (is_float(arg)) { - FloatDef f; - - GET_DOUBLE(arg, f); - if (f.fd < 0.0) { - if (ERTS_NEED_GC(p, FLOAT_SIZE_OBJECT)) { - erts_garbage_collect(p, FLOAT_SIZE_OBJECT, reg, live+1); - arg = reg[live]; - } - hp = p->htop; - p->htop += FLOAT_SIZE_OBJECT; - f.fd = fabs(f.fd); - res = make_float(hp); - PUT_DOUBLE(f, hp); - return res; - } - else - return arg; - } - BIF_ERROR(p, BADARG); -} - -Eterm erts_gc_float_1(Process* p, Eterm* reg, Uint live) -{ - Eterm arg; - Eterm res; - Eterm* hp; - FloatDef f; - - /* check args */ - arg = reg[live]; - if (is_not_integer(arg)) { - if (is_float(arg)) { - return arg; - } else { - badarg: - BIF_ERROR(p, BADARG); - } - } - if (is_small(arg)) { - Sint i = signed_val(arg); - f.fd = i; /* use "C"'s auto casting */ - } else if (big_to_double(arg, &f.fd) < 0) { - goto badarg; - } - if (ERTS_NEED_GC(p, FLOAT_SIZE_OBJECT)) { - erts_garbage_collect(p, FLOAT_SIZE_OBJECT, reg, live+1); - arg = reg[live]; - } - hp = p->htop; - p->htop += FLOAT_SIZE_OBJECT; - res = make_float(hp); - PUT_DOUBLE(f, hp); - return res; -} - -Eterm erts_gc_round_1(Process* p, Eterm* reg, Uint live) -{ - Eterm arg; - FloatDef f; - - arg = reg[live]; - if (is_not_float(arg)) { - if (is_integer(arg)) { - return arg; - } - BIF_ERROR(p, BADARG); - } - GET_DOUBLE(arg, f); - - return gc_double_to_integer(p, round(f.fd), reg, live); -} - -Eterm erts_gc_trunc_1(Process* p, Eterm* reg, Uint live) -{ - Eterm arg; - FloatDef f; - - arg = reg[live]; - if (is_not_float(arg)) { - if (is_integer(arg)) { - return arg; - } - BIF_ERROR(p, BADARG); - } - /* get the float */ - GET_DOUBLE(arg, f); - - /* truncate it and return the resultant integer */ - return gc_double_to_integer(p, (f.fd >= 0.0) ? floor(f.fd) : ceil(f.fd), - reg, live); -} - -Eterm erts_gc_floor_1(Process* p, Eterm* reg, Uint live) -{ - Eterm arg; - FloatDef f; - - arg = reg[live]; - if (is_not_float(arg)) { - if (is_integer(arg)) { - return arg; - } - BIF_ERROR(p, BADARG); - } - GET_DOUBLE(arg, f); - return gc_double_to_integer(p, floor(f.fd), reg, live); -} - -Eterm erts_gc_ceil_1(Process* p, Eterm* reg, Uint live) -{ - Eterm arg; - FloatDef f; - - arg = reg[live]; - if (is_not_float(arg)) { - if (is_integer(arg)) { - return arg; - } - BIF_ERROR(p, BADARG); - } - GET_DOUBLE(arg, f); - return gc_double_to_integer(p, ceil(f.fd), reg, live); -} - -static Eterm -gc_double_to_integer(Process* p, double x, Eterm* reg, Uint live) -{ - int is_negative; - int ds; - ErtsDigit* xp; - int i; - Eterm res; - size_t sz; - Eterm* hp; - double dbase; - - if ((x < (double) (MAX_SMALL+1)) && (x > (double) (MIN_SMALL-1))) { - Sint xi = x; - return make_small(xi); - } - - if (x >= 0) { - is_negative = 0; - } else { - is_negative = 1; - x = -x; - } - - /* Unscale & (calculate exponent) */ - ds = 0; - dbase = ((double)(D_MASK)+1); - while(x >= 1.0) { - x /= dbase; /* "shift" right */ - ds++; - } - sz = BIG_NEED_SIZE(ds); /* number of words including arity */ - if (ERTS_NEED_GC(p, sz)) { - erts_garbage_collect(p, sz, reg, live); - } - hp = p->htop; - p->htop += sz; - res = make_big(hp); - xp = (ErtsDigit*) (hp + 1); - - for (i = ds-1; i >= 0; i--) { - ErtsDigit d; - - x *= dbase; /* "shift" left */ - d = x; /* trunc */ - xp[i] = d; /* store digit */ - x -= d; /* remove integer part */ - } - while ((ds & (BIG_DIGITS_PER_WORD-1)) != 0) { - xp[ds++] = 0; - } - - if (is_negative) { - *hp = make_neg_bignum_header(sz-1); - } else { - *hp = make_pos_bignum_header(sz-1); - } - return res; -} - -/******************************************************************************** - * binary_part guards. The actual implementation is in erl_bif_binary.c - ********************************************************************************/ -Eterm erts_gc_binary_part_3(Process* p, Eterm* reg, Uint live) -{ - return erts_gc_binary_part(p,reg,live,0); -} - -Eterm erts_gc_binary_part_2(Process* p, Eterm* reg, Uint live) -{ - return erts_gc_binary_part(p,reg,live,1); -} diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 3b0f0d33fa..8fb8bd2831 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -3137,6 +3137,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) DECL_AM(tag); BIF_RET(AM_tag); #endif + } else if (ERTS_IS_ATOM_STR("system_logger", BIF_ARG_1)) { + BIF_RET(erts_get_system_logger()); } BIF_ERROR(BIF_P, BADARG); @@ -4606,6 +4608,7 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) } } else if (ERTS_IS_ATOM_STR("broken_halt", BIF_ARG_1)) { + erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); broken_halt_test(BIF_ARG_2); } else if (ERTS_IS_ATOM_STR("unique_monotonic_integer_state", BIF_ARG_1)) { diff --git a/erts/emulator/beam/erl_bif_lists.c b/erts/emulator/beam/erl_bif_lists.c index 9b6b84d000..b23fa77f5f 100644 --- a/erts/emulator/beam/erl_bif_lists.c +++ b/erts/emulator/beam/erl_bif_lists.c @@ -1040,7 +1040,8 @@ BIF_RETTYPE lists_member_2(BIF_ALIST_2) Eterm list; Eterm item; int non_immed_key; - int max_iter = 10 * CONTEXT_REDS; + int reds_left = ERTS_BIF_REDS_LEFT(BIF_P); + int max_iter = 16 * reds_left; if (is_nil(BIF_ARG_2)) { BIF_RET(am_false); @@ -1058,14 +1059,15 @@ BIF_RETTYPE lists_member_2(BIF_ALIST_2) } item = CAR(list_val(list)); if ((item == term) || (non_immed_key && eq(item, term))) { - BIF_RET2(am_true, CONTEXT_REDS - max_iter/10); + BIF_RET2(am_true, reds_left - max_iter/16); } list = CDR(list_val(list)); } if (is_not_nil(list)) { + BUMP_REDS(BIF_P, reds_left - max_iter/16); BIF_ERROR(BIF_P, BADARG); } - BIF_RET2(am_false, CONTEXT_REDS - max_iter/10); + BIF_RET2(am_false, reds_left - max_iter/16); } static BIF_RETTYPE lists_reverse_alloc(Process *c_p, @@ -1159,8 +1161,6 @@ static BIF_RETTYPE lists_reverse_onheap(Process *c_p, if (is_nil(list)) { BIF_RET(tail); } else if (is_list(list)) { - ASSERT(is_list(tail)); - if (cells_left > CELLS_PER_RED) { return lists_reverse_alloc(c_p, list, tail); } diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 7fe4e02782..ed825d3dda 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -891,10 +891,6 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) driver = &spawn_driver; } else if (*tp == am_fd) { /* An fd port */ - int n; - struct Sint_buf sbuf; - char* p; - if (arity != make_arityval(3)) { goto badarg; } @@ -904,15 +900,9 @@ open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump) opts.ifd = unsigned_val(tp[1]); opts.ofd = unsigned_val(tp[2]); - /* Syntesize name from input and output descriptor. */ - name_buf = erts_alloc(ERTS_ALC_T_TMP, - 2*sizeof(struct Sint_buf) + 2); - p = Sint_to_buf(opts.ifd, &sbuf); - n = sys_strlen(p); - sys_strncpy(name_buf, p, n); - name_buf[n] = '/'; - p = Sint_to_buf(opts.ofd, &sbuf); - sys_strcpy(name_buf+n+1, p); + /* Syntesize name from input and output descriptor. */ + name_buf = erts_alloc(ERTS_ALC_T_TMP, 256); + erts_snprintf(name_buf, 256, "%i/%i", opts.ifd, opts.ofd); driver = &fd_driver; } else { diff --git a/erts/emulator/beam/erl_binary.h b/erts/emulator/beam/erl_binary.h index 08edb43c49..4bf77988f7 100644 --- a/erts/emulator/beam/erl_binary.h +++ b/erts/emulator/beam/erl_binary.h @@ -278,7 +278,6 @@ Eterm erts_bin_bytes_to_list(Eterm previous, Eterm* hp, byte* bytes, Uint size, */ BIF_RETTYPE erts_list_to_binary_bif(Process *p, Eterm arg, Export *bif); -BIF_RETTYPE erts_gc_binary_part(Process *p, Eterm *reg, Eterm live, int range_is_tuple); BIF_RETTYPE erts_binary_part(Process *p, Eterm binary, Eterm epos, Eterm elen); diff --git a/erts/emulator/beam/erl_db_catree.c b/erts/emulator/beam/erl_db_catree.c index b642ae009d..75ac1c4a93 100644 --- a/erts/emulator/beam/erl_db_catree.c +++ b/erts/emulator/beam/erl_db_catree.c @@ -621,9 +621,9 @@ static TreeDbTerm* join_trees(TreeDbTerm *left_root_param, } #ifdef DEBUG -# define FORCE_RANDOM_SPLIT_JOIN +# define PROVOKE_RANDOM_SPLIT_JOIN #endif -#ifdef FORCE_RANDOM_SPLIT_JOIN +#ifdef PROVOKE_RANDOM_SPLIT_JOIN static int dbg_fastrand(void) { static int g_seed = 648835; @@ -631,8 +631,12 @@ static int dbg_fastrand(void) return (g_seed>>16)&0x7FFF; } -static void dbg_maybe_force_splitjoin(DbTableCATreeNode* base_node) +static void dbg_provoke_random_splitjoin(DbTableCATree* tb, + DbTableCATreeNode* base_node) { + if (tb->common.status & DB_CATREE_FORCE_SPLIT) + return; + switch (dbg_fastrand() % 8) { case 1: base_node->u.base.lock_statistics = 1+ERL_DB_CATREE_HIGH_CONTENTION_LIMIT; @@ -643,8 +647,8 @@ static void dbg_maybe_force_splitjoin(DbTableCATreeNode* base_node) } } #else -# define dbg_maybe_force_splitjoin(N) -#endif /* FORCE_RANDOM_SPLIT_JOIN */ +# define dbg_provoke_random_splitjoin(T,N) +#endif /* PROVOKE_RANDOM_SPLIT_JOIN */ static ERTS_INLINE int try_wlock_base_node(DbTableCATreeBaseNode *base_node) @@ -691,7 +695,7 @@ void wunlock_adapt_base_node(DbTableCATree* tb, DbTableCATreeNode* parent, int current_level) { - dbg_maybe_force_splitjoin(node); + dbg_provoke_random_splitjoin(tb,node); if ((!node->u.base.root && parent && !(tb->common.status & DB_CATREE_FORCE_SPLIT)) || node->u.base.lock_statistics < ERL_DB_CATREE_LOW_CONTENTION_LIMIT) { diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index a78623f490..ec7442aeeb 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -497,6 +497,7 @@ static erts_atomic32_t trace_control_word; /* This needs to be here, before the bif table... */ static Eterm db_set_trace_control_word_fake_1(BIF_ALIST_1); +static Eterm db_length_1(BIF_ALIST_1); /* ** The table of callable bif's, i e guard bif's and @@ -603,7 +604,7 @@ static DMCGuardBif guard_tab[] = }, { am_length, - &length_1, + &db_length_1, 1, DBIF_ALL }, @@ -971,6 +972,26 @@ BIF_RETTYPE db_set_trace_control_word_1(BIF_ALIST_1) BIF_RET(db_set_trace_control_word(BIF_P, BIF_ARG_1)); } +/* + * Implementation of length/1 for match specs (non-trapping). + */ +static Eterm db_length_1(BIF_ALIST_1) +{ + Eterm list; + Uint i; + + list = BIF_ARG_1; + i = 0; + while (is_list(list)) { + i++; + list = CDR(list_val(list)); + } + if (is_not_nil(list)) { + BIF_ERROR(BIF_P, BADARG); + } + BIF_RET(make_small(i)); +} + static Eterm db_set_trace_control_word_fake_1(BIF_ALIST_1) { Process *p = BIF_P; @@ -2470,7 +2491,7 @@ restart: case matchProcessDump: { erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(0); ASSERT(c_p == self); - print_process_info(ERTS_PRINT_DSBUF, (void *) dsbufp, c_p); + print_process_info(ERTS_PRINT_DSBUF, (void *) dsbufp, c_p, ERTS_PROC_LOCK_MAIN); *esp++ = new_binary(build_proc, (byte *)dsbufp->str, dsbufp->str_len); erts_destroy_tmp_dsbuf(dsbufp); diff --git a/erts/emulator/beam/erl_drv_nif.h b/erts/emulator/beam/erl_drv_nif.h index 9ef7c39d41..a5ecbfff06 100644 --- a/erts/emulator/beam/erl_drv_nif.h +++ b/erts/emulator/beam/erl_drv_nif.h @@ -54,7 +54,8 @@ enum ErlNifSelectFlags { ERL_NIF_SELECT_READ = (1 << 0), ERL_NIF_SELECT_WRITE = (1 << 1), ERL_NIF_SELECT_STOP = (1 << 2), - ERL_NIF_SELECT_CANCEL = (1 << 3) + ERL_NIF_SELECT_CANCEL = (1 << 3), + ERL_NIF_SELECT_CUSTOM_MSG= (1 << 4) }; /* diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index b4df418cd5..3a50b294d1 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -2438,27 +2438,9 @@ erts_copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap, cpy_words: ASSERT(sz >= cpy_sz); sz -= cpy_sz; - while (cpy_sz >= 8) { - cpy_sz -= 8; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - *hp++ = *fhp++; - } - switch (cpy_sz) { - case 7: *hp++ = *fhp++; - case 6: *hp++ = *fhp++; - case 5: *hp++ = *fhp++; - case 4: *hp++ = *fhp++; - case 3: *hp++ = *fhp++; - case 2: *hp++ = *fhp++; - case 1: *hp++ = *fhp++; - default: break; - } + sys_memcpy(hp, fhp, cpy_sz * sizeof(Eterm)); + hp += cpy_sz; + fhp += cpy_sz; if (oh) { /* Add to offheap list */ oh->next = off_heap->first; @@ -2477,7 +2459,7 @@ erts_copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap, *hpp = hp; for (i = 0; i < nrefs; i++) { - if (is_not_immed(refs[i])) + if (is_not_immed(refs[i]) && !erts_is_literal(refs[i],ptr_val(refs[i]))) refs[i] = offset_ptr(refs[i], offs); } bp->off_heap.first = NULL; diff --git a/erts/emulator/beam/erl_goodfit_alloc.c b/erts/emulator/beam/erl_goodfit_alloc.c index 01d4aa54ff..68b9579433 100644 --- a/erts/emulator/beam/erl_goodfit_alloc.c +++ b/erts/emulator/beam/erl_goodfit_alloc.c @@ -226,6 +226,8 @@ erts_gfalc_start(GFAllctr_t *gfallctr, allctr->add_mbc = NULL; allctr->remove_mbc = NULL; allctr->largest_fblk_in_mbc = NULL; + allctr->first_fblk_in_mbc = NULL; + allctr->next_fblk_in_mbc = NULL; allctr->init_atoms = init_atoms; #ifdef ERTS_ALLOC_UTIL_HARD_DEBUG diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c index 6ec6f8065e..ef7a55fa38 100644 --- a/erts/emulator/beam/erl_hl_timer.c +++ b/erts/emulator/beam/erl_hl_timer.c @@ -3041,15 +3041,23 @@ erts_set_port_timer(Port *c_prt, Sint64 tmo) check_canceled_queue(esdp, esdp->timer_service); - timeout_pos = get_timeout_pos(erts_get_monotonic_time(esdp), tmo); - - create_timer = (tmo < ERTS_TIMER_WHEEL_MSEC - ? create_tw_timer - : create_hl_timer); - tmr = (void *) create_timer(esdp, timeout_pos, 0, ERTS_TMR_PORT, - (void *) c_prt, c_prt->common.id, - THE_NON_VALUE, NULL, NULL, NULL); - erts_atomic_set_relb(&c_prt->common.timer, (erts_aint_t) tmr); + if (tmo == 0) { + erts_atomic_set_relb(&c_prt->common.timer, ERTS_PTMR_TIMEDOUT); + erts_port_task_schedule(c_prt->common.id, + &c_prt->timeout_task, + ERTS_PORT_TASK_TIMEOUT); + } else { + + timeout_pos = get_timeout_pos(erts_get_monotonic_time(esdp), tmo); + + create_timer = (tmo < ERTS_TIMER_WHEEL_MSEC + ? create_tw_timer + : create_hl_timer); + tmr = (void *) create_timer(esdp, timeout_pos, 0, ERTS_TMR_PORT, + (void *) c_prt, c_prt->common.id, + THE_NON_VALUE, NULL, NULL, NULL); + erts_atomic_set_relb(&c_prt->common.timer, (erts_aint_t) tmr); + } } void diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index dcf9f90c99..c0a86ea738 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -353,6 +353,7 @@ erl_init(int ncpu, erts_init_bif(); erts_init_bif_chksum(); erts_init_bif_binary(); + erts_init_bif_guard(); erts_init_bif_persistent_term(); erts_init_bif_re(); erts_init_unicode(); /* after RE to get access to PCRE unicode */ @@ -375,6 +376,28 @@ erl_init(int ncpu, } static Eterm + +erl_spawn_system_process(Process* parent, Eterm mod, Eterm func, Eterm args, + ErlSpawnOpts *so) +{ + Eterm res; + int arity; + + ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(parent)); + arity = erts_list_length(args); + + if (erts_find_function(mod, func, arity, erts_active_code_ix()) == NULL) { + erts_exit(ERTS_ERROR_EXIT, "No function %T:%T/%i\n", mod, func, arity); + } + + so->flags |= SPO_SYSTEM_PROC; + + res = erl_create_process(parent, mod, func, args, so); + + return res; +} + +static Eterm erl_first_process_otp(char* mod_name, int argc, char** argv) { int i; @@ -385,17 +408,13 @@ erl_first_process_otp(char* mod_name, int argc, char** argv) ErlSpawnOpts so; Eterm boot_mod; - if (erts_find_function(am_erl_init, am_start, 2, - erts_active_code_ix()) == NULL) { - erts_exit(ERTS_ERROR_EXIT, "No function erl_init:start/2\n"); - } - /* * We need a dummy parent process to be able to call erl_create_process(). */ erts_init_empty_process(&parent); erts_proc_lock(&parent, ERTS_PROC_LOCK_MAIN); + hp = HAlloc(&parent, argc*2 + 4); args = NIL; for (i = argc-1; i >= 0; i--) { @@ -403,49 +422,76 @@ erl_first_process_otp(char* mod_name, int argc, char** argv) args = CONS(hp, new_binary(&parent, (byte*)argv[i], len), args); hp += 2; } - boot_mod = erts_atom_put((byte *) mod_name, sys_strlen(mod_name), ERTS_ATOM_ENC_LATIN1, 1); + boot_mod = erts_atom_put((byte *) mod_name, sys_strlen(mod_name), + ERTS_ATOM_ENC_LATIN1, 1); args = CONS(hp, args, NIL); hp += 2; args = CONS(hp, boot_mod, args); - so.flags = erts_default_spo_flags|SPO_SYSTEM_PROC; - res = erl_create_process(&parent, am_erl_init, am_start, args, &so); + so.flags = erts_default_spo_flags; + res = erl_spawn_system_process(&parent, am_erl_init, am_start, args, &so); + ASSERT(is_internal_pid(res)); + erts_proc_unlock(&parent, ERTS_PROC_LOCK_MAIN); erts_cleanup_empty_process(&parent); + return res; } static Eterm erl_system_process_otp(Eterm parent_pid, char* modname, int off_heap_msgq, int prio) -{ - Eterm start_mod; - Process* parent; +{ + Process *parent; ErlSpawnOpts so; - Eterm res; - - start_mod = erts_atom_put((byte *) modname, sys_strlen(modname), ERTS_ATOM_ENC_LATIN1, 1); - if (erts_find_function(start_mod, am_start, 0, - erts_active_code_ix()) == NULL) { - erts_exit(ERTS_ERROR_EXIT, "No function %s:start/0\n", modname); - } + Eterm mod, res; parent = erts_pid2proc(NULL, 0, parent_pid, ERTS_PROC_LOCK_MAIN); + mod = erts_atom_put((byte *) modname, sys_strlen(modname), + ERTS_ATOM_ENC_LATIN1, 1); + + so.flags = erts_default_spo_flags|SPO_USE_ARGS; - so.flags = erts_default_spo_flags|SPO_SYSTEM_PROC|SPO_USE_ARGS; - if (off_heap_msgq) + if (off_heap_msgq) { so.flags |= SPO_OFF_HEAP_MSGQ; - so.min_heap_size = H_MIN_SIZE; - so.min_vheap_size = BIN_VH_MIN_SIZE; - so.max_heap_size = H_MAX_SIZE; - so.max_heap_flags = H_MAX_FLAGS; - so.priority = prio; - so.max_gen_gcs = (Uint16) erts_atomic32_read_nob(&erts_max_gen_gcs); - so.scheduler = 0; - res = erl_create_process(parent, start_mod, am_start, NIL, &so); + } + + so.min_heap_size = H_MIN_SIZE; + so.min_vheap_size = BIN_VH_MIN_SIZE; + so.max_heap_size = H_MAX_SIZE; + so.max_heap_flags = H_MAX_FLAGS; + so.priority = prio; + so.max_gen_gcs = (Uint16) erts_atomic32_read_nob(&erts_max_gen_gcs); + so.scheduler = 0; + + res = erl_spawn_system_process(parent, mod, am_start, NIL, &so); + ASSERT(is_internal_pid(res)); + erts_proc_unlock(parent, ERTS_PROC_LOCK_MAIN); + return res; } +Eterm erts_internal_spawn_system_process_3(BIF_ALIST_3) { + Eterm mod, func, args, res; + ErlSpawnOpts so; + + mod = BIF_ARG_1; + func = BIF_ARG_2; + args = BIF_ARG_3; + + ASSERT(is_atom(mod)); + ASSERT(is_atom(func)); + ASSERT(erts_list_length(args) >= 0); + + so.flags = erts_default_spo_flags; + res = erl_spawn_system_process(BIF_P, mod, func, args, &so); + + if (is_non_value(res)) { + BIF_ERROR(BIF_P, so.error_code); + } + + BIF_RET(res); +} Eterm erts_preloaded(Process* p) @@ -2320,8 +2366,8 @@ system_cleanup(int flush_async) * The exiting thread might be waiting for * us to block; need to update status... */ - erts_thr_progress_active(NULL, 0); - erts_thr_progress_prepare_wait(NULL); + erts_thr_progress_active(erts_thr_prgr_data(NULL), 0); + erts_thr_progress_prepare_wait(erts_thr_prgr_data(NULL)); } /* Wait forever... */ while (1) diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 3d6c9eb43f..93816542cd 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -125,15 +125,20 @@ BIF_RETTYPE map_size_1(BIF_ALIST_1) { flatmap_t *mp = (flatmap_t*)flatmap_val(BIF_ARG_1); BIF_RET(make_small(flatmap_get_size(mp))); } else if (is_hashmap(BIF_ARG_1)) { - Eterm *head, *hp, res; - Uint size, hsz=0; + Eterm *head; + Uint size; head = hashmap_val(BIF_ARG_1); size = head[1]; - (void) erts_bld_uint(NULL, &hsz, size); - hp = HAlloc(BIF_P, hsz); - res = erts_bld_uint(&hp, NULL, size); - BIF_RET(res); + + /* + * As long as a small has 28 bits (on a 32-bit machine) for + * the integer itself, it is impossible to build a map whose + * size would not fit in a small. Add an assertion in case we + * ever decreases the number of bits in a small. + */ + ASSERT(IS_USMALL(0, size)); + BIF_RET(make_small(size)); } BIF_P->fvalue = BIF_ARG_1; diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index a3274d7443..942bec84cf 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -532,9 +532,7 @@ erts_try_alloc_message_on_heap(Process *pp, if ((*psp) & ERTS_PSFLGS_VOLATILE_HEAP) goto in_message_fragment; - else if ( - *plp & ERTS_PROC_LOCK_MAIN - ) { + else if (*plp & ERTS_PROC_LOCK_MAIN) { try_on_heap: if (((*psp) & ERTS_PSFLGS_VOLATILE_HEAP) || (pp->flags & F_DISABLE_GC) diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 7339aa8874..a48d0391f6 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -707,6 +707,29 @@ error: return reds; } +/** @brief Create a message with the content of process independent \c msg_env. + * Invalidates \c msg_env. + */ +ErtsMessage* erts_create_message_from_nif_env(ErlNifEnv* msg_env) +{ + struct enif_msg_environment_t* menv = (struct enif_msg_environment_t*)msg_env; + ErtsMessage* mp; + + flush_env(msg_env); + mp = erts_alloc_message(0, NULL); + mp->data.heap_frag = menv->env.heap_frag; + ASSERT(mp->data.heap_frag == MBUF(&menv->phony_proc)); + if (mp->data.heap_frag != NULL) { + /* Move all offheap's from phony proc to the first fragment. + Quick and dirty... */ + ASSERT(!is_offheap(&mp->data.heap_frag->off_heap)); + mp->data.heap_frag->off_heap = MSO(&menv->phony_proc); + clear_offheap(&MSO(&menv->phony_proc)); + menv->env.heap_frag = NULL; + MBUF(&menv->phony_proc) = NULL; + } + return mp; +} int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, ErlNifEnv* msg_env, ERL_NIF_TERM msg) @@ -803,20 +826,8 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, } #endif } - flush_env(msg_env); - mp = erts_alloc_message(0, NULL); + mp = erts_create_message_from_nif_env(msg_env); ERL_MESSAGE_TOKEN(mp) = token; - mp->data.heap_frag = menv->env.heap_frag; - ASSERT(mp->data.heap_frag == MBUF(&menv->phony_proc)); - if (mp->data.heap_frag != NULL) { - /* Move all offheap's from phony proc to the first fragment. - Quick and dirty... */ - ASSERT(!is_offheap(&mp->data.heap_frag->off_heap)); - mp->data.heap_frag->off_heap = MSO(&menv->phony_proc); - clear_offheap(&MSO(&menv->phony_proc)); - menv->env.heap_frag = NULL; - MBUF(&menv->phony_proc) = NULL; - } } else { erts_literal_area_t litarea; ErlOffHeap *ohp; diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index 58a217c20b..3fd1a8fd4c 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -54,7 +54,7 @@ ** 2.13: 20.1 add enif_ioq ** 2.14: 21.0 add enif_ioq_peek_head, enif_(mutex|cond|rwlock|thread)_name ** enif_vfprintf, enif_vsnprintf, enif_make_map_from_arrays -** 2.15: 22.0 ERL_NIF_SELECT_CANCEL +** 2.15: 22.0 ERL_NIF_SELECT_CANCEL, enif_select_(read|write) */ #define ERL_NIF_MAJOR_VERSION 2 #define ERL_NIF_MINOR_VERSION 15 diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 81f64f2390..129166562d 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -210,6 +210,9 @@ ERL_NIF_API_FUNC_DECL(int,enif_vsnprintf,(char*, size_t, const char *fmt, va_lis ERL_NIF_API_FUNC_DECL(int,enif_make_map_from_arrays,(ErlNifEnv *env, ERL_NIF_TERM keys[], ERL_NIF_TERM values[], size_t cnt, ERL_NIF_TERM *map_out)); +ERL_NIF_API_FUNC_DECL(int,enif_select_x,(ErlNifEnv* env, ErlNifEvent e, enum ErlNifSelectFlags flags, void* obj, const ErlNifPid* pid, ERL_NIF_TERM msg, ErlNifEnv* msg_env)); + + /* ** ADD NEW ENTRIES HERE (before this comment) !!! */ @@ -392,6 +395,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_make_map_from_arrays,(ErlNifEnv *env, ERL_NIF_TER # define enif_vfprintf ERL_NIF_API_FUNC_MACRO(enif_vfprintf) # define enif_vsnprintf ERL_NIF_API_FUNC_MACRO(enif_vsnprintf) # define enif_make_map_from_arrays ERL_NIF_API_FUNC_MACRO(enif_make_map_from_arrays) +# define enif_select_x ERL_NIF_API_FUNC_MACRO(enif_select_x) /* ** ADD NEW ENTRIES HERE (before this comment) @@ -623,6 +627,12 @@ static ERL_NIF_INLINE ERL_NIF_TERM enif_make_list9(ErlNifEnv* env, #ifndef enif_make_pid # define enif_make_pid(ENV, PID) ((void)(ENV),(const ERL_NIF_TERM)((PID)->pid)) +# define enif_select_read(ENV, E, OBJ, PID, MSG, MSG_ENV) \ + enif_select_x(ENV, E, ERL_NIF_SELECT_READ | ERL_NIF_SELECT_CUSTOM_MSG, \ + OBJ, PID, MSG, MSG_ENV) +# define enif_select_write(ENV, E, OBJ, PID, MSG, MSG_ENV) \ + enif_select_x(ENV, E, ERL_NIF_SELECT_WRITE | ERL_NIF_SELECT_CUSTOM_MSG, \ + OBJ, PID, MSG, MSG_ENV) #if SIZEOF_LONG == 8 # define enif_get_int64 enif_get_long diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h index 2be0a5bf74..25976d38cc 100644 --- a/erts/emulator/beam/erl_port.h +++ b/erts/emulator/beam/erl_port.h @@ -334,6 +334,8 @@ Eterm erts_request_io_bytes(Process *c_p); #define ERTS_PORT_SFLG_INVALID ((Uint32) (1 << 11)) /* Last port to terminate halts the emulator */ #define ERTS_PORT_SFLG_HALT ((Uint32) (1 << 12)) +/* Check if the event in ready_input should be cleaned */ +#define ERTS_PORT_SFLG_CHECK_FD_CLEANUP ((Uint32) (1 << 13)) #ifdef DEBUG /* Only debug: make sure all flags aren't cleared unintentionally */ #define ERTS_PORT_SFLG_PORT_DEBUG ((Uint32) (1 << 31)) diff --git a/erts/emulator/beam/erl_port_task.c b/erts/emulator/beam/erl_port_task.c index 4928d80f27..c8f2e88127 100644 --- a/erts/emulator/beam/erl_port_task.c +++ b/erts/emulator/beam/erl_port_task.c @@ -97,6 +97,9 @@ static void chk_task_queues(Port *pp, ErtsPortTask *execq, int processing_busy_q typedef union { struct { /* I/O tasks */ ErlDrvEvent event; +#if ERTS_POLL_USE_SCHEDULER_POLLING + int is_scheduler_event; +#endif } io; struct { ErtsProc2PortSigCallback callback; @@ -141,6 +144,9 @@ struct ErtsPortTaskBusyCallerTable_ { ErtsPortTaskBusyCaller pre_alloc_busy_caller; }; +#if ERTS_POLL_USE_SCHEDULER_POLLING +erts_atomic_t erts_port_task_outstanding_io_tasks; +#endif static void begin_port_cleanup(Port *pp, ErtsPortTask **execq, @@ -578,13 +584,26 @@ reset_handle(ErtsPortTask *ptp) } static ERTS_INLINE void -reset_executed_io_task_handle(ErtsPortTask *ptp) +reset_executed_io_task_handle(Port *prt, ErtsPortTask *ptp) { if (ptp->u.alive.handle) { ASSERT(ptp == handle2task(ptp->u.alive.handle)); - /* The port task handle is reset inside task_executed */ - erts_io_notify_port_task_executed(ptp->type, ptp->u.alive.handle, - reset_port_task_handle); +#if ERTS_POLL_USE_SCHEDULER_POLLING + if (ptp->u.alive.td.io.is_scheduler_event) { + if ((erts_atomic32_read_nob(&prt->state) & ERTS_PORT_SFLG_CHECK_FD_CLEANUP)) { + erts_io_notify_port_task_executed(ptp->type, ptp->u.alive.handle, + reset_port_task_handle); + erts_atomic32_read_band_nob(&prt->state, ~ERTS_PORT_SFLG_CHECK_FD_CLEANUP); + } else { + reset_port_task_handle(ptp->u.alive.handle); + } + } else +#endif + { + /* The port task handle is reset inside task_executed */ + erts_io_notify_port_task_executed(ptp->type, ptp->u.alive.handle, + reset_port_task_handle); + } } } @@ -1307,6 +1326,22 @@ erts_port_task_abort(ErtsPortTaskHandle *pthp) res = - 1; /* Task already aborted, executing, or executed */ else { reset_port_task_handle(pthp); + +#if ERTS_POLL_USE_SCHEDULER_POLLING + switch (ptp->type) { + case ERTS_PORT_TASK_INPUT: + case ERTS_PORT_TASK_OUTPUT: + if (ptp->u.alive.td.io.is_scheduler_event) { + ASSERT(erts_atomic_read_nob( + &erts_port_task_outstanding_io_tasks) > 0); + erts_atomic_dec_relb(&erts_port_task_outstanding_io_tasks); + } + break; + default: + break; + } +#endif + res = 0; } } @@ -1442,7 +1477,14 @@ erts_port_task_schedule(Eterm id, va_list argp; va_start(argp, type); ptp->u.alive.td.io.event = va_arg(argp, ErlDrvEvent); +#if ERTS_POLL_USE_SCHEDULER_POLLING + ptp->u.alive.td.io.is_scheduler_event = va_arg(argp, int); +#endif va_end(argp); +#if ERTS_POLL_USE_SCHEDULER_POLLING + if (ptp->u.alive.td.io.is_scheduler_event) + erts_atomic_inc_relb(&erts_port_task_outstanding_io_tasks); +#endif break; } case ERTS_PORT_TASK_PROC_SIG: { @@ -1621,12 +1663,14 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) int processing_busy_q; int vreds = 0; int reds = 0; - erts_aint_t io_tasks_executed = 0; int fpe_was_unmasked; erts_aint32_t state; int active; Uint64 start_time = 0; ErtsSchedulerData *esdp = runq->scheduler; +#if ERTS_POLL_USE_SCHEDULER_POLLING + erts_aint_t io_tasks_executed = 0; +#endif ERTS_MSACC_PUSH_STATE_M(); ERTS_LC_ASSERT(erts_lc_runq_is_locked(runq)); @@ -1722,8 +1766,11 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) for input and output */ (*pp->drv_ptr->ready_input)((ErlDrvData) pp->drv_data, ptp->u.alive.td.io.event); - reset_executed_io_task_handle(ptp); - io_tasks_executed++; + reset_executed_io_task_handle(pp, ptp); +#if ERTS_POLL_USE_SCHEDULER_POLLING + if (ptp->u.alive.td.io.is_scheduler_event) + io_tasks_executed++; +#endif break; case ERTS_PORT_TASK_OUTPUT: reds = ERTS_PORT_REDS_OUTPUT; @@ -1732,8 +1779,11 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) LTTNG_DRIVER(driver_ready_output, pp); (*pp->drv_ptr->ready_output)((ErlDrvData) pp->drv_data, ptp->u.alive.td.io.event); - reset_executed_io_task_handle(ptp); - io_tasks_executed++; + reset_executed_io_task_handle(pp, ptp); +#if ERTS_POLL_USE_SCHEDULER_POLLING + if (ptp->u.alive.td.io.is_scheduler_event) + io_tasks_executed++; +#endif break; case ERTS_PORT_TASK_PROC_SIG: { ErtsProc2PortSigData *sigdp = &ptp->u.alive.td.psig.data; @@ -1799,6 +1849,15 @@ erts_port_task_execute(ErtsRunQueue *runq, Port **curr_port_pp) erts_unblock_fpe(fpe_was_unmasked); ERTS_MSACC_POP_STATE_M(); +#if ERTS_POLL_USE_SCHEDULER_POLLING + if (io_tasks_executed) { + ASSERT(erts_atomic_read_nob(&erts_port_task_outstanding_io_tasks) + >= io_tasks_executed); + erts_atomic_add_relb(&erts_port_task_outstanding_io_tasks, + -1*io_tasks_executed); + } +#endif + ASSERT(runq == erts_get_runq_port(pp)); active = finalize_exec(pp, &execq, processing_busy_q); @@ -2086,6 +2145,10 @@ erts_dequeue_port(ErtsRunQueue *rq) void erts_port_task_init(void) { +#if ERTS_POLL_USE_SCHEDULER_POLLING + erts_atomic_init_nob(&erts_port_task_outstanding_io_tasks, + (erts_aint_t) 0); +#endif init_port_task_alloc(erts_no_schedulers + erts_no_poll_threads + 1); /* aux_thread */ init_busy_caller_table_alloc(); diff --git a/erts/emulator/beam/erl_port_task.h b/erts/emulator/beam/erl_port_task.h index ae78a7d8a3..ca5183b305 100644 --- a/erts/emulator/beam/erl_port_task.h +++ b/erts/emulator/beam/erl_port_task.h @@ -38,6 +38,8 @@ typedef erts_atomic_t ErtsPortTaskHandle; #ifndef ERL_PORT_TASK_H__ #define ERL_PORT_TASK_H__ +#include "erl_poll.h" + #undef ERTS_INCLUDE_SCHEDULER_INTERNALS #if (defined(ERL_PROCESS_C__) \ || defined(ERL_PORT_TASK_C__) \ @@ -54,8 +56,8 @@ typedef erts_atomic_t ErtsPortTaskHandle; #define ERTS_PT_FLG_BAD_OUTPUT (1 << 4) typedef enum { - ERTS_PORT_TASK_INPUT, - ERTS_PORT_TASK_OUTPUT, + ERTS_PORT_TASK_INPUT = 0, + ERTS_PORT_TASK_OUTPUT = 1, ERTS_PORT_TASK_TIMEOUT, ERTS_PORT_TASK_DIST_CMD, ERTS_PORT_TASK_PROC_SIG @@ -134,6 +136,12 @@ ERTS_GLB_INLINE void erts_port_task_sched_unlock(ErtsPortTaskSched *ptsp); ERTS_GLB_INLINE int erts_port_task_sched_lock_is_locked(ErtsPortTaskSched *ptsp); ERTS_GLB_INLINE void erts_port_task_sched_enter_exiting_state(ErtsPortTaskSched *ptsp); +#if defined(ERTS_INCLUDE_SCHEDULER_INTERNALS) && ERTS_POLL_USE_SCHEDULER_POLLING +ERTS_GLB_INLINE int erts_port_task_have_outstanding_io_tasks(void); +/* NOTE: Do not access any of the exported variables directly */ +extern erts_atomic_t erts_port_task_outstanding_io_tasks; +#endif + #if ERTS_GLB_INLINE_INCL_FUNC_DEF ERTS_GLB_INLINE void @@ -211,6 +219,15 @@ erts_port_task_sched_enter_exiting_state(ErtsPortTaskSched *ptsp) erts_atomic32_read_bor_nob(&ptsp->flags, ERTS_PTS_FLG_EXITING); } +#if defined(ERTS_INCLUDE_SCHEDULER_INTERNALS) && ERTS_POLL_USE_SCHEDULER_POLLING +ERTS_GLB_INLINE int +erts_port_task_have_outstanding_io_tasks(void) +{ + return (erts_atomic_read_acqb(&erts_port_task_outstanding_io_tasks) + != 0); +} +#endif + #endif #ifdef ERTS_INCLUDE_SCHEDULER_INTERNALS diff --git a/erts/emulator/beam/erl_printf_term.c b/erts/emulator/beam/erl_printf_term.c index 990a01b96f..9cfb7fc681 100644 --- a/erts/emulator/beam/erl_printf_term.c +++ b/erts/emulator/beam/erl_printf_term.c @@ -364,13 +364,13 @@ print_term(fmtfn_t fn, void* arg, Eterm obj, long *dcount) { int print_res; char def_buf[64]; char *buf, *big_str; - Uint sz = (Uint) big_decimal_estimate(wobj); + Uint sz = (Uint) big_integer_estimate(wobj, 10); sz++; if (sz <= 64) buf = &def_buf[0]; else buf = erts_alloc(ERTS_ALC_T_TMP, sz); - big_str = erts_big_to_string(wobj, buf, sz); + big_str = erts_big_to_string(wobj, 10, buf, sz); print_res = erts_printf_string(fn, arg, big_str); if (buf != &def_buf[0]) erts_free(ERTS_ALC_T_TMP, (void *) buf); diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c index f343e984f7..18418a76e1 100644 --- a/erts/emulator/beam/erl_proc_sig_queue.c +++ b/erts/emulator/beam/erl_proc_sig_queue.c @@ -3812,7 +3812,6 @@ clear_seq_trace_token(ErtsMessage *sig) void erts_proc_sig_clear_seq_trace_tokens(Process *c_p) { - ASSERT(erts_thr_progress_is_blocking()); erts_proc_sig_fetch(c_p); ERTS_FOREACH_SIG_PRIVQS(c_p, sig, clear_seq_trace_token(sig)); } diff --git a/erts/emulator/beam/erl_proc_sig_queue.h b/erts/emulator/beam/erl_proc_sig_queue.h index 3fc2d06b2d..6b065a7add 100644 --- a/erts/emulator/beam/erl_proc_sig_queue.h +++ b/erts/emulator/beam/erl_proc_sig_queue.h @@ -989,8 +989,7 @@ erts_proc_sig_fetch(Process *proc) Sint res = 0; ErtsSignal *sig; - ERTS_LC_ASSERT(erts_thr_progress_is_blocking() - || ERTS_PROC_IS_EXITING(proc) + ERTS_LC_ASSERT(ERTS_PROC_IS_EXITING(proc) || ((erts_proc_lc_my_proc_locks(proc) & (ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_MSGQ)) diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index a21acb9a57..68d99e8e78 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -92,10 +92,6 @@ #undef HARDDEBUG #endif -#ifdef HARDDEBUG -#define HARDDEBUG_RUNQS -#endif - #ifdef HIPE #include "hipe_mode_switch.h" /* for hipe_init_process() */ #include "hipe_signal.h" /* for hipe_thread_signal_init() */ @@ -174,7 +170,6 @@ ErtsLcPSDLocks erts_psd_required_locks[ERTS_PSD_SIZE]; typedef struct { int aux_work; int tse; - int sys_schedule; } ErtsBusyWaitParams; static ErtsBusyWaitParams sched_busy_wait_params[ERTS_SCHED_TYPE_LAST + 1]; @@ -344,6 +339,9 @@ erts_sched_stat_t erts_sched_stat; static erts_tsd_key_t ERTS_WRITE_UNLIKELY(sched_data_key); +#if ERTS_POLL_USE_SCHEDULER_POLLING +static erts_atomic32_t doing_sys_schedule; +#endif static erts_atomic32_t no_empty_run_queues; long erts_runq_supervision_interval = 0; static ethr_event runq_supervision_event; @@ -1646,7 +1644,7 @@ haw_thr_prgr_wakeup(ErtsAuxWorkData *awdp, ErtsThrPrgrVal val) awdp->latest_wakeup = val; haw_chk_later_cleanup_op_wakeup(awdp, val); } - erts_thr_progress_wakeup(awdp->esdp, val); + erts_thr_progress_wakeup(erts_thr_prgr_data(awdp->esdp), val); } } @@ -1656,7 +1654,7 @@ haw_thr_prgr_soft_wakeup(ErtsAuxWorkData *awdp, ErtsThrPrgrVal val) if (erts_thr_progress_cmp(val, awdp->latest_wakeup) > 0) { awdp->latest_wakeup = val; haw_chk_later_cleanup_op_wakeup(awdp, val); - erts_thr_progress_wakeup(awdp->esdp, val); + erts_thr_progress_wakeup(erts_thr_prgr_data(awdp->esdp), val); } } @@ -1670,7 +1668,7 @@ haw_thr_prgr_later_cleanup_op_wakeup(ErtsAuxWorkData *awdp, ErtsThrPrgrVal val, else { awdp->latest_wakeup = val; awdp->later_op.size = thr_prgr_later_cleanup_op_threshold; - erts_thr_progress_wakeup(awdp->esdp, val); + erts_thr_progress_wakeup(erts_thr_prgr_data(awdp->esdp), val); } } } @@ -3066,6 +3064,7 @@ aux_thread(void *unused) ErtsSchedulerSleepInfo *ssi = ERTS_SCHED_SLEEP_INFO_IX(-1); erts_aint32_t aux_work; ErtsThrPrgrCallbacks callbacks; + ErtsThrPrgrData *tpd; int thr_prgr_active = 1; ERTS_MSACC_DECLARE_CACHE(); @@ -3087,12 +3086,16 @@ aux_thread(void *unused) callbacks.wait = thr_prgr_wait; callbacks.finalize_wait = thr_prgr_fin_wait; - erts_thr_progress_register_managed_thread(NULL, &callbacks, 1); + tpd = erts_thr_progress_register_managed_thread(NULL, &callbacks, 1); init_aux_work_data(awdp, NULL, NULL); awdp->ssi = ssi; #if ERTS_POLL_USE_FALLBACK - ssi->psi = erts_create_pollset_thread(-1); +#if ERTS_POLL_USE_SCHEDULER_POLLING + ssi->psi = erts_create_pollset_thread(-2, tpd); +#else + ssi->psi = erts_create_pollset_thread(-1, tpd); +#endif #endif sched_prep_spin_wait(ssi); @@ -3105,11 +3108,11 @@ aux_thread(void *unused) aux_work = erts_atomic32_read_acqb(&ssi->aux_work); if (aux_work) { if (!thr_prgr_active) - erts_thr_progress_active(NULL, thr_prgr_active = 1); + erts_thr_progress_active(tpd, thr_prgr_active = 1); aux_work = handle_aux_work(awdp, aux_work, 1); ERTS_MSACC_UPDATE_CACHE(); - if (aux_work && erts_thr_progress_update(NULL)) - erts_thr_progress_leader_update(NULL); + if (aux_work && erts_thr_progress_update(tpd)) + erts_thr_progress_leader_update(tpd); } if (!aux_work) { @@ -3120,7 +3123,7 @@ aux_thread(void *unused) #endif if (thr_prgr_active) - erts_thr_progress_active(NULL, thr_prgr_active = 0); + erts_thr_progress_active(tpd, thr_prgr_active = 0); #if ERTS_POLL_USE_FALLBACK @@ -3132,11 +3135,11 @@ aux_thread(void *unused) if (flgs & ERTS_SSI_FLG_SLEEPING) { ASSERT(flgs & ERTS_SSI_FLG_POLL_SLEEPING); ASSERT(flgs & ERTS_SSI_FLG_WAITING); - erts_check_io(ssi->psi); + erts_check_io(ssi->psi, ERTS_POLL_INF_TIMEOUT); } } #else - erts_thr_progress_prepare_wait(NULL); + erts_thr_progress_prepare_wait(tpd); flgs = sched_spin_wait(ssi, 0); @@ -3153,7 +3156,7 @@ aux_thread(void *unused) ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_OTHER); } } - erts_thr_progress_finalize_wait(NULL); + erts_thr_progress_finalize_wait(tpd); #endif } @@ -3171,7 +3174,8 @@ poll_thread(void *arg) erts_aint32_t aux_work; ErtsThrPrgrCallbacks callbacks; int thr_prgr_active = 1; - struct erts_poll_thread *psi = erts_create_pollset_thread(id); + struct erts_poll_thread *psi; + ErtsThrPrgrData *tpd; ERTS_MSACC_DECLARE_CACHE(); #ifdef ERTS_ENABLE_LOCK_CHECK @@ -3192,9 +3196,12 @@ poll_thread(void *arg) callbacks.wait = thr_prgr_wait; callbacks.finalize_wait = thr_prgr_fin_wait; - erts_thr_progress_register_managed_thread(NULL, &callbacks, 0); + tpd = erts_thr_progress_register_managed_thread(NULL, &callbacks, 0); init_aux_work_data(awdp, NULL, NULL); awdp->ssi = ssi; + + psi = erts_create_pollset_thread(id, tpd); + ssi->psi = psi; sched_prep_spin_wait(ssi); @@ -3207,16 +3214,16 @@ poll_thread(void *arg) aux_work = erts_atomic32_read_acqb(&ssi->aux_work); if (aux_work) { if (!thr_prgr_active) - erts_thr_progress_active(NULL, thr_prgr_active = 1); + erts_thr_progress_active(tpd, thr_prgr_active = 1); aux_work = handle_aux_work(awdp, aux_work, 1); ERTS_MSACC_UPDATE_CACHE(); - if (aux_work && erts_thr_progress_update(NULL)) - erts_thr_progress_leader_update(NULL); + if (aux_work && erts_thr_progress_update(tpd)) + erts_thr_progress_leader_update(tpd); } if (!aux_work) { if (thr_prgr_active) - erts_thr_progress_active(NULL, thr_prgr_active = 0); + erts_thr_progress_active(tpd, thr_prgr_active = 0); flgs = sched_spin_wait(ssi, 0); @@ -3226,7 +3233,7 @@ poll_thread(void *arg) if (flgs & ERTS_SSI_FLG_SLEEPING) { ASSERT(flgs & ERTS_SSI_FLG_POLL_SLEEPING); ASSERT(flgs & ERTS_SSI_FLG_WAITING); - erts_check_io(psi); + erts_check_io(psi, ERTS_POLL_INF_TIMEOUT); } } } @@ -3236,6 +3243,99 @@ poll_thread(void *arg) return NULL; } +#if ERTS_POLL_USE_SCHEDULER_POLLING +static ERTS_INLINE void +clear_sys_scheduling(void) +{ + erts_atomic32_set_mb(&doing_sys_schedule, 0); +} + +static ERTS_INLINE int +try_set_sys_scheduling(void) +{ + return 0 == erts_atomic32_cmpxchg_acqb(&doing_sys_schedule, 1, 0); +} + + +static ERTS_INLINE int +prepare_for_sys_schedule(void) +{ + while (!erts_port_task_have_outstanding_io_tasks() + && try_set_sys_scheduling()) { + if (!erts_port_task_have_outstanding_io_tasks()) + return 1; + clear_sys_scheduling(); + } + return 0; +} + +static void +check_io_timer(void *null) +{ + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + if (prepare_for_sys_schedule()) { + erts_check_io(esdp->ssi->psi, ERTS_POLL_NO_TIMEOUT); + clear_sys_scheduling(); + } + + /* The timer is cleared if this schedulers run-queue became empty + or if the CHECKIO flag was cleared. The CHECKIO flags is cleared + when a check_balance assigns another scheduler to be the poller in + the overload scenario. */ + if ((ERTS_RUNQ_FLGS_GET_NOB(esdp->run_queue) & (ERTS_RUNQ_FLG_OUT_OF_WORK|ERTS_RUNQ_FLG_CHECKIO)) + == ERTS_RUNQ_FLG_CHECKIO) { + erts_start_timer_callback(ERTS_POLL_SCHEDULER_POLLING_TIMEOUT, + check_io_timer, NULL); + } else { + ERTS_RUNQ_FLGS_UNSET(esdp->run_queue, ERTS_RUNQ_FLG_CHECKIO); + } +} + +#else +#define clear_sys_scheduling() +#define prepare_for_sys_schedule() 0 +#endif + +#ifdef HARDDEBUG +#define ERTS_HDBG_CHK_SLEEP_LIST(SL, L, F, FN) \ + check_sleepers_list((SL), (L), (F), (FN)) +static void check_sleepers_list(ErtsSchedulerSleepList *sl, + int lock, + ErtsSchedulerSleepInfo *find, + ErtsSchedulerSleepInfo *find_not) +{ + ErtsSchedulerSleepInfo *last_out; + int found = 0; + + if (lock) + erts_spin_lock(&sl->lock); + + ERTS_ASSERT(!find_not || (!find_not->next && !find_not->prev)); + + last_out = sl->list; + if (last_out) { + ErtsSchedulerSleepInfo *tmp = last_out; + do { + ERTS_ASSERT(tmp->next); + ERTS_ASSERT(tmp->prev); + ERTS_ASSERT(tmp->next->prev == tmp); + ERTS_ASSERT(tmp->prev->next == tmp); + ERTS_ASSERT(tmp != find_not); + if (tmp == find) + found = !0; + tmp = tmp->next; + + } while (tmp != last_out); + } + ERTS_ASSERT(!find || found); + + if (lock) + erts_spin_unlock(&sl->lock); +} +#else +#define ERTS_HDBG_CHK_SLEEP_LIST(SL, L, F, FN) ((void) 0) +#endif + static void scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) { @@ -3249,23 +3349,29 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ERTS_LC_ASSERT(erts_lc_runq_is_locked(rq)); - if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) - erts_spin_lock(&rq->sleepers.lock); flgs = sched_prep_spin_wait(ssi); if (flgs & ERTS_SSI_FLG_SUSPENDED) { /* Go suspend instead... */ - if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) - erts_spin_unlock(&rq->sleepers.lock); return; } if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) { - ssi->prev = NULL; - ssi->next = rq->sleepers.list; - if (rq->sleepers.list) - rq->sleepers.list->prev = ssi; - rq->sleepers.list = ssi; - erts_spin_unlock(&rq->sleepers.lock); + erts_spin_lock(&rq->sleepers.lock); + ERTS_HDBG_CHK_SLEEP_LIST(&rq->sleepers, 0, NULL, ssi); + ASSERT(!ssi->next); /* Not in sleepers list */ + ASSERT(!ssi->prev); + if (!rq->sleepers.list) { + ssi->next = ssi->prev = ssi; + rq->sleepers.list = ssi; + } + else { + ssi->prev = rq->sleepers.list; + ssi->next = rq->sleepers.list->next; + ssi->prev->next = ssi; + ssi->next->prev = ssi; + } + ERTS_HDBG_CHK_SLEEP_LIST(&rq->sleepers, 0, ssi, NULL); + erts_spin_unlock(&rq->sleepers.lock); dirty_active(esdp, -1); } @@ -3284,28 +3390,31 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) ErtsMonotonicTime current_time = 0; aux_work = erts_atomic32_read_acqb(&ssi->aux_work); - if (aux_work && !ERTS_SCHEDULER_IS_DIRTY(esdp)) { + + if (aux_work && ERTS_SCHEDULER_IS_DIRTY(esdp)) { + ERTS_INTERNAL_ERROR("Executing aux work on a dirty scheduler."); + } + + if (aux_work) { if (!thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); + erts_thr_progress_active(erts_thr_prgr_data(esdp), thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } aux_work = handle_aux_work(&esdp->aux_work_data, aux_work, 1); ERTS_MSACC_UPDATE_CACHE(); - if (aux_work && erts_thr_progress_update(esdp)) - erts_thr_progress_leader_update(esdp); + if (aux_work && erts_thr_progress_update(erts_thr_prgr_data(esdp))) + erts_thr_progress_leader_update(erts_thr_prgr_data(esdp)); } if (aux_work) { - if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { - flgs = erts_atomic32_read_acqb(&ssi->flags); - current_time = erts_get_monotonic_time(esdp); - if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { - if (!thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); - sched_wall_time_change(esdp, 1); - } - erts_bump_timers(esdp->timer_wheel, current_time); + flgs = erts_atomic32_read_acqb(&ssi->flags); + current_time = erts_get_monotonic_time(esdp); + if (current_time >= erts_next_timeout_time(esdp->next_tmo_ref)) { + if (!thr_prgr_active) { + erts_thr_progress_active(erts_thr_prgr_data(esdp), thr_prgr_active = 1); + sched_wall_time_change(esdp, 1); } + erts_bump_timers(esdp->timer_wheel, current_time); } } else { @@ -3321,19 +3430,36 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) } if (do_timeout) { if (!thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); + erts_thr_progress_active(erts_thr_prgr_data(esdp), thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } } - else { + else if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && prepare_for_sys_schedule()) { + /* We sleep in check_io, only for normal schedulers */ + if (thr_prgr_active) { + erts_thr_progress_active(erts_thr_prgr_data(esdp), thr_prgr_active = 0); + sched_wall_time_change(esdp, 0); + } + flgs = sched_spin_wait(ssi, 0); + if (flgs & ERTS_SSI_FLG_SLEEPING) { + ASSERT(flgs & ERTS_SSI_FLG_WAITING); + flgs = sched_set_sleeptype(ssi, ERTS_SSI_FLG_POLL_SLEEPING); + if (flgs & ERTS_SSI_FLG_SLEEPING) { + ASSERT(flgs & ERTS_SSI_FLG_POLL_SLEEPING); + ASSERT(flgs & ERTS_SSI_FLG_WAITING); + erts_check_io(ssi->psi, timeout_time); + current_time = erts_get_monotonic_time(esdp); + } + } + clear_sys_scheduling(); + } else { if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) { if (thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 0); + erts_thr_progress_active(erts_thr_prgr_data(esdp), thr_prgr_active = 0); sched_wall_time_change(esdp, 0); } - erts_thr_progress_prepare_wait(esdp); + erts_thr_progress_prepare_wait(erts_thr_prgr_data(esdp)); } - flgs = sched_spin_wait(ssi, spincount); if (flgs & ERTS_SSI_FLG_SLEEPING) { ASSERT(flgs & ERTS_SSI_FLG_WAITING); @@ -3363,7 +3489,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) } } if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) - erts_thr_progress_finalize_wait(esdp); + erts_thr_progress_finalize_wait(erts_thr_prgr_data(esdp)); } if (!ERTS_SCHEDULER_IS_DIRTY(esdp) && current_time >= timeout_time) erts_bump_timers(esdp->timer_wheel, current_time); @@ -3389,10 +3515,30 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) (ERTS_SSI_FLG_SUSPENDED | ERTS_SSI_FLG_MSB_EXEC)); - if (ERTS_SCHEDULER_IS_DIRTY(esdp)) + if (ERTS_SCHEDULER_IS_DIRTY(esdp)) { dirty_sched_wall_time_change(esdp, working = 1); + erts_spin_lock(&rq->sleepers.lock); + ERTS_HDBG_CHK_SLEEP_LIST(&rq->sleepers, 0, ssi->next ? ssi : NULL, NULL); + if (ssi->next) { /* Still in list... */ + if (ssi->next == ssi) { + ASSERT(rq->sleepers.list == ssi); + ASSERT(ssi->prev == ssi); + rq->sleepers.list = NULL; + } + else { + ASSERT(ssi->prev != ssi); + if (rq->sleepers.list == ssi) + rq->sleepers.list = ssi->next; + ssi->prev->next = ssi->next; + ssi->next->prev = ssi->prev; + } + ssi->next = ssi->prev = NULL; + } + ERTS_HDBG_CHK_SLEEP_LIST(&rq->sleepers, 0, NULL, ssi); + erts_spin_unlock(&rq->sleepers.lock); + } else if (!thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); + erts_thr_progress_active(erts_thr_prgr_data(esdp), thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } @@ -3478,56 +3624,44 @@ wake_scheduler(ErtsRunQueue *rq) } static void -wake_dirty_schedulers(ErtsRunQueue *rq, int one) +wake_dirty_scheduler(ErtsRunQueue *rq) { - ErtsSchedulerSleepInfo *ssi; + ErtsSchedulerSleepInfo *lo_ssi, *fo_ssi; ErtsSchedulerSleepList *sl; ASSERT(ERTS_RUNQ_IX_IS_DIRTY(rq->ix)); sl = &rq->sleepers; erts_spin_lock(&sl->lock); - ssi = sl->list; - if (!ssi) { + ERTS_HDBG_CHK_SLEEP_LIST(&rq->sleepers, 0, NULL, NULL); + lo_ssi = sl->list; + if (!lo_ssi) { erts_spin_unlock(&sl->lock); - if (one) - wake_scheduler(rq); - } else if (one) { + wake_scheduler(rq); + } + else { erts_aint32_t flgs; - if (ssi->prev) - ssi->prev->next = ssi->next; - else { - ASSERT(sl->list == ssi); - sl->list = ssi->next; + fo_ssi = lo_ssi->next; + ASSERT(fo_ssi->prev == lo_ssi); + if (fo_ssi == lo_ssi) { + ASSERT(lo_ssi->prev == lo_ssi); + sl->list = NULL; + } + else { + ASSERT(lo_ssi->prev != lo_ssi); + lo_ssi->next = fo_ssi->next; + fo_ssi->next->prev = fo_ssi->prev; } - if (ssi->next) - ssi->next->prev = ssi->prev; - + fo_ssi->next = fo_ssi->prev = NULL; + ERTS_HDBG_CHK_SLEEP_LIST(&rq->sleepers, 0, NULL, fo_ssi); erts_spin_unlock(&sl->lock); ERTS_THR_MEMORY_BARRIER; - flgs = ssi_flags_set_wake(ssi); - erts_sched_finish_poke(ssi, flgs); - } else { - sl->list = NULL; - erts_spin_unlock(&sl->lock); - - ERTS_THR_MEMORY_BARRIER; - do { - ErtsSchedulerSleepInfo *wake_ssi = ssi; - ssi = ssi->next; - erts_sched_finish_poke(wake_ssi, ssi_flags_set_wake(wake_ssi)); - } while (ssi); + flgs = ssi_flags_set_wake(fo_ssi); + erts_sched_finish_poke(fo_ssi, flgs); } } -static void -wake_dirty_scheduler(ErtsRunQueue *rq) -{ - wake_dirty_schedulers(rq, 1); -} - - #define ERTS_NO_USED_RUNQS_SHIFT 16 #define ERTS_NO_RUNQS_MASK 0xffffU @@ -4573,6 +4707,15 @@ check_balance(ErtsRunQueue *c_rq) if (blnc_no_rqs == 1) { c_rq->check_balance_reds = INT_MAX; erts_atomic32_set_nob(&balance_info.checking_balance, 0); +#if ERTS_POLL_USE_SCHEDULER_POLLING + c_rq->check_balance_reds = ERTS_RUNQ_CALL_CHECK_BALANCE_REDS; + if ((ERTS_RUNQ_FLGS_GET_NOB(c_rq) & (ERTS_RUNQ_FLG_OUT_OF_WORK|ERTS_RUNQ_FLG_CHECKIO)) + == 0) { + ERTS_RUNQ_FLGS_SET(c_rq, ERTS_RUNQ_FLG_CHECKIO); + erts_start_timer_callback(ERTS_POLL_SCHEDULER_POLLING_TIMEOUT, check_io_timer, NULL); + } + ERTS_RUNQ_FLGS_UNSET(c_rq, ERTS_RUNQ_FLGS_MIGRATION_INFO); +#endif return; } @@ -5092,6 +5235,19 @@ erts_fprintf(stderr, "--------------------------------\n"); /* Publish new migration paths... */ erts_atomic_set_wb(&erts_migration_paths, (erts_aint_t) new_mpaths); +#if ERTS_POLL_USE_SCHEDULER_POLLING + if (full_scheds == current_active) { + ERTS_ASSERT(full_scheds <= current_active); + /* All active schedulers ran for full, we need to do active polling, + so we setup a timer that does active polling */ + if (!(ERTS_RUNQ_FLGS_GET_NOB(c_rq) & ERTS_RUNQ_FLG_CHECKIO)) { + /* Active polling is not running, start it */ + erts_start_timer_callback(ERTS_POLL_SCHEDULER_POLLING_TIMEOUT, check_io_timer, NULL); + } + run_queue_info[c_rq->ix].flags |= ERTS_RUNQ_FLG_CHECKIO; + } +#endif + /* Reset balance statistics in all online queues */ for (qix = 0; qix < blnc_no_rqs; qix++) { Uint32 flags = run_queue_info[qix].flags; @@ -5101,6 +5257,8 @@ erts_fprintf(stderr, "--------------------------------\n"); ASSERT(!(flags & ERTS_RUNQ_FLG_OUT_OF_WORK)); if (rq->waiting) flags |= ERTS_RUNQ_FLG_OUT_OF_WORK; + if (rq != c_rq) + flags &= ~ERTS_RUNQ_FLG_CHECKIO; rq->full_reds_history_sum = run_queue_info[qix].full_reds_history_sum; @@ -5110,8 +5268,7 @@ erts_fprintf(stderr, "--------------------------------\n"); ERTS_DBG_CHK_FULL_REDS_HISTORY(rq); rq->out_of_work_count = 0; - (void) ERTS_RUNQ_FLGS_READ_BSET(rq, ERTS_RUNQ_FLGS_MIGRATION_INFO, flags); - + (void) ERTS_RUNQ_FLGS_READ_BSET(rq, ERTS_RUNQ_FLGS_MIGRATION_INFO|ERTS_RUNQ_FLG_CHECKIO, flags); rq->max_len = erts_atomic32_read_dirty(&rq->len); for (pix = 0; pix < ERTS_NO_PRIO_LEVELS; pix++) { ErtsRunQueueInfo *rqi; @@ -5550,7 +5707,6 @@ erts_sched_set_busy_wait_threshold(ErtsSchedType sched_type, char *str) return EINVAL; } - params->sys_schedule = sys_sched; params->tse = sys_sched * ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT; params->aux_work = sys_sched * aux_work_fact; @@ -5761,6 +5917,9 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online, int no_poll_th size_runqs = sizeof(ErtsAlignedRunQueue) * tot_rqs; erts_aligned_run_queues = erts_alloc_permanent_cache_aligned(ERTS_ALC_T_RUNQS, size_runqs); +#if ERTS_POLL_USE_SCHEDULER_POLLING + erts_atomic32_init_nob(&doing_sys_schedule, 0); +#endif erts_atomic32_init_nob(&no_empty_run_queues, 0); erts_no_run_queues = n; @@ -5875,6 +6034,8 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online, int no_poll_th for (ix = 0; ix < no_dirty_cpu_schedulers; ix++) { ErtsSchedulerSleepInfo *ssi = &aligned_dirty_cpu_sched_sleep_info[ix].ssi; erts_atomic32_init_nob(&ssi->flags, 0); + ssi->next = NULL; + ssi->prev = NULL; ssi->event = NULL; /* initialized in sched_dirty_cpu_thread_func */ erts_atomic32_init_nob(&ssi->aux_work, 0); } @@ -5885,6 +6046,8 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online, int no_poll_th for (ix = 0; ix < no_dirty_io_schedulers; ix++) { ErtsSchedulerSleepInfo *ssi = &aligned_dirty_io_sched_sleep_info[ix].ssi; erts_atomic32_init_nob(&ssi->flags, 0); + ssi->next = NULL; + ssi->prev = NULL; ssi->event = NULL; /* initialized in sched_dirty_io_thread_func */ erts_atomic32_init_nob(&ssi->aux_work, 0); } @@ -7394,7 +7557,13 @@ suspend_scheduler(ErtsSchedulerData *esdp) return; } +#ifdef HARDDEBUG + if (sched_type != ERTS_SCHED_NORMAL) + ERTS_HDBG_CHK_SLEEP_LIST(&esdp->run_queue->sleepers, !0, NULL, ssi); +#endif + if (erts_atomic32_read_nob(&ssi->flags) & ERTS_SSI_FLG_MSB_EXEC) { + ASSERT(no == 1); if (!msb_scheduler_type_switch(sched_type, esdp, no)) return; @@ -7548,7 +7717,8 @@ suspend_scheduler(ErtsSchedulerData *esdp) if (aux_work|evacuate) { if (!thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); + erts_thr_progress_active(erts_thr_prgr_data(esdp), + thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } if (aux_work) @@ -7556,8 +7726,8 @@ suspend_scheduler(ErtsSchedulerData *esdp) aux_work, 1); - if (aux_work && erts_thr_progress_update(esdp)) - erts_thr_progress_leader_update(esdp); + if (aux_work && erts_thr_progress_update(erts_thr_prgr_data(esdp))) + erts_thr_progress_leader_update(erts_thr_prgr_data(esdp)); if (evacuate) { erts_runq_lock(esdp->run_queue); evacuate_run_queue(esdp->run_queue, &sbp); @@ -7576,18 +7746,18 @@ suspend_scheduler(ErtsSchedulerData *esdp) if (!aux_work && current_time < timeout_time) { /* go to sleep... */ if (thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 0); + erts_thr_progress_active(erts_thr_prgr_data(esdp), thr_prgr_active = 0); sched_wall_time_change(esdp, 0); } - erts_thr_progress_prepare_wait(NULL); + erts_thr_progress_prepare_wait(erts_thr_prgr_data(NULL)); suspend_normal_scheduler_sleep(esdp); - erts_thr_progress_finalize_wait(NULL); + erts_thr_progress_finalize_wait(erts_thr_prgr_data(NULL)); current_time = erts_get_monotonic_time(esdp); } if (current_time >= timeout_time) { if (!thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); + erts_thr_progress_active(erts_thr_prgr_data(esdp), thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } erts_bump_timers(esdp->timer_wheel, current_time); @@ -7644,7 +7814,7 @@ suspend_scheduler(ErtsSchedulerData *esdp) profile_scheduler(make_small(esdp->no), am_active); if (!thr_prgr_active) { - erts_thr_progress_active(esdp, thr_prgr_active = 1); + erts_thr_progress_active(erts_thr_prgr_data(esdp), thr_prgr_active = 1); sched_wall_time_change(esdp, 1); } } @@ -8279,6 +8449,11 @@ sched_thread_func(void *vesdp) erts_msacc_init_thread("scheduler", no, 1); erts_thr_progress_register_managed_thread(esdp, &callbacks, 0); + +#if ERTS_POLL_USE_SCHEDULER_POLLING + esdp->ssi->psi = erts_create_pollset_thread(-1, NULL); +#endif + erts_alloc_register_scheduler(vesdp); #ifdef ERTS_ENABLE_LOCK_CHECK { @@ -9293,12 +9468,12 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) } } - leader_update = erts_thr_progress_update(esdp); + leader_update = erts_thr_progress_update(erts_thr_prgr_data(esdp)); aux_work = erts_atomic32_read_acqb(&esdp->ssi->aux_work); if (aux_work | leader_update) { erts_runq_unlock(rq); if (leader_update) - erts_thr_progress_leader_update(esdp); + erts_thr_progress_leader_update(erts_thr_prgr_data(esdp)); if (aux_work) handle_aux_work(&esdp->aux_work_data, aux_work, 0); erts_runq_lock(rq); diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 8d20ccdf90..0aa19e7bde 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -173,8 +173,10 @@ extern int erts_dio_sched_thread_suggested_stack_size; (((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 9)) #define ERTS_RUNQ_FLG_HALTING \ (((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 10)) +#define ERTS_RUNQ_FLG_CHECKIO \ + (((Uint32) 1) << (ERTS_RUNQ_FLG_BASE2 + 11)) -#define ERTS_RUNQ_FLG_MAX (ERTS_RUNQ_FLG_BASE2 + 11) +#define ERTS_RUNQ_FLG_MAX (ERTS_RUNQ_FLG_BASE2 + 12) #define ERTS_RUNQ_FLGS_MIGRATION_QMASKS \ (ERTS_RUNQ_FLGS_EMIGRATE_QMASK \ @@ -357,7 +359,7 @@ typedef struct ErtsSchedulerSleepInfo_ ErtsSchedulerSleepInfo; typedef struct { erts_spinlock_t lock; - ErtsSchedulerSleepInfo *list; + ErtsSchedulerSleepInfo *list; /* circular lifo list; points to last out */ } ErtsSchedulerSleepList; struct ErtsSchedulerSleepInfo_ { diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.h b/erts/emulator/beam/erl_sched_spec_pre_alloc.h index b119c59ab3..74cc966cbe 100644 --- a/erts/emulator/beam/erl_sched_spec_pre_alloc.h +++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.h @@ -188,6 +188,7 @@ erts_sspa_alloc(erts_sspa_data_t *data, int cix) erts_sspa_chunk_t *chnk; erts_sspa_chunk_header_t *chdr; erts_sspa_blk_t *res; + ERTS_MSACC_PUSH_AND_SET_STATE_M_X(ERTS_MSACC_STATE_ALLOC); chnk = erts_sspa_cix2chunk(data, cix); chdr = &chnk->aligned.header; @@ -201,11 +202,15 @@ erts_sspa_alloc(erts_sspa_data_t *data, int cix) chdr->local.last = NULL; ERTS_SSPA_DBG_CHK_LCL(chdr); } - if (chdr->local.cnt <= chdr->local.lim) - return (char *) erts_sspa_process_remote_frees(chdr, res); + if (chdr->local.cnt <= chdr->local.lim) { + res = erts_sspa_process_remote_frees(chdr, res); + ERTS_MSACC_POP_STATE_M_X(); + return (char*) res; + } else if (chdr->head.no_thr_progress_check < ERTS_SSPA_FORCE_THR_CHECK_PROGRESS) chdr->head.no_thr_progress_check++; ASSERT(res); + ERTS_MSACC_POP_STATE_M_X(); return (char *) res; } diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c index aa08eb40ec..bac437efe9 100644 --- a/erts/emulator/beam/erl_thr_progress.c +++ b/erts/emulator/beam/erl_thr_progress.c @@ -508,6 +508,10 @@ init_wakeup_request_array(ErtsThrPrgrVal *w) } } +ErtsThrPrgrData *erts_thr_progress_data(void) { + return erts_tsd_get(erts_thr_prgr_data_key__); +} + void erts_thr_progress_register_unmanaged_thread(ErtsThrPrgrCallbacks *callbacks) { @@ -551,7 +555,7 @@ erts_thr_progress_register_unmanaged_thread(ErtsThrPrgrCallbacks *callbacks) } -void +ErtsThrPrgrData * erts_thr_progress_register_managed_thread(ErtsSchedulerData *esdp, ErtsThrPrgrCallbacks *callbacks, int pref_wakeup) @@ -630,6 +634,7 @@ erts_thr_progress_register_managed_thread(ErtsSchedulerData *esdp, wakeup_managed(id); } callbacks->finalize_wait(callbacks->arg); + return tpd; } static ERTS_INLINE int @@ -796,7 +801,7 @@ leader_update(ErtsThrPrgrData *tpd) == ERTS_THR_PRGR_LFLG_NO_LEADER)) && got_sched_wakeups()) { /* Someone need to make progress */ - wakeup_managed(0); + wakeup_managed(tpd->id); } } } @@ -849,23 +854,22 @@ update(ErtsThrPrgrData *tpd) } int -erts_thr_progress_update(ErtsSchedulerData *esdp) +erts_thr_progress_update(ErtsThrPrgrData *tpd) { - return update(thr_prgr_data(esdp)); + return update(tpd); } int -erts_thr_progress_leader_update(ErtsSchedulerData *esdp) +erts_thr_progress_leader_update(ErtsThrPrgrData *tpd) { - return leader_update(thr_prgr_data(esdp)); + return leader_update(tpd); } void -erts_thr_progress_prepare_wait(ErtsSchedulerData *esdp) +erts_thr_progress_prepare_wait(ErtsThrPrgrData *tpd) { erts_aint32_t lflgs; - ErtsThrPrgrData *tpd = thr_prgr_data(esdp); #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_check_exact(NULL, 0); @@ -884,14 +888,13 @@ erts_thr_progress_prepare_wait(ErtsSchedulerData *esdp) == ERTS_THR_PRGR_LFLG_NO_LEADER && got_sched_wakeups()) { /* Someone need to make progress */ - wakeup_managed(0); + wakeup_managed(tpd->id); } } void -erts_thr_progress_finalize_wait(ErtsSchedulerData *esdp) +erts_thr_progress_finalize_wait(ErtsThrPrgrData *tpd) { - ErtsThrPrgrData *tpd = thr_prgr_data(esdp); ErtsThrPrgrVal current, val; #ifdef ERTS_ENABLE_LOCK_CHECK @@ -921,9 +924,8 @@ erts_thr_progress_finalize_wait(ErtsSchedulerData *esdp) } void -erts_thr_progress_active(ErtsSchedulerData *esdp, int on) +erts_thr_progress_active(ErtsThrPrgrData *tpd, int on) { - ErtsThrPrgrData *tpd = thr_prgr_data(esdp); #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_check_exact(NULL, 0); @@ -973,7 +975,7 @@ unmanaged_continue(ErtsThrPrgrDelayHandle handle) == (ERTS_THR_PRGR_LFLG_NO_LEADER|ERTS_THR_PRGR_LFLG_WAITING_UM) && got_sched_wakeups()) { /* Others waiting for us... */ - wakeup_managed(0); + wakeup_managed(1); } } } @@ -1182,10 +1184,10 @@ request_wakeup_unmanaged(ErtsThrPrgrData *tpd, ErtsThrPrgrVal value) } void -erts_thr_progress_wakeup(ErtsSchedulerData *esdp, +erts_thr_progress_wakeup(ErtsThrPrgrData *tpd, ErtsThrPrgrVal value) { - ErtsThrPrgrData *tpd = thr_prgr_data(esdp); + ASSERT(!tpd->is_temporary); if (tpd->is_managed) request_wakeup_managed(tpd, value); diff --git a/erts/emulator/beam/erl_thr_progress.h b/erts/emulator/beam/erl_thr_progress.h index 8329995b24..00a9e61407 100644 --- a/erts/emulator/beam/erl_thr_progress.h +++ b/erts/emulator/beam/erl_thr_progress.h @@ -123,22 +123,24 @@ extern ErtsThrPrgr erts_thr_prgr__; void erts_thr_progress_pre_init(void); void erts_thr_progress_init(int no_schedulers, int managed, int unmanaged); -void erts_thr_progress_register_managed_thread(ErtsSchedulerData *esdp, - ErtsThrPrgrCallbacks *, - int); +ErtsThrPrgrData *erts_thr_progress_register_managed_thread( + ErtsSchedulerData *esdp, ErtsThrPrgrCallbacks *, int); void erts_thr_progress_register_unmanaged_thread(ErtsThrPrgrCallbacks *); -void erts_thr_progress_active(ErtsSchedulerData *esdp, int on); -void erts_thr_progress_wakeup(ErtsSchedulerData *esdp, +void erts_thr_progress_active(ErtsThrPrgrData *, int on); +void erts_thr_progress_wakeup(ErtsThrPrgrData *, ErtsThrPrgrVal value); -int erts_thr_progress_update(ErtsSchedulerData *esdp); -int erts_thr_progress_leader_update(ErtsSchedulerData *esdp); -void erts_thr_progress_prepare_wait(ErtsSchedulerData *esdp); -void erts_thr_progress_finalize_wait(ErtsSchedulerData *esdp); +int erts_thr_progress_update(ErtsThrPrgrData *); +int erts_thr_progress_leader_update(ErtsThrPrgrData *); +void erts_thr_progress_prepare_wait(ErtsThrPrgrData *); +void erts_thr_progress_finalize_wait(ErtsThrPrgrData *); ErtsThrPrgrDelayHandle erts_thr_progress_unmanaged_delay__(void); void erts_thr_progress_unmanaged_continue__(int umrefc_ix); +ErtsThrPrgrData *erts_thr_progress_data(void); void erts_thr_progress_dbg_print_state(void); +ERTS_GLB_INLINE ErtsThrPrgrData *erts_thr_prgr_data(ErtsSchedulerData *esdp); + ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_nob__(ERTS_THR_PRGR_ATOMIC *atmc); ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_acqb__(ERTS_THR_PRGR_ATOMIC *atmc); ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_mb__(ERTS_THR_PRGR_ATOMIC *atmc); @@ -161,6 +163,15 @@ ERTS_GLB_INLINE int erts_thr_progress_has_reached(ErtsThrPrgrVal val); #if ERTS_GLB_INLINE_INCL_FUNC_DEF +ERTS_GLB_INLINE ErtsThrPrgrData * +erts_thr_prgr_data(ErtsSchedulerData *esdp) { + if (esdp) { + return &esdp->thr_progress_data; + } else { + return erts_thr_progress_data(); + } +} + ERTS_GLB_INLINE ErtsThrPrgrVal erts_thr_prgr_read_nob__(ERTS_THR_PRGR_ATOMIC *atmc) { diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 53a020e7a5..701fb38147 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -72,6 +72,7 @@ static ErtsTracer default_port_tracer; static Eterm system_monitor; static Eterm system_profile; +static erts_aint_t system_logger; #ifdef HAVE_ERTS_NOW_CPU int erts_cpu_timestamp; @@ -340,6 +341,7 @@ void erts_init_trace(void) { default_port_trace_flags = F_INITIAL_TRACE_FLAGS; default_port_tracer = erts_tracer_nil; system_seq_tracer = erts_tracer_nil; + erts_atomic_init_nob(&system_logger, am_logger); init_sys_msg_dispatcher(); init_tracer_nif(); } @@ -2027,10 +2029,24 @@ enqueue_sys_msg(enum ErtsSysMsgType type, erts_mtx_unlock(&smq_mtx); } +Eterm +erts_get_system_logger(void) +{ + return (Eterm)erts_atomic_read_nob(&system_logger); +} + +Eterm +erts_set_system_logger(Eterm logger) +{ + if (logger != am_logger && logger != am_undefined && !is_internal_pid(logger)) + return THE_NON_VALUE; + return (Eterm)erts_atomic_xchg_nob(&system_logger, logger); +} + void erts_queue_error_logger_message(Eterm from, Eterm msg, ErlHeapFragment *bp) { - enqueue_sys_msg(SYS_MSG_TYPE_ERRLGR, from, am_logger, msg, bp); + enqueue_sys_msg(SYS_MSG_TYPE_ERRLGR, from, erts_get_system_logger(), msg, bp); } void @@ -2177,6 +2193,7 @@ sys_msg_dispatcher_func(void *unused) { ErtsThrPrgrCallbacks callbacks; ErtsSysMsgQ *local_sys_message_queue = NULL; + ErtsThrPrgrData *tpd; int wait = 0; #ifdef ERTS_ENABLE_LOCK_CHECK @@ -2189,7 +2206,7 @@ sys_msg_dispatcher_func(void *unused) callbacks.wait = sys_msg_dispatcher_wait; callbacks.finalize_wait = sys_msg_dispatcher_fin_wait; - erts_thr_progress_register_managed_thread(NULL, &callbacks, 0); + tpd = erts_thr_progress_register_managed_thread(NULL, &callbacks, 0); while (1) { int end_wait = 0; @@ -2210,8 +2227,8 @@ sys_msg_dispatcher_func(void *unused) if (!sys_message_queue) { erts_mtx_unlock(&smq_mtx); end_wait = 1; - erts_thr_progress_active(NULL, 0); - erts_thr_progress_prepare_wait(NULL); + erts_thr_progress_active(tpd, 0); + erts_thr_progress_prepare_wait(tpd); erts_mtx_lock(&smq_mtx); } @@ -2225,8 +2242,8 @@ sys_msg_dispatcher_func(void *unused) erts_mtx_unlock(&smq_mtx); if (end_wait) { - erts_thr_progress_finalize_wait(NULL); - erts_thr_progress_active(NULL, 1); + erts_thr_progress_finalize_wait(tpd); + erts_thr_progress_active(tpd, 1); } /* Send trace messages ... */ @@ -2239,8 +2256,8 @@ sys_msg_dispatcher_func(void *unused) Process *proc = NULL; Port *port = NULL; - if (erts_thr_progress_update(NULL)) - erts_thr_progress_leader_update(NULL); + if (erts_thr_progress_update(tpd)) + erts_thr_progress_leader_update(tpd); #ifdef DEBUG_PRINTOUTS print_msg_type(smqp); @@ -2270,7 +2287,7 @@ sys_msg_dispatcher_func(void *unused) } break; case SYS_MSG_TYPE_ERRLGR: - receiver = am_logger; + receiver = smqp->to; break; default: receiver = NIL; @@ -2284,8 +2301,15 @@ sys_msg_dispatcher_func(void *unused) if (is_internal_pid(receiver)) { proc = erts_pid2proc(NULL, 0, receiver, proc_locks); if (!proc) { - /* Bad tracer */ - goto failure; + if (smqp->type == SYS_MSG_TYPE_ERRLGR) { + /* Bad logger process, send to kernel 'logger' process */ + erts_set_system_logger(am_logger); + receiver = erts_get_system_logger(); + goto logger; + } else { + /* Bad tracer */ + goto failure; + } } else { ErtsMessage *mp; @@ -2298,9 +2322,9 @@ sys_msg_dispatcher_func(void *unused) #endif erts_proc_unlock(proc, proc_locks); } - } - else if (receiver == am_logger) { - proc = erts_whereis_process(NULL,0,receiver,proc_locks,0); + } else if (receiver == am_logger) { + logger: + proc = erts_whereis_process(NULL,0,am_logger,proc_locks,0); if (!proc) goto failure; else if (smqp->from == proc->common.id) @@ -2308,7 +2332,10 @@ sys_msg_dispatcher_func(void *unused) else goto queue_proc_msg; } - else if (is_internal_port(receiver)) { + else if (receiver == am_undefined) { + goto drop_sys_msg; + } + else if (is_internal_port(receiver)) { port = erts_thr_id2port_sflgs(receiver, ERTS_PORT_SFLGS_INVALID_TRACER_LOOKUP); if (!port) @@ -2365,7 +2392,7 @@ erts_foreach_sys_msg_in_q(void (*func)(Eterm, to = erts_get_system_profile(); break; case SYS_MSG_TYPE_ERRLGR: - to = am_logger; + to = erts_get_system_logger(); break; default: to = NIL; diff --git a/erts/emulator/beam/erl_trace.h b/erts/emulator/beam/erl_trace.h index bccf31606e..b7844d1cb0 100644 --- a/erts/emulator/beam/erl_trace.h +++ b/erts/emulator/beam/erl_trace.h @@ -94,6 +94,8 @@ void erts_foreach_sys_msg_in_q(void (*func)(Eterm, Eterm, Eterm, ErlHeapFragment *)); +Eterm erts_set_system_logger(Eterm); +Eterm erts_get_system_logger(void); void erts_queue_error_logger_message(Eterm, Eterm, ErlHeapFragment *); void erts_send_sys_msg_proc(Eterm, Eterm, Eterm, ErlHeapFragment *); diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index 4089fac48e..35eae18394 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -41,8 +41,8 @@ #define MAX_REG 1024 /* Max number of x(N) registers used */ /* - * The new arithmetic operations need some extra X registers in the register array. - * so does the gc_bif's (i_gc_bif3 need 3 extra). + * The new trapping length/1 implementation need 3 extra registers in the + * register array. */ #define ERTS_X_REGS_ALLOCATED (MAX_REG+3) @@ -146,6 +146,21 @@ (HEAP_TOP(p) = HEAP_TOP(p) + (sz), HEAP_TOP(p) - (sz)))) #endif +/* + * Always allocate in a heap fragment, never on the heap. + */ +#if defined(VALGRIND) +/* Running under valgrind, allocate exactly as much as needed.*/ +# define HeapFragOnlyAlloc(p, sz) \ + (ASSERT((sz) >= 0), \ + ErtsHAllocLockCheck(p), \ + erts_heap_alloc((p),(sz),0)) +#else +# define HeapFragOnlyAlloc(p, sz) \ + (ASSERT((sz) >= 0), \ + ErtsHAllocLockCheck(p), \ + erts_heap_alloc((p),(sz),512)) +#endif /* * Description for each instruction (defined here because the name and diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index 322981ca1d..29de162b42 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -131,6 +131,7 @@ extern Eterm erts_nif_call_function(Process *p, Process *tracee, int erts_call_dirty_nif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *reg); +ErtsMessage* erts_create_message_from_nif_env(ErlNifEnv* msg_env); /* Driver handle (wrapper for old plain handle) */ @@ -891,6 +892,11 @@ void erts_init_bif(void); Eterm erl_send(Process *p, Eterm to, Eterm msg); int erts_set_group_leader(Process *proc, Eterm new_gl); +/* erl_bif_guard.c */ + +void erts_init_bif_guard(void); +Eterm erts_trapping_length_1(Process* p, Eterm* args); + /* erl_bif_op.c */ Eterm erl_is_function(Process* p, Eterm arg1, Eterm arg2); @@ -961,7 +967,7 @@ void init_break_handler(void); void erts_set_ignore_break(void); void erts_replace_intr(void); void process_info(fmtfn_t, void *); -void print_process_info(fmtfn_t, void *, Process*); +void print_process_info(fmtfn_t, void *, Process*, ErtsProcLocks); void info(fmtfn_t, void *); void loaded(fmtfn_t, void *); void erts_print_base64(fmtfn_t to, void *to_arg, byte* src, Uint size); @@ -1302,14 +1308,7 @@ Sint intlist_to_buf(Eterm, char*, Sint); /* most callers pass plain char*'s */ int erts_unicode_list_to_buf(Eterm list, byte *buf, Sint len, Sint* written); Sint erts_unicode_list_to_buf_len(Eterm list); -struct Sint_buf { -#if defined(ARCH_64) - char s[22]; -#else - char s[12]; -#endif -}; -char* Sint_to_buf(Sint, struct Sint_buf*); +int Sint_to_buf(Sint num, int base, char **buf_p, size_t buf_size); #define ERTS_IOLIST_STATE_INITER(C_P, OBJ) \ {(C_P), 0, 0, (OBJ), {NULL, NULL, NULL, ERTS_ALC_T_INVALID}, 0, 0} diff --git a/erts/emulator/beam/instrs.tab b/erts/emulator/beam/instrs.tab index da1dd3dc45..df60e889f3 100644 --- a/erts/emulator/beam/instrs.tab +++ b/erts/emulator/beam/instrs.tab @@ -238,6 +238,7 @@ HANDLE_APPLY_FUN_ERROR() { } DISPATCH_FUN(I) { + //| -no_next SET_I($I); Dispatchfun(); } @@ -299,6 +300,7 @@ i_call_fun_last(Fun, Deallocate) { } return() { + //| -no_next SET_I(c_p->cp); DTRACE_RETURN_FROM_PC(c_p); @@ -951,6 +953,7 @@ build_stacktrace() { } raw_raise() { + //| -no_prefetch Eterm class = x(0); Eterm value = x(1); Eterm stacktrace = x(2); diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 349034e8ac..ee99c9e563 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1002,10 +1002,11 @@ bif1 Fail Bif=u$bif:erlang:get/1 Src=s Dst=d => gen_get(Src, Dst) bif2 Jump=j u$bif:erlang:element/2 S1=s S2=xy Dst=d => gen_element(Jump, S1, S2, Dst) -bif1 p Bif S1 Dst => bif1_body Bif S1 Dst +bif1 p Bif S1 Dst => i_bif1_body S1 Bif Dst +bif1 Fail=f Bif S1 Dst => i_bif1 S1 Fail Bif Dst -bif2 p Bif S1 S2 Dst => i_bif2_body Bif S1 S2 Dst -bif2 Fail Bif S1 S2 Dst => i_bif2 Fail Bif S1 S2 Dst +bif2 p Bif S1 S2 Dst => i_bif2_body S2 S1 Bif Dst +bif2 Fail=f Bif S1 S2 Dst => i_bif2 S2 S1 Fail Bif Dst i_get_hash c I d i_get s d @@ -1023,10 +1024,12 @@ i_fast_element xy j? I d i_element xy j? s d -bif1 f? b s d -bif1_body b s d -i_bif2 f? b s s d -i_bif2_body b s s d +i_bif1 s f? b d +i_bif1_body s b d +i_bif2 s s f? b d +i_bif2_body s s b d +i_bif3 s s s f? b d +i_bif3_body s s s b d # # Internal calls. @@ -1499,80 +1502,80 @@ gc_bif2 Fail Live u$bif:erlang:sminus/2 S1 S2 Dst => \ # # Optimize addition and subtraction of small literals using -# the i_increment/4 instruction (in bodies, not in guards). +# the i_increment/3 instruction (in bodies, not in guards). # gen_plus p Live Int=i Reg=d Dst => \ - gen_increment(Reg, Int, Live, Dst) + gen_increment(Reg, Int, Dst) gen_plus p Live Reg=d Int=i Dst => \ - gen_increment(Reg, Int, Live, Dst) + gen_increment(Reg, Int, Dst) gen_minus p Live Reg=d Int=i Dst | negation_is_small(Int) => \ - gen_increment_from_minus(Reg, Int, Live, Dst) + gen_increment_from_minus(Reg, Int, Dst) # -# GCing arithmetic instructions. +# Arithmetic instructions. # -gen_plus Fail Live S1 S2 Dst => i_plus S1 S2 Fail Live Dst +gen_plus Fail Live S1 S2 Dst => i_plus S1 S2 Fail Dst -gen_minus Fail Live S1 S2 Dst => i_minus S1 S2 Fail Live Dst +gen_minus Fail Live S1 S2 Dst => i_minus S1 S2 Fail Dst gc_bif2 Fail Live u$bif:erlang:stimes/2 S1 S2 Dst => \ - i_times Fail Live S1 S2 Dst + i_times Fail S1 S2 Dst gc_bif2 Fail Live u$bif:erlang:div/2 S1 S2 Dst => \ - i_m_div Fail Live S1 S2 Dst + i_m_div Fail S1 S2 Dst gc_bif2 Fail Live u$bif:erlang:intdiv/2 S1 S2 Dst => \ - i_int_div Fail Live S1 S2 Dst + i_int_div Fail S1 S2 Dst gc_bif2 Fail Live u$bif:erlang:rem/2 S1 S2 Dst => \ - i_rem S1 S2 Fail Live Dst + i_rem S1 S2 Fail Dst gc_bif2 Fail Live u$bif:erlang:bsl/2 S1 S2 Dst => \ - i_bsl S1 S2 Fail Live Dst + i_bsl S1 S2 Fail Dst gc_bif2 Fail Live u$bif:erlang:bsr/2 S1 S2 Dst => \ - i_bsr S1 S2 Fail Live Dst + i_bsr S1 S2 Fail Dst gc_bif2 Fail Live u$bif:erlang:band/2 S1 S2 Dst => \ - i_band S1 S2 Fail Live Dst + i_band S1 S2 Fail Dst gc_bif2 Fail Live u$bif:erlang:bor/2 S1 S2 Dst => \ - i_bor Fail Live S1 S2 Dst + i_bor Fail S1 S2 Dst gc_bif2 Fail Live u$bif:erlang:bxor/2 S1 S2 Dst => \ - i_bxor Fail Live S1 S2 Dst + i_bxor Fail S1 S2 Dst -gc_bif1 Fail I u$bif:erlang:bnot/1 Src Dst=d => i_int_bnot Fail Src I Dst +gc_bif1 Fail Live u$bif:erlang:bnot/1 Src Dst=d => i_int_bnot Fail Src Dst -i_increment rxy W t d +i_increment rxy W d -i_plus x xy j? t d -i_plus s s j? t d +i_plus x xy j? d +i_plus s s j? d -i_minus x x j? t d -i_minus s s j? t d +i_minus x x j? d +i_minus s s j? d -i_times j? t s s d +i_times j? s s d -i_m_div j? t s s d -i_int_div j? t s s d +i_m_div j? s s d +i_int_div j? s s d -i_rem x x j? t d -i_rem s s j? t d +i_rem x x j? d +i_rem s s j? d -i_bsl s s j? t d -i_bsr s s j? t d +i_bsl s s j? d +i_bsr s s j? d -i_band x c j? t d -i_band s s j? t d +i_band x c j? d +i_band s s j? d -i_bor j? I s s d -i_bxor j? I s s d +i_bor j? s s d +i_bxor j? s s d -i_int_bnot Fail Src=c Live Dst => move Src x | i_int_bnot Fail x Live Dst +i_int_bnot Fail Src=c Dst => move Src x | i_int_bnot Fail x Dst -i_int_bnot j? S t d +i_int_bnot j? S d # # Old guard BIFs that creates heap fragments are no longer allowed. @@ -1585,29 +1588,27 @@ bif1 Fail u$bif:erlang:round/1 s d => too_old_compiler bif1 Fail u$bif:erlang:trunc/1 s d => too_old_compiler # -# Guard BIFs. +# Handle the length/1 guard BIF specially to make it trappable. # -gc_bif1 Fail I Bif Src Dst => \ - gen_guard_bif1(Fail, I, Bif, Src, Dst) - -gc_bif2 Fail I Bif S1 S2 Dst => \ - gen_guard_bif2(Fail, I, Bif, S1, S2, Dst) -gc_bif3 Fail I Bif S1 S2 S3 Dst => \ - gen_guard_bif3(Fail, I, Bif, S1, S2, S3, Dst) +gc_bif1 Fail=j Live u$bif:erlang:length/1 Src Dst => \ + i_length_setup Live Src | i_length Fail Live Dst -i_gc_bif1 j? W s t? d +i_length_setup t xyc -i_gc_bif2 j? W t? s s d +i_length j? t d -ii_gc_bif3/7 +# +# Guard BIFs. +# +gc_bif1 p Live Bif Src Dst => i_bif1_body Src Bif Dst +gc_bif1 Fail=f Live Bif Src Dst => i_bif1 Src Fail Bif Dst -# A specific instruction can only have 6 operands, so we must -# pass one of the arguments in an x register. -ii_gc_bif3 Fail Bif Live S1 S2 S3 Dst => \ - move S1 x | i_gc_bif3 Fail Bif Live S2 S3 Dst +gc_bif2 p Live Bif S1 S2 Dst => i_bif2_body S2 S1 Bif Dst +gc_bif2 Fail=f Live Bif S1 S2 Dst => i_bif2 S2 S1 Fail Bif Dst -i_gc_bif3 j? W t? s s d +gc_bif3 p Live Bif S1 S2 S3 Dst => i_bif3_body S3 S2 S1 Bif Dst +gc_bif3 Fail=f Live Bif S1 S2 S3 Dst => i_bif3 S3 S2 S1 Fail Bif Dst # # The following instruction is specially handled in beam_load.c diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c index f70f5e73d4..1e7b494a91 100644 --- a/erts/emulator/beam/utils.c +++ b/erts/emulator/beam/utils.c @@ -3681,30 +3681,47 @@ erts_unicode_list_to_buf_len(Eterm list) } } -/* -** Convert an integer to a byte list -** return pointer to converted stuff (need not to be at start of buf!) -*/ -char* Sint_to_buf(Sint n, struct Sint_buf *buf) +/* Prints an integer in the given base, returning the number of digits printed. + * + * (*buf) is a pointer to the buffer, and is set to the start of the string + * when returning. */ +int Sint_to_buf(Sint n, int base, char **buf, size_t buf_size) { - char* p = &buf->s[sizeof(buf->s)-1]; - int sign = 0; - - *p-- = '\0'; /* null terminate */ - if (n == 0) - *p-- = '0'; - else if (n < 0) { - sign = 1; - n = -n; + char *p = &(*buf)[buf_size - 1]; + int sign = 0, size = 0; + + ASSERT(base >= 2 && base <= 36); + + if (n == 0) { + *p-- = '0'; + size++; + } else if (n < 0) { + sign = 1; + n = -n; } while (n != 0) { - *p-- = (n % 10) + '0'; - n /= 10; + int digit = n % base; + + if (digit < 10) { + *p-- = '0' + digit; + } else { + *p-- = 'A' + (digit - 10); + } + + size++; + + n /= base; } - if (sign) - *p-- = '-'; - return p+1; + + if (sign) { + *p-- = '-'; + size++; + } + + *buf = p + 1; + + return size; } /* Build a list of integers in some safe memory area @@ -4771,58 +4788,3 @@ erts_ptr_id(void *ptr) return ptr; } -#ifdef DEBUG -/* - * Handy functions when using a debugger - don't use in the code! - */ - -void upp(byte *buf, size_t sz) -{ - bin_write(ERTS_PRINT_STDERR, NULL, buf, sz); -} - -void pat(Eterm atom) -{ - upp(atom_tab(atom_val(atom))->name, - atom_tab(atom_val(atom))->len); -} - - -void pinfo() -{ - process_info(ERTS_PRINT_STDOUT, NULL); -} - - -void pp(p) -Process *p; -{ - if(p) - print_process_info(ERTS_PRINT_STDERR, NULL, p); -} - -void ppi(Eterm pid) -{ - pp(erts_proc_lookup(pid)); -} - -void td(Eterm x) -{ - erts_fprintf(stderr, "%T\n", x); -} - -void -ps(Process* p, Eterm* stop) -{ - Eterm* sp = STACK_START(p) - 1; - - if (stop <= STACK_END(p)) { - stop = STACK_END(p) + 1; - } - - while(sp >= stop) { - erts_printf("%p: %.75T\n", sp, *sp); - sp--; - } -} -#endif diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index dbe0201caf..207bef4044 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -38,6 +38,7 @@ #include <ctype.h> #include <sys/types.h> #include <errno.h> +#include <stdint.h> #define IDENTITY(c) c #define STRINGIFY_1(b) IDENTITY(#b) @@ -633,9 +634,13 @@ static size_t my_strnlen(const char *s, size_t maxlen) * header length. To get the header length we use * the pointer difference from the cmsg start pointer * to the CMSG_DATA(cmsg) pointer. + * + * Some platforms (seen on ppc Linux 2.6.29-3.ydl61.3) + * may return 0 as the cmsg_len if the cmsg is to be ignored. */ #define LEN_CMSG_DATA(cmsg) \ - ((cmsg)->cmsg_len - ((char*)CMSG_DATA(cmsg) - (char*)(cmsg))) + ((cmsg)->cmsg_len < sizeof (struct cmsghdr) ? 0 : \ + (cmsg)->cmsg_len - ((char*)CMSG_DATA(cmsg) - (char*)(cmsg))) #define NXT_CMSG_HDR(cmsg) \ ((struct cmsghdr*)(((char*)(cmsg)) + CMSG_SPACE(LEN_CMSG_DATA(cmsg)))) #endif @@ -955,6 +960,7 @@ static size_t my_strnlen(const char *s, size_t maxlen) #endif #endif +typedef struct _tcp_descriptor tcp_descriptor; #if defined(TCP_CORK) #define INET_TCP_NOPUSH TCP_CORK @@ -1010,16 +1016,19 @@ typedef struct _multi_timer_data { struct _multi_timer_data *prev; } MultiTimerData; -static MultiTimerData *add_multi_timer(MultiTimerData **first, ErlDrvPort port, - ErlDrvTermData caller, unsigned timeout, - void (*timeout_fun)(ErlDrvData drv_data, - ErlDrvTermData caller)); -static void fire_multi_timers(MultiTimerData **first, ErlDrvPort port, +static MultiTimerData *add_multi_timer(tcp_descriptor *desc, ErlDrvPort port, + ErlDrvTermData caller, unsigned timeout, + void (*timeout_fun)(ErlDrvData drv_data, + ErlDrvTermData caller)); +static void fire_multi_timers(tcp_descriptor *desc, ErlDrvPort port, ErlDrvData data); -static void remove_multi_timer(MultiTimerData **first, ErlDrvPort port, MultiTimerData *p); +static void remove_multi_timer(tcp_descriptor *desc, ErlDrvPort port, MultiTimerData *p); +static void cancel_multi_timer(tcp_descriptor *desc, ErlDrvPort port, + void (*timeout_fun)(ErlDrvData drv_data, + ErlDrvTermData caller)); static void tcp_inet_multi_timeout(ErlDrvData e, ErlDrvTermData caller); -static void clean_multi_timers(MultiTimerData **first, ErlDrvPort port); +static void clean_multi_timers(tcp_descriptor *desc, ErlDrvPort port); typedef struct { int id; /* id used to identify reply */ @@ -1278,7 +1287,7 @@ static struct erl_drv_entry sctp_inet_driver_entry = }; #endif -typedef struct { +struct _tcp_descriptor { inet_descriptor inet; /* common data structure (DON'T MOVE) */ int high; /* high watermark */ int low; /* low watermark */ @@ -1294,7 +1303,8 @@ typedef struct { int http_state; /* 0 = response|request 1=headers fields */ inet_async_multi_op *multi_first;/* NULL == no multi-accept-queue, op is in ordinary queue */ inet_async_multi_op *multi_last; - MultiTimerData *mtd; /* Timer structures for multiple accept */ + MultiTimerData *mtd; /* Timer structures for multiple accept */ + MultiTimerData *mtd_cache; /* A cache for timer allocations */ #ifdef HAVE_SENDFILE struct { ErlDrvSizeT ioq_skip; /* The number of bytes in the queue at the time @@ -1310,7 +1320,7 @@ typedef struct { Uint64 length; } sendfile; #endif -} tcp_descriptor; +}; /* send function */ static int tcp_send(tcp_descriptor* desc, char* ptr, ErlDrvSizeT len); @@ -1320,7 +1330,10 @@ static int tcp_deliver(tcp_descriptor* desc, int len); static int tcp_shutdown_error(tcp_descriptor* desc, int err); +#ifdef HAVE_SENDFILE static int tcp_inet_sendfile(tcp_descriptor* desc); +static int tcp_sendfile_aborted(tcp_descriptor* desc, int socket_error); +#endif static int tcp_inet_output(tcp_descriptor* desc, HANDLE event); static int tcp_inet_input(tcp_descriptor* desc, HANDLE event); @@ -9772,6 +9785,7 @@ static ErlDrvData prep_tcp_inet_start(ErlDrvPort port, char* args) desc->tcp_add_flags = 0; desc->http_state = 0; desc->mtd = NULL; + desc->mtd_cache = NULL; desc->multi_first = desc->multi_last = NULL; DEBUGF(("tcp_inet_start(%ld) }\r\n", (long)port)); return (ErlDrvData) desc; @@ -9875,15 +9889,14 @@ static void tcp_close_check(tcp_descriptor* desc) driver_demonitor_process(desc->inet.port, &monitor); send_async_error(desc->inet.dport, id, caller, am_closed); } - clean_multi_timers(&(desc->mtd), desc->inet.port); } - else if (desc->inet.state == INET_STATE_CONNECTING) { async_error_am(INETP(desc), am_closed); } else if (desc->inet.state == INET_STATE_CONNECTED) { async_error_am_all(INETP(desc), am_closed); } + clean_multi_timers(desc, desc->inet.port); } /* @@ -9926,6 +9939,15 @@ static void tcp_desc_close(tcp_descriptor* desc) erl_inet_close(INETP(desc)); } +static void tcp_inet_recv_timeout(ErlDrvData e, ErlDrvTermData dummy) +{ + tcp_descriptor* desc = (tcp_descriptor*)e; + ASSERT(!desc->inet.active); + sock_select(INETP(desc),(FD_READ|FD_CLOSE),0); + desc->i_remain = 0; + async_error_am(INETP(desc), am_timeout); +} + /* TCP requests from Erlang */ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, ErlDrvSizeT len, @@ -10096,12 +10118,12 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, if (time_left <= 0) { time_left = 1; } - omtd = add_multi_timer(&(desc->mtd), desc->inet.port, ocaller, + omtd = add_multi_timer(desc, desc->inet.port, ocaller, time_left, &tcp_inet_multi_timeout); } enq_old_multi_op(desc, oid, oreq, ocaller, omtd, &omonitor); if (timeout != INET_INFINITY) { - mtd = add_multi_timer(&(desc->mtd), desc->inet.port, caller, + mtd = add_multi_timer(desc, desc->inet.port, caller, timeout, &tcp_inet_multi_timeout); } enq_multi_op(desc, tbuf, INET_REQ_ACCEPT, caller, mtd, &monitor); @@ -10116,7 +10138,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_xerror("noproc", rbuf, rsize); } if (timeout != INET_INFINITY) { - mtd = add_multi_timer(&(desc->mtd), desc->inet.port, caller, + mtd = add_multi_timer(desc, desc->inet.port, caller, timeout, &tcp_inet_multi_timeout); } enq_multi_op(desc, tbuf, INET_REQ_ACCEPT, caller, mtd, &monitor); @@ -10213,7 +10235,8 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, async_error_am(INETP(desc), am_timeout); else { if (timeout != INET_INFINITY) - driver_set_timer(desc->inet.port, timeout); + add_multi_timer(desc, INETP(desc)->port, 0, + timeout, &tcp_inet_recv_timeout); if (!INETP(desc)->is_ignored) sock_select(INETP(desc),(FD_READ|FD_CLOSE),1); else @@ -10300,12 +10323,11 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, desc->tcp_add_flags |= TCP_ADDF_SENDFILE; /* See if we can finish sending without selecting & rescheduling. */ - tcp_inet_sendfile(desc); - - if(desc->sendfile.length > 0) { - sock_select(INETP(desc), FD_WRITE, 1); + if (tcp_inet_sendfile(desc) == 0) { + if(desc->sendfile.length > 0) { + sock_select(INETP(desc), FD_WRITE, 1); + } } - return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); #else return ctl_error(ENOTSUP, rbuf, rsize); @@ -10319,12 +10341,27 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, } +static void tcp_inet_send_timeout(ErlDrvData e, ErlDrvTermData dummy) +{ + tcp_descriptor* desc = (tcp_descriptor*)e; + ASSERT(IS_BUSY(INETP(desc))); + ASSERT(desc->busy_on_send); + desc->inet.caller = desc->inet.busy_caller; + desc->inet.state &= ~INET_F_BUSY; + desc->busy_on_send = 0; + set_busy_port(desc->inet.port, 0); + inet_reply_error_am(INETP(desc), am_timeout); + if (desc->send_timeout_close) { + tcp_desc_close(desc); + } +} + /* ** tcp_inet_timeout: ** called when timer expire: ** TCP socket may be: ** -** a) receiving -- deselect +** a) receiving -- send timeout ** b) connecting -- close socket ** c) accepting -- reset listener ** @@ -10338,26 +10375,9 @@ static void tcp_inet_timeout(ErlDrvData e) DEBUGF(("tcp_inet_timeout(%ld) {s=%d\r\n", (long)desc->inet.port, desc->inet.s)); if ((state & INET_F_MULTI_CLIENT)) { /* Multi-client always means multi-timers */ - fire_multi_timers(&(desc->mtd), desc->inet.port, e); + fire_multi_timers(desc, desc->inet.port, e); } else if ((state & INET_STATE_CONNECTED) == INET_STATE_CONNECTED) { - if (desc->busy_on_send) { - ASSERT(IS_BUSY(INETP(desc))); - desc->inet.caller = desc->inet.busy_caller; - desc->inet.state &= ~INET_F_BUSY; - desc->busy_on_send = 0; - set_busy_port(desc->inet.port, 0); - inet_reply_error_am(INETP(desc), am_timeout); - if (desc->send_timeout_close) { - tcp_desc_close(desc); - } - } - else { - /* assume recv timeout */ - ASSERT(!desc->inet.active); - sock_select(INETP(desc),(FD_READ|FD_CLOSE),0); - desc->i_remain = 0; - async_error_am(INETP(desc), am_timeout); - } + fire_multi_timers(desc, desc->inet.port, e); } else if ((state & INET_STATE_CONNECTING) == INET_STATE_CONNECTING) { /* assume connect timeout */ @@ -10487,7 +10507,7 @@ static void tcp_inet_process_exit(ErlDrvData e, ErlDrvMonitor *monitorp) return; } if (timeout != NULL) { - remove_multi_timer(&(desc->mtd), desc->inet.port, timeout); + remove_multi_timer(desc, desc->inet.port, timeout); } if (desc->multi_first == NULL) { sock_select(INETP(desc),FD_ACCEPT,0); @@ -10518,6 +10538,7 @@ static int tcp_recv_closed(tcp_descriptor* desc) #ifdef DEBUG long port = (long) desc->inet.port; /* Used after driver_exit() */ #endif + int blocking_send = 0; DEBUGF(("tcp_recv_closed(%ld): s=%d, in %s, line %d\r\n", port, desc->inet.s, __FILE__, __LINE__)); if (IS_BUSY(INETP(desc))) { @@ -10525,7 +10546,7 @@ static int tcp_recv_closed(tcp_descriptor* desc) desc->inet.caller = desc->inet.busy_caller; tcp_clear_output(desc); if (desc->busy_on_send) { - driver_cancel_timer(desc->inet.port); + cancel_multi_timer(desc, INETP(desc)->port, &tcp_inet_send_timeout); desc->busy_on_send = 0; DEBUGF(("tcp_recv_closed(%ld): busy on send\r\n", port)); } @@ -10533,16 +10554,25 @@ static int tcp_recv_closed(tcp_descriptor* desc) set_busy_port(desc->inet.port, 0); inet_reply_error_am(INETP(desc), am_closed); DEBUGF(("tcp_recv_closed(%ld): busy reply 'closed'\r\n", port)); - } else { + blocking_send = 1; + } +#ifdef HAVE_SENDFILE + if (desc->tcp_add_flags & TCP_ADDF_SENDFILE) { + tcp_sendfile_aborted(desc, ENOTCONN); + blocking_send = 1; + } +#endif + if (!blocking_send) { /* No blocking send op to reply to right now. * If next op is a send, make sure it returns {error,closed} * rather than {error,enotconn}. */ desc->tcp_add_flags |= TCP_ADDF_DELAYED_CLOSE_SEND; } + if (!desc->inet.active) { - /* We must cancel any timer here ! */ - driver_cancel_timer(desc->inet.port); + /* We must cancel any timer here ! */ + clean_multi_timers(desc, INETP(desc)->port); /* passive mode do not terminate port ! */ tcp_clear_input(desc); if (desc->inet.exitf) { @@ -10577,16 +10607,21 @@ static int tcp_recv_error(tcp_descriptor* desc, int err) desc->inet.caller = desc->inet.busy_caller; tcp_clear_output(desc); if (desc->busy_on_send) { - driver_cancel_timer(desc->inet.port); + cancel_multi_timer(desc, INETP(desc)->port, &tcp_inet_send_timeout); desc->busy_on_send = 0; } desc->inet.state &= ~INET_F_BUSY; set_busy_port(desc->inet.port, 0); inet_reply_error_am(INETP(desc), am_closed); } +#ifdef HAVE_SENDFILE + if (desc->tcp_add_flags & TCP_ADDF_SENDFILE) { + tcp_sendfile_aborted(desc, err); + } +#endif if (!desc->inet.active) { /* We must cancel any timer here ! */ - driver_cancel_timer(desc->inet.port); + clean_multi_timers(desc, INETP(desc)->port); tcp_clear_input(desc); if (desc->inet.exitf) { tcp_desc_close(desc); @@ -10691,13 +10726,13 @@ static int tcp_deliver(tcp_descriptor* desc, int len) if (len == 0) { /* empty buffer or waiting for more input */ if ((desc->i_buf == NULL) || (desc->i_remain > 0)) - return count; + return 0; if ((n = tcp_remain(desc, &len)) != 0) { if (n < 0) /* packet error */ return n; if (len > 0) /* more data pending */ desc->i_remain = len; - return count; + return 0; } } @@ -10749,9 +10784,7 @@ static int tcp_deliver(tcp_descriptor* desc, int len) len = 0; if (!desc->inet.active) { - if (!desc->busy_on_send) { - driver_cancel_timer(desc->inet.port); - } + cancel_multi_timer(desc, INETP(desc)->port, &tcp_inet_recv_timeout); sock_select(INETP(desc),(FD_READ|FD_CLOSE),0); if (desc->i_buf != NULL) tcp_restart_input(desc); @@ -10777,7 +10810,7 @@ static int tcp_recv(tcp_descriptor* desc, int request_len) int len; int nread; - if (desc->i_buf == NULL) { /* allocte a read buffer */ + if (desc->i_buf == NULL) { /* allocate a read buffer */ int sz = (request_len > 0) ? request_len : desc->inet.bufsz; if ((desc->i_buf = alloc_buffer(sz)) == NULL) @@ -10850,10 +10883,11 @@ static int tcp_recv(tcp_descriptor* desc, int request_len) return tcp_deliver(desc, desc->i_ptr - desc->i_ptr_start); } else { - if ((nread = tcp_remain(desc, &len)) < 0) + nread = tcp_remain(desc, &len); + if (nread < 0) return tcp_recv_error(desc, EMSGSIZE); else if (nread == 0) - return tcp_deliver(desc, len); + return tcp_deliver(desc, len); else if (len > 0) desc->i_remain = len; /* set remain */ } @@ -11172,7 +11206,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) } if (timeout != NULL) { - remove_multi_timer(&(desc->mtd), desc->inet.port, timeout); + remove_multi_timer(desc, desc->inet.port, timeout); } driver_demonitor_process(desc->inet.port, &monitor); @@ -11231,8 +11265,8 @@ static int tcp_send_or_shutdown_error(tcp_descriptor* desc, int err) if (IS_BUSY(INETP(desc))) { desc->inet.caller = desc->inet.busy_caller; if (desc->busy_on_send) { - driver_cancel_timer(desc->inet.port); - desc->busy_on_send = 0; + cancel_multi_timer(desc, INETP(desc)->port, &tcp_inet_send_timeout); + desc->busy_on_send = 0; } desc->inet.state &= ~INET_F_BUSY; set_busy_port(desc->inet.port, 0); @@ -11247,27 +11281,31 @@ static int tcp_send_or_shutdown_error(tcp_descriptor* desc, int err) DEBUGF(("driver_failure_eof(%ld) in %s, line %d\r\n", (long)desc->inet.port, __FILE__, __LINE__)); if (desc->inet.active) { + ErlDrvTermData err_atom; if (show_econnreset) { tcp_error_message(desc, err); - tcp_closed_message(desc); - inet_reply_error(INETP(desc), err); + err_atom = error_atom(err); } else { - tcp_closed_message(desc); - inet_reply_error_am(INETP(desc), am_closed); + err_atom = am_closed; } + tcp_closed_message(desc); + if (!(desc->tcp_add_flags & TCP_ADDF_SENDFILE)) + inet_reply_error_am(INETP(desc), err_atom); + if (desc->inet.exitf) driver_exit(desc->inet.port, 0); else tcp_desc_close(desc); } else { tcp_close_check(desc); - tcp_desc_close(desc); if (desc->inet.caller) { - if (show_econnreset) - inet_reply_error(INETP(desc), err); - else - inet_reply_error_am(INETP(desc), am_closed); + if (!(desc->tcp_add_flags & TCP_ADDF_SENDFILE)) { + if (show_econnreset) + inet_reply_error(INETP(desc), err); + else + inet_reply_error_am(INETP(desc), am_closed); + } } else { /* No blocking send op to reply to right now. @@ -11276,6 +11314,7 @@ static int tcp_send_or_shutdown_error(tcp_descriptor* desc, int err) */ desc->tcp_add_flags |= TCP_ADDF_DELAYED_CLOSE_SEND; } + tcp_desc_close(desc); /* * Make sure that the next receive operation gets an {error,closed} @@ -11332,6 +11371,12 @@ static int tcp_shutdown_error(tcp_descriptor* desc, int err) return tcp_send_or_shutdown_error(desc, err); } +static void tcp_inet_delay_send(ErlDrvData data, ErlDrvTermData dummy) +{ + tcp_descriptor *desc = (tcp_descriptor*)data; + (void)tcp_inet_output(desc, INETP(desc)->s); +} + /* ** Send non-blocking vector data */ @@ -11384,7 +11429,9 @@ static int tcp_sendv(tcp_descriptor* desc, ErlIOVec* ev) set_busy_port(desc->inet.port, 1); if (desc->send_timeout != INET_INFINITY) { desc->busy_on_send = 1; - driver_set_timer(desc->inet.port, desc->send_timeout); + add_multi_timer(desc, INETP(desc)->port, + 0 /* arg */, desc->send_timeout /* timeout */, + &tcp_inet_send_timeout); } return 1; } @@ -11399,7 +11446,10 @@ static int tcp_sendv(tcp_descriptor* desc, ErlIOVec* ev) INETP(desc)->is_ignored |= INET_IGNORE_WRITE; n = 0; } else if (desc->tcp_add_flags & TCP_ADDF_DELAY_SEND) { - n = 0; + driver_enqv(ix, ev, 0); + add_multi_timer(desc, INETP(desc)->port, 0, + 0, &tcp_inet_delay_send); + return 0; } else if (IS_SOCKET_ERROR(sock_sendv(desc->inet.s, ev->iov, vsize, &n, 0))) { if ((sock_errno() != ERRNO_BLOCK) && (sock_errno() != EINTR)) { @@ -11482,7 +11532,9 @@ static int tcp_send(tcp_descriptor* desc, char* ptr, ErlDrvSizeT len) set_busy_port(desc->inet.port, 1); if (desc->send_timeout != INET_INFINITY) { desc->busy_on_send = 1; - driver_set_timer(desc->inet.port, desc->send_timeout); + add_multi_timer(desc, INETP(desc)->port, + 0 /* arg */, desc->send_timeout /* timeout */, + &tcp_inet_send_timeout); } return 1; } @@ -11586,7 +11638,8 @@ static int tcp_sendfile_completed(tcp_descriptor* desc) { /* if we have a timer then cancel and send ok to client */ if (desc->busy_on_send) { - driver_cancel_timer(desc->inet.port); + cancel_multi_timer(desc, INETP(desc)->port, + &tcp_inet_send_timeout); desc->busy_on_send = 0; } @@ -11788,8 +11841,8 @@ socket_error: { DEBUGF(("tcp_inet_sendfile(%ld): send errno = %d (errno %d)\r\n", (long)desc->inet.port, socket_errno, errno)); - result = tcp_send_error(desc, socket_errno); tcp_sendfile_aborted(desc, socket_errno); + result = tcp_send_error(desc, socket_errno); goto done; } @@ -11893,6 +11946,12 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) #ifdef __WIN32__ desc->inet.send_would_block = 1; #endif + /* If DELAY_SEND is set ready_output may have + been called without doing select so we do + a select in order to get into the correct + state */ + if (desc->tcp_add_flags & TCP_ADDF_DELAY_SEND) + sock_select(INETP(desc), FD_WRITE, 1); goto done; } else if (n == 0) { /* Workaround for redhat/CentOS 6.3 returning 0 when sending packets with @@ -11918,7 +11977,7 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) set_busy_port(desc->inet.port, 0); /* if we have a timer then cancel and send ok to client */ if (desc->busy_on_send) { - driver_cancel_timer(desc->inet.port); + cancel_multi_timer(desc, INETP(desc)->port, &tcp_inet_send_timeout); desc->busy_on_send = 0; } inet_reply_ok(INETP(desc)); @@ -12730,7 +12789,7 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) udesc->i_buf = NULL; if (!desc->active) { async_error(desc, err); - driver_cancel_timer(desc->port); + driver_cancel_timer(desc->port); sock_select(desc,FD_READ,0); } else { @@ -12819,7 +12878,7 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) return count; count++; if (!desc->active) { - driver_cancel_timer(desc->port); /* possibly cancel */ + driver_cancel_timer(desc->port); sock_select(desc,FD_READ,0); return count; /* passive mode (read one packet only) */ } @@ -12898,55 +12957,69 @@ make_noninheritable_handle(SOCKET s) * Multi-timers */ -static void fire_multi_timers(MultiTimerData **first, ErlDrvPort port, +static void fire_multi_timers(tcp_descriptor *desc, ErlDrvPort port, ErlDrvData data) { ErlDrvTime next_timeout; - if (!*first) { + MultiTimerData *curr = desc->mtd; + if (!curr) { ASSERT(0); return; } #ifdef DEBUG { ErlDrvTime chk = erl_drv_monotonic_time(ERL_DRV_MSEC); - ASSERT(chk >= (*first)->when); + ASSERT(chk >= curr->when); } #endif do { - MultiTimerData *save = *first; - *first = save->next; + MultiTimerData *save = curr; + (*(save->timeout_function))(data,save->caller); - FREE(save); - if (*first == NULL) { + + curr = curr->next; + + if (desc->mtd_cache == NULL) + desc->mtd_cache = save; + else + FREE(save); + + if (curr == NULL) { + desc->mtd = NULL; return; } - (*first)->prev = NULL; - next_timeout = (*first)->when - erl_drv_monotonic_time(ERL_DRV_MSEC); + curr->prev = NULL; + next_timeout = curr->when - erl_drv_monotonic_time(ERL_DRV_MSEC); } while (next_timeout <= 0); + desc->mtd = curr; driver_set_timer(port, (unsigned long) next_timeout); } -static void clean_multi_timers(MultiTimerData **first, ErlDrvPort port) +static void clean_multi_timers(tcp_descriptor *desc, ErlDrvPort port) { - MultiTimerData *p; - if (*first) { + if (desc->mtd) { driver_cancel_timer(port); } - while (*first) { - p = *first; - *first = p->next; - FREE(p); + while (desc->mtd) { + MultiTimerData *p = desc->mtd; + desc->mtd = p->next; + FREE(p); + } + desc->mtd = NULL; + if (desc->mtd_cache) { + FREE(desc->mtd_cache); + desc->mtd_cache = NULL; } } -static void remove_multi_timer(MultiTimerData **first, ErlDrvPort port, MultiTimerData *p) +static void remove_multi_timer(tcp_descriptor *desc, ErlDrvPort port, MultiTimerData *p) { if (p->prev != NULL) { p->prev->next = p->next; } else { driver_cancel_timer(port); - *first = p->next; - if (*first) { - ErlDrvTime ntmo = (*first)->when - erl_drv_monotonic_time(ERL_DRV_MSEC); + desc->mtd = p->next; + if (desc->mtd) { + ErlDrvTime ntmo = desc->mtd->when - erl_drv_monotonic_time(ERL_DRV_MSEC); if (ntmo < 0) ntmo = 0; driver_set_timer(port, (unsigned long) ntmo); @@ -12955,36 +13028,67 @@ static void remove_multi_timer(MultiTimerData **first, ErlDrvPort port, MultiTim if (p->next != NULL) { p->next->prev = p->prev; } - FREE(p); + if (desc->mtd_cache == NULL) + desc->mtd_cache = p; + else + FREE(p); +} + +/* Cancel a timer based on the timeout_fun */ +static void cancel_multi_timer(tcp_descriptor *desc, ErlDrvPort port, + void (*timeout_fun)(ErlDrvData drv_data, + ErlDrvTermData caller)) +{ + MultiTimerData *timer = desc->mtd; + while(timer && timer->timeout_function != timeout_fun) { + timer = timer->next; + } + if (timer) { + remove_multi_timer(desc, port, timer); + } } -static MultiTimerData *add_multi_timer(MultiTimerData **first, ErlDrvPort port, +static MultiTimerData *add_multi_timer(tcp_descriptor *desc, ErlDrvPort port, ErlDrvTermData caller, unsigned timeout, void (*timeout_fun)(ErlDrvData drv_data, ErlDrvTermData caller)) { MultiTimerData *mtd, *p, *s; - mtd = ALLOC(sizeof(MultiTimerData)); - mtd->when = erl_drv_monotonic_time(ERL_DRV_MSEC) + ((ErlDrvTime) timeout) + 1; + + /* Use cached timer if available */ + if (desc->mtd_cache != NULL) { + mtd = desc->mtd_cache; + desc->mtd_cache = NULL; + } else + mtd = ALLOC(sizeof(MultiTimerData)); + + if (timeout) + mtd->when = erl_drv_monotonic_time(ERL_DRV_MSEC) + ((ErlDrvTime) timeout); + else + mtd->when = INT64_MIN; /* Don't have to get the time for 0 msec timeouts */ + mtd->timeout_function = timeout_fun; mtd->caller = caller; mtd->next = mtd->prev = NULL; - for(p = *first,s = NULL; p != NULL; s = p, p = p->next) { + + /* Find correct slot in timer linked list */ + for(p = desc->mtd,s = NULL; p != NULL; s = p, p = p->next) { if (p->when >= mtd->when) { break; } } + /* Insert in linked list */ if (!p) { if (!s) { - *first = mtd; + desc->mtd = mtd; } else { s->next = mtd; mtd->prev = s; } } else { if (!s) { - *first = mtd; + desc->mtd = mtd; } else { s->next = mtd; mtd->prev = s; @@ -12992,10 +13096,8 @@ static MultiTimerData *add_multi_timer(MultiTimerData **first, ErlDrvPort port, mtd->next = p; p->prev = mtd; } + /* Possibly set new timer */ if (!s) { - if (mtd->next) { - driver_cancel_timer(port); - } driver_set_timer(port,timeout); } return mtd; diff --git a/erts/emulator/nifs/common/prim_file_nif.c b/erts/emulator/nifs/common/prim_file_nif.c index 0ada345442..3df04e42e2 100644 --- a/erts/emulator/nifs/common/prim_file_nif.c +++ b/erts/emulator/nifs/common/prim_file_nif.c @@ -38,7 +38,6 @@ static void unload(ErlNifEnv *env, void* priv_data); static ErlNifResourceType *efile_resource_type; -static ERL_NIF_TERM am_erts_prim_file; static ERL_NIF_TERM am_close; static ERL_NIF_TERM am_ok; @@ -220,7 +219,6 @@ static int load(ErlNifEnv *env, void** priv_data, ERL_NIF_TERM prim_file_pid) ASSERT(!"bad pid passed to prim_file_nif"); } - am_erts_prim_file = enif_make_atom(env, "erts_prim_file"); am_close = enif_make_atom(env, "close"); am_ok = enif_make_atom(env, "ok"); @@ -936,7 +934,7 @@ static ERL_NIF_TERM set_permissions_nif(ErlNifEnv *env, int argc, const ERL_NIF_ posix_errno_t posix_errno; efile_path_t path; - Uint32 permissions; + unsigned int permissions; ASSERT(argc == 2); if(!enif_get_uint(env, argv[1], &permissions)) { @@ -956,7 +954,7 @@ static ERL_NIF_TERM set_owner_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM a posix_errno_t posix_errno; efile_path_t path; - Sint32 uid, gid; + int uid, gid; ASSERT(argc == 3); if(!enif_get_int(env, argv[1], &uid) || !enif_get_int(env, argv[2], &gid)) { diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index f421794f91..304e0a5d0c 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -46,11 +46,11 @@ #if 0 #define DEBUG_PRINT(FMT, ...) erts_printf(FMT "\r\n", ##__VA_ARGS__) #define DEBUG_PRINT_FD(FMT, STATE, ...) \ - DEBUG_PRINT("%d: " FMT " (ev=%s, ac=%s, flg=%d)", \ + DEBUG_PRINT("%d: " FMT " (ev=%s, ac=%s, flg=%s)", \ (STATE) ? (STATE)->fd : (ErtsSysFdType)-1, ##__VA_ARGS__, \ ev2str((STATE) ? (STATE)->events : ERTS_POLL_EV_NONE), \ ev2str((STATE) ? (STATE)->active_events : ERTS_POLL_EV_NONE), \ - (STATE) ? (STATE)->flags : ERTS_EV_FLAG_CLEAR) + (STATE) ? flag2str((STATE)->flags) : ERTS_EV_FLAG_CLEAR) #define DEBUG_PRINT_MODE #else #define DEBUG_PRINT(...) @@ -76,22 +76,40 @@ typedef enum { typedef enum { ERTS_EV_FLAG_CLEAR = 0, ERTS_EV_FLAG_USED = 1, /* ERL_DRV_USE has been turned on */ -#ifdef ERTS_ENABLE_KERNEL_POLL - ERTS_EV_FLAG_FALLBACK = 2, /* Set when kernel poll rejected fd +#if ERTS_POLL_USE_SCHEDULER_POLLING + ERTS_EV_FLAG_SCHEDULER = 2, /* Set when the fd has been migrated + to scheduler pollset */ + ERTS_EV_FLAG_IN_SCHEDULER = 4, /* Set when the fd is currently in + scheduler pollset */ +#else + ERTS_EV_FLAG_SCHEDULER = ERTS_EV_FLAG_CLEAR, + ERTS_EV_FLAG_IN_SCHEDULER = ERTS_EV_FLAG_CLEAR, +#endif +#ifdef ERTS_POLL_USE_FALLBACK + ERTS_EV_FLAG_FALLBACK = 8, /* Set when kernel poll rejected fd and it was put in the nkp version */ #else ERTS_EV_FLAG_FALLBACK = ERTS_EV_FLAG_CLEAR, #endif /* Combinations */ - ERTS_EV_FLAG_USED_FALLBACK = ERTS_EV_FLAG_USED | ERTS_EV_FLAG_FALLBACK + ERTS_EV_FLAG_USED_FALLBACK = ERTS_EV_FLAG_USED | ERTS_EV_FLAG_FALLBACK, + ERTS_EV_FLAG_USED_SCHEDULER = ERTS_EV_FLAG_USED | ERTS_EV_FLAG_SCHEDULER, + ERTS_EV_FLAG_USED_IN_SCHEDULER = ERTS_EV_FLAG_USED | ERTS_EV_FLAG_SCHEDULER | ERTS_EV_FLAG_IN_SCHEDULER, + ERTS_EV_FLAG_UNUSED_SCHEDULER = ERTS_EV_FLAG_SCHEDULER, + ERTS_EV_FLAG_UNUSED_IN_SCHEDULER = ERTS_EV_FLAG_SCHEDULER | ERTS_EV_FLAG_IN_SCHEDULER } EventStateFlags; #define flag2str(flags) \ ((flags) == ERTS_EV_FLAG_CLEAR ? "CLEAR" : \ ((flags) == ERTS_EV_FLAG_USED ? "USED" : \ ((flags) == ERTS_EV_FLAG_FALLBACK ? "FLBK" : \ - ((flags) == ERTS_EV_FLAG_USED_FALLBACK ? "USED|FLBK" : "ERROR")))) + ((flags) == ERTS_EV_FLAG_USED_FALLBACK ? "USED|FLBK" : \ + ((flags) == ERTS_EV_FLAG_USED_SCHEDULER ? "USED|SCHD" : \ + ((flags) == ERTS_EV_FLAG_UNUSED_SCHEDULER ? "SCHD" : \ + ((flags) == ERTS_EV_FLAG_USED_IN_SCHEDULER ? "USED|IN_SCHD" : \ + ((flags) == ERTS_EV_FLAG_UNUSED_IN_SCHEDULER ? "IN_SCHD" : \ + "ERROR")))))))) /* How many events that can be handled at once by one erts_poll_wait call */ #define ERTS_CHECK_IO_POLL_RES_LEN 512 @@ -105,6 +123,7 @@ typedef struct erts_poll_thread { ErtsPollSet *ps; ErtsPollResFd *pollres; + ErtsThrPrgrData *tpd; int pollres_len; } ErtsPollThread; @@ -112,10 +131,13 @@ typedef struct erts_poll_thread * Which pollset to use is determined by hashing the fd. */ static ErtsPollSet **pollsetv; +static ErtsPollThread *psiv; #if ERTS_POLL_USE_FALLBACK static ErtsPollSet *flbk_pollset; #endif -static ErtsPollThread *psiv; +#if ERTS_POLL_USE_SCHEDULER_POLLING +static ErtsPollSet *sched_pollset; +#endif typedef struct { #ifndef ERTS_SYS_CONTINOUS_FD_NUMBERS @@ -130,10 +152,12 @@ typedef struct { ErtsResource* resource; /* ERTS_EV_TYPE_STOP_NIF */ } stop; } driver; - ErtsPollEvents events; /* The events that have been selected upon */ + ErtsPollEvents events; /* The events that have been selected upon */ ErtsPollEvents active_events; /* The events currently active in the pollset */ EventStateType type; EventStateFlags flags; + int count; /* Number of times this fd has triggered + without being deselected. */ } ErtsDrvEventState; struct drv_ev_state_shared { @@ -370,12 +394,22 @@ get_pollset(ErtsSysFdType fd) #if ERTS_POLL_USE_FALLBACK static ERTS_INLINE ErtsPollSet * -get_fallback(void) +get_fallback_pollset(void) { return flbk_pollset; } #endif +static ERTS_INLINE ErtsPollSet * +get_scheduler_pollset(ErtsSysFdType fd) +{ +#if ERTS_POLL_USE_SCHEDULER_POLLING + return sched_pollset; +#else + return get_pollset(fd); +#endif +} + /* * Place a fd within a pollset. This will automatically use * the fallback ps if needed. @@ -391,18 +425,27 @@ erts_io_control_wakeup(ErtsDrvEventState *state, ErtsPollOp op, ERTS_LC_ASSERT(erts_lc_mtx_is_locked(fd_mtx(state->fd))); if (!(flags & ERTS_EV_FLAG_FALLBACK)) { - res = erts_poll_control(get_pollset(fd), fd, op, pe, wake_poller); + + if (op == ERTS_POLL_OP_DEL && (flags & ERTS_EV_FLAG_SCHEDULER)) { + erts_poll_control(get_scheduler_pollset(fd), fd, op, pe, wake_poller); + flags &= ~ERTS_EV_FLAG_IN_SCHEDULER; + } + if (!(flags & ERTS_EV_FLAG_IN_SCHEDULER) || (pe & ERTS_POLL_EV_OUT)) { + res = erts_poll_control(get_pollset(fd), fd, op, pe, wake_poller); + } else { + res = erts_poll_control(get_scheduler_pollset(fd), fd, op, pe, wake_poller); + } #if ERTS_POLL_USE_FALLBACK if (op == ERTS_POLL_OP_ADD && res == ERTS_POLL_EV_NVAL) { /* When an add fails with NVAL, the poll/kevent operation could not put that fd in the pollset, so we instead put it into a fallback pollset */ state->flags |= ERTS_EV_FLAG_FALLBACK; - res = erts_poll_control_flbk(get_fallback(), fd, op, pe, wake_poller); + res = erts_poll_control_flbk(get_fallback_pollset(), fd, op, pe, wake_poller); } } else { ASSERT(op != ERTS_POLL_OP_ADD); - res = erts_poll_control_flbk(get_fallback(), fd, op, pe, wake_poller); + res = erts_poll_control_flbk(get_fallback_pollset(), fd, op, pe, wake_poller); #endif } @@ -425,59 +468,77 @@ erts_io_notify_port_task_executed(ErtsPortTaskType type, ErtsIoTask *itp = ErtsContainerStruct(pthp, ErtsIoTask, task); ErtsSysFdType fd = itp->fd; erts_mtx_t *mtx = fd_mtx(fd); - int active_events; + ErtsPollOp op = ERTS_POLL_OP_MOD; + int active_events, new_events = 0; ErtsDrvEventState *state; ErtsDrvSelectDataState *free_select = NULL; ErtsNifSelectDataState *free_nif = NULL; + ERTS_MSACC_PUSH_AND_SET_STATE_M_X(ERTS_MSACC_STATE_CHECK_IO); + erts_mtx_lock(mtx); state = get_drv_ev_state(fd); + reset_handle(pthp); + active_events = state->active_events; - switch (type) { - case ERTS_PORT_TASK_INPUT: + if (!(state->flags & ERTS_EV_FLAG_IN_SCHEDULER) || type == ERTS_PORT_TASK_OUTPUT) { + switch (type) { + case ERTS_PORT_TASK_INPUT: + + DEBUG_PRINT_FD("executed ready_input", state); + + ASSERT(!(state->active_events & ERTS_POLL_EV_IN)); + if (state->events & ERTS_POLL_EV_IN) { + active_events |= ERTS_POLL_EV_IN; + if (state->count > 10 && ERTS_POLL_USE_SCHEDULER_POLLING) { + if (!(state->flags & ERTS_EV_FLAG_SCHEDULER)) + op = ERTS_POLL_OP_ADD; + state->flags |= ERTS_EV_FLAG_IN_SCHEDULER|ERTS_EV_FLAG_SCHEDULER; + new_events = ERTS_POLL_EV_IN; + DEBUG_PRINT_FD("moving to scheduler ps", state); + } else + new_events = active_events; + if (!(state->flags & ERTS_EV_FLAG_FALLBACK) && ERTS_POLL_USE_SCHEDULER_POLLING) + state->count++; + } + break; + case ERTS_PORT_TASK_OUTPUT: - DEBUG_PRINT_FD("executed ready_input", state); + DEBUG_PRINT_FD("executed ready_output", state); - ASSERT(!(state->active_events & ERTS_POLL_EV_IN)); - if (state->events & ERTS_POLL_EV_IN) - active_events |= ERTS_POLL_EV_IN; - break; - case ERTS_PORT_TASK_OUTPUT: + ASSERT(!(state->active_events & ERTS_POLL_EV_OUT)); + if (state->events & ERTS_POLL_EV_OUT) { + active_events |= ERTS_POLL_EV_OUT; + if (state->flags & ERTS_EV_FLAG_IN_SCHEDULER && active_events & ERTS_POLL_EV_IN) + new_events = ERTS_POLL_EV_OUT; + else + new_events = active_events; + } + break; + default: + erts_exit(ERTS_ABORT_EXIT, "Invalid IO port task type"); + break; + } - DEBUG_PRINT_FD("executed ready_output", state); + if (state->active_events != active_events && new_events) { + state->active_events = active_events; + new_events = erts_io_control(state, op, new_events); + } - ASSERT(!(state->active_events & ERTS_POLL_EV_OUT)); - if (state->events & ERTS_POLL_EV_OUT) - active_events |= ERTS_POLL_EV_OUT; - break; - default: - erts_exit(ERTS_ABORT_EXIT, "Invalid IO port task type"); - break; + /* We were unable to re-insert the fd into the pollset, signal the callback. */ + if (new_events & (ERTS_POLL_EV_ERR|ERTS_POLL_EV_NVAL)) { + if (state->active_events & ERTS_POLL_EV_IN) + iready(state->driver.select->inport, state); + if (state->active_events & ERTS_POLL_EV_OUT) + oready(state->driver.select->outport, state); + state->active_events = 0; + } } - reset_handle(pthp); - - if (active_events) { - /* This is not needed if active_events has not changed */ - if (state->active_events != active_events) { - ErtsPollEvents new_events; - state->active_events = active_events; - new_events = erts_io_control(state, ERTS_POLL_OP_MOD, active_events); - - /* We were unable to re-insert the fd into the pollset, signal the callback. */ - if (new_events & (ERTS_POLL_EV_ERR|ERTS_POLL_EV_NVAL)) { - if (active_events & ERTS_POLL_EV_IN) - iready(state->driver.select->inport, state); - if (active_events & ERTS_POLL_EV_OUT) - oready(state->driver.select->outport, state); - state->active_events = 0; - } - } - } else { + if (!active_events) check_fd_cleanup(state, &free_select, &free_nif); - } erts_mtx_unlock(mtx); @@ -485,6 +546,8 @@ erts_io_notify_port_task_executed(ErtsPortTaskType type, free_drv_select_data(free_select); if (free_nif) free_nif_select_data(free_nif); + + ERTS_MSACC_POP_STATE_M_X(); } static ERTS_INLINE void @@ -528,6 +591,96 @@ abort_tasks(ErtsDrvEventState *state, int mode) } } +static void prepare_select_msg(struct erts_nif_select_event* e, + enum ErlNifSelectFlags mode, + Eterm recipient, + ErtsResource* resource, + Eterm msg, + ErlNifEnv* msg_env, + Eterm event_atom) +{ + ErtsMessage* mp; + Eterm* hp; + Uint hsz; + + if (is_not_nil(e->pid)) { + ASSERT(e->mp); + erts_cleanup_messages(e->mp); + } + + if (mode & ERL_NIF_SELECT_CUSTOM_MSG) { + if (msg_env) { + mp = erts_create_message_from_nif_env(msg_env); + ERL_MESSAGE_TERM(mp) = msg; + } + else { + hsz = size_object(msg); + mp = erts_alloc_message(hsz, &hp); + ERL_MESSAGE_TERM(mp) = copy_struct(msg, hsz, &hp, &mp->hfrag.off_heap); + } + } + else { + ErtsBinary* bin; + Eterm resource_term, ref_term, tuple; + Eterm* hp_start; + + /* {select, Resource, Ref, EventAtom} */ + hsz = 5 + ERTS_MAGIC_REF_THING_SIZE; + if (is_internal_ref(msg)) + hsz += ERTS_REF_THING_SIZE; + else + ASSERT(is_immed(msg)); + + mp = erts_alloc_message(hsz, &hp); + hp_start = hp; + + bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource); + resource_term = erts_mk_magic_ref(&hp, &mp->hfrag.off_heap, &bin->binary); + if (is_internal_ref(msg)) { + Uint32* refn = internal_ref_numbers(msg); + write_ref_thing(hp, refn[0], refn[1], refn[2]); + ref_term = make_internal_ref(hp); + hp += ERTS_REF_THING_SIZE; + } + else { + ASSERT(is_immed(msg)); + ref_term = msg; + } + tuple = TUPLE4(hp, am_select, resource_term, ref_term, event_atom); + hp += 5; + ERL_MESSAGE_TERM(mp) = tuple; + ASSERT(hp == hp_start + hsz); (void)hp_start; + } + + ASSERT(is_not_nil(recipient)); + e->pid = recipient; + e->mp = mp; +} + +static ERTS_INLINE void send_select_msg(struct erts_nif_select_event* e) +{ + Process* rp = erts_proc_lookup(e->pid); + + ASSERT(is_internal_pid(e->pid)); + if (!rp) { + erts_cleanup_messages(e->mp); + return; + } + + erts_queue_message(rp, 0, e->mp, ERL_MESSAGE_TERM(e->mp), am_system); +} + +static void clear_select_event(struct erts_nif_select_event* e) +{ + if (is_not_nil(e->pid)) { + /* Discard unsent message */ + ASSERT(e->mp); + erts_cleanup_messages(e->mp); + e->mp = NULL; + e->pid = NIL; + } +} + static void deselect(ErtsDrvEventState *state, int mode) { @@ -558,8 +711,8 @@ deselect(ErtsDrvEventState *state, int mode) erts_io_control(state, ERTS_POLL_OP_DEL, 0); switch (state->type) { case ERTS_EV_TYPE_NIF: - state->driver.nif->in.pid = NIL; - state->driver.nif->out.pid = NIL; + clear_select_event(&state->driver.nif->in); + clear_select_event(&state->driver.nif->out); enif_release_resource(state->driver.stop.resource->data); state->driver.stop.resource = NULL; break; @@ -755,11 +908,22 @@ driver_select(ErlDrvPort ix, ErlDrvEvent e, int mode, int on) if (old_events == 0 && !(state->flags & ERTS_EV_FLAG_USED)) { ctl_op = ERTS_POLL_OP_ADD; } + new_events = state->active_events; + if (state->flags & ERTS_EV_FLAG_IN_SCHEDULER) + new_events &= ~ERTS_POLL_EV_IN; } else { ctl_events &= old_events; state->events &= ~ctl_events; state->active_events &= ~ctl_events; + new_events = state->active_events; + + if (ctl_events & ERTS_POLL_EV_IN) { + state->count = 0; + if (state->flags & ERTS_EV_FLAG_IN_SCHEDULER) { + new_events = 0; + } + } if (!state->events) { if (!(state->flags & ERTS_EV_FLAG_USED) || mode & ERL_DRV_USE) @@ -770,7 +934,7 @@ driver_select(ErlDrvPort ix, ErlDrvEvent e, int mode, int on) if (ctl_events || ctl_op == ERTS_POLL_OP_DEL) { new_events = erts_io_control_wakeup(state, ctl_op, - state->active_events, + new_events, &wake_poller); ASSERT(state->type == ERTS_EV_TYPE_DRV_SEL || state->type == ERTS_EV_TYPE_NONE); @@ -802,6 +966,7 @@ driver_select(ErlDrvPort ix, ErlDrvEvent e, int mode, int on) if (ctl_events & ERTS_POLL_EV_IN) { abort_tasks(state, ERL_DRV_READ); state->driver.select->inport = NIL; + state->flags &= ~ERTS_EV_FLAG_IN_SCHEDULER; } if (ctl_events & ERTS_POLL_EV_OUT) { abort_tasks(state, ERL_DRV_WRITE); @@ -810,6 +975,8 @@ driver_select(ErlDrvPort ix, ErlDrvEvent e, int mode, int on) if (state->events == 0) { if ((mode & ERL_DRV_USE) || !(state->flags & ERTS_EV_FLAG_USED)) { state->type = ERTS_EV_TYPE_NONE; + if (state->flags & ERTS_EV_FLAG_SCHEDULER) + erts_atomic32_read_bor_nob(&prt->state, ERTS_PORT_SFLG_CHECK_FD_CLEANUP); state->flags = 0; } /*else keep it, as fd will probably be selected upon again */ @@ -866,12 +1033,21 @@ done_unknown: } int -enif_select(ErlNifEnv* env, - ErlNifEvent e, - enum ErlNifSelectFlags mode, - void* obj, - const ErlNifPid* pid, - Eterm ref) +enif_select(ErlNifEnv* env, ErlNifEvent e, enum ErlNifSelectFlags mode, + void* obj, const ErlNifPid* pid, Eterm msg) +{ + return enif_select_x(env, e, mode, obj, pid, msg, NULL); +} + + +int +enif_select_x(ErlNifEnv* env, + ErlNifEvent e, + enum ErlNifSelectFlags mode, + void* obj, + const ErlNifPid* pid, + Eterm msg, + ErlNifEnv* msg_env) { int on; ErtsResource* resource = DATA_TO_RESOURCE(obj); @@ -889,7 +1065,7 @@ enif_select(ErlNifEnv* env, #ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS if (!grow_drv_ev_state(fd)) { - if (fd > 0) nif_select_large_fd_error(fd, mode, resource, ref); + if (fd > 0) nif_select_large_fd_error(fd, mode, resource, msg); return INT_MIN | ERL_NIF_SELECT_INVALID_EVENT; } #endif @@ -935,21 +1111,21 @@ enif_select(ErlNifEnv* env, * Changing process and/or ref is ok (I think?). */ if (state->driver.stop.resource != resource) - nif_select_steal(state, ERL_DRV_READ | ERL_DRV_WRITE, resource, ref); + nif_select_steal(state, ERL_DRV_READ | ERL_DRV_WRITE, resource, msg); break; case ERTS_EV_TYPE_DRV_SEL: - nif_select_steal(state, mode, resource, ref); + nif_select_steal(state, mode, resource, msg); break; case ERTS_EV_TYPE_STOP_USE: { erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); - print_nif_select_op(dsbufp, fd, mode, resource, ref); + print_nif_select_op(dsbufp, fd, mode, resource, msg); steal_pending_stop_use(dsbufp, ERTS_INVALID_ERL_DRV_PORT, state, mode, on); ASSERT(state->type == ERTS_EV_TYPE_NONE); break; } case ERTS_EV_TYPE_STOP_NIF: { erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); - print_nif_select_op(dsbufp, fd, mode, resource, ref); + print_nif_select_op(dsbufp, fd, mode, resource, msg); steal_pending_stop_nif(dsbufp, resource, state, mode, on); if (state->type == ERTS_EV_TYPE_STOP_NIF) { ret = ERL_NIF_SELECT_STOP_SCHEDULED; /* ?? */ @@ -1005,7 +1181,6 @@ enif_select(ErlNifEnv* env, if (on) { const Eterm recipient = pid ? pid->pid : env->proc->common.id; - Uint32* refn; if (!state->driver.nif) state->driver.nif = alloc_nif_select_data(); if (state->type == ERTS_EV_TYPE_NONE) { @@ -1016,28 +1191,13 @@ enif_select(ErlNifEnv* env, ASSERT(state->type == ERTS_EV_TYPE_NIF); ASSERT(state->driver.stop.resource == resource); if (mode & ERL_DRV_READ) { - state->driver.nif->in.pid = recipient; - if (is_immed(ref)) { - state->driver.nif->in.immed = ref; - } else { - ASSERT(is_internal_ref(ref)); - refn = internal_ref_numbers(ref); - state->driver.nif->in.immed = THE_NON_VALUE; - sys_memcpy(state->driver.nif->in.refn, refn, - sizeof(state->driver.nif->in.refn)); - } + prepare_select_msg(&state->driver.nif->in, mode, recipient, + resource, msg, msg_env, am_ready_input); + msg_env = NULL; } if (mode & ERL_DRV_WRITE) { - state->driver.nif->out.pid = recipient; - if (is_immed(ref)) { - state->driver.nif->out.immed = ref; - } else { - ASSERT(is_internal_ref(ref)); - refn = internal_ref_numbers(ref); - state->driver.nif->out.immed = THE_NON_VALUE; - sys_memcpy(state->driver.nif->out.refn, refn, - sizeof(state->driver.nif->out.refn)); - } + prepare_select_msg(&state->driver.nif->out, mode, recipient, + resource, msg, msg_env, am_ready_output); } ret = 0; } @@ -1046,12 +1206,12 @@ enif_select(ErlNifEnv* env, if (state->type == ERTS_EV_TYPE_NIF) { if (mode & ERL_NIF_SELECT_READ && is_not_nil(state->driver.nif->in.pid)) { - state->driver.nif->in.pid = NIL; + clear_select_event(&state->driver.nif->in); ret |= ERL_NIF_SELECT_READ_CANCELLED; } if (mode & ERL_NIF_SELECT_WRITE && is_not_nil(state->driver.nif->out.pid)) { - state->driver.nif->out.pid = NIL; + clear_select_event(&state->driver.nif->out); ret |= ERL_NIF_SELECT_WRITE_CANCELLED; } } @@ -1440,7 +1600,8 @@ iready(Eterm id, ErtsDrvEventState *state) if (erts_port_task_schedule(id, &iotask->task, ERTS_PORT_TASK_INPUT, - (ErlDrvEvent) state->fd) != 0) { + (ErlDrvEvent) state->fd, + state->flags & ERTS_EV_FLAG_IN_SCHEDULER) != 0) { stale_drv_select(id, state, ERL_DRV_READ); } else { DEBUG_PRINT_FD("schedule ready_input(%T, %d)", @@ -1458,7 +1619,8 @@ oready(Eterm id, ErtsDrvEventState *state) if (erts_port_task_schedule(id, &iotask->task, ERTS_PORT_TASK_OUTPUT, - (ErlDrvEvent) state->fd) != 0) { + (ErlDrvEvent) state->fd, + 0) != 0) { stale_drv_select(id, state, ERL_DRV_WRITE); } else { DEBUG_PRINT_FD("schedule ready_output(%T, %d)", state, id, state->fd); @@ -1466,53 +1628,6 @@ oready(Eterm id, ErtsDrvEventState *state) } } -static ERTS_INLINE void -send_event_tuple(struct erts_nif_select_event* e, ErtsResource* resource, - Eterm event_atom) -{ - Process* rp = erts_proc_lookup(e->pid); - ErtsProcLocks rp_locks = 0; - ErtsMessage* mp; - ErlOffHeap* ohp; - ErtsBinary* bin; - Eterm* hp; - Uint hsz; - Eterm resource_term, ref_term, tuple; - - if (!rp) { - return; - } - - bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource); - - /* {select, Resource, Ref, EventAtom} */ - if (is_value(e->immed)) { - hsz = 5 + ERTS_MAGIC_REF_THING_SIZE; - } - else { - hsz = 5 + ERTS_MAGIC_REF_THING_SIZE + ERTS_REF_THING_SIZE; - } - - mp = erts_alloc_message_heap(rp, &rp_locks, hsz, &hp, &ohp); - - resource_term = erts_mk_magic_ref(&hp, ohp, &bin->binary); - if (is_value(e->immed)) { - ASSERT(is_immed(e->immed)); - ref_term = e->immed; - } - else { - write_ref_thing(hp, e->refn[0], e->refn[1], e->refn[2]); - ref_term = make_internal_ref(hp); - hp += ERTS_REF_THING_SIZE; - } - tuple = TUPLE4(hp, am_select, resource_term, ref_term, event_atom); - - erts_queue_message(rp, rp_locks, mp, tuple, am_system); - - if (rp_locks) - erts_proc_unlock(rp, rp_locks); -} - static void bad_fd_in_pollset(ErtsDrvEventState *, Eterm inport, Eterm outport); void @@ -1520,7 +1635,7 @@ erts_check_io_interrupt(ErtsPollThread *psi, int set) { if (psi) { #if ERTS_POLL_USE_FALLBACK - if (psi->ps == get_fallback()) { + if (psi->ps == get_fallback_pollset()) { erts_poll_interrupt_flbk(psi->ps, set); return; } @@ -1530,12 +1645,13 @@ erts_check_io_interrupt(ErtsPollThread *psi, int set) } ErtsPollThread * -erts_create_pollset_thread(int id) { +erts_create_pollset_thread(int id, ErtsThrPrgrData *tpd) { + psiv[id].tpd = tpd; return psiv+id; } void -erts_check_io(ErtsPollThread *psi) +erts_check_io(ErtsPollThread *psi, ErtsMonotonicTime timeout_time) { int pollres_len; int poll_ret, i; @@ -1550,14 +1666,14 @@ erts_check_io(ErtsPollThread *psi) pollres_len = psi->pollres_len; #if ERTS_POLL_USE_FALLBACK - if (psi->ps == get_fallback()) { + if (psi->ps == get_fallback_pollset()) { - poll_ret = erts_poll_wait_flbk(psi->ps, psi->pollres, &pollres_len); + poll_ret = erts_poll_wait_flbk(psi->ps, psi->pollres, &pollres_len, psi->tpd, timeout_time); } else #endif { - poll_ret = erts_poll_wait(psi->ps, psi->pollres, &pollres_len); + poll_ret = erts_poll_wait(psi->ps, psi->pollres, &pollres_len, psi->tpd, timeout_time); } #ifdef ERTS_ENABLE_LOCK_CHECK @@ -1593,7 +1709,12 @@ erts_check_io(ErtsPollThread *psi) ErtsNifSelectDataState *free_nif = NULL; ErtsSysFdType fd = (ErtsSysFdType) ERTS_POLL_RES_GET_FD(&psi->pollres[i]); ErtsDrvEventState *state; - ErtsPollEvents revents; + ErtsPollEvents revents = ERTS_POLL_RES_GET_EVTS(&psi->pollres[i]); + + /* The fd will be set to -1 if a pollset internal fd was triggered + that was determined to be too expensive to remove from the result. + */ + if (fd == -1) continue; erts_mtx_lock(fd_mtx(fd)); @@ -1604,8 +1725,6 @@ erts_check_io(ErtsPollThread *psi) continue; } - revents = ERTS_POLL_RES_GET_EVTS(&psi->pollres[i]); - DEBUG_PRINT_FD("triggered %s", state, ev2str(revents)); if (revents & ERTS_POLL_EV_ERR) { @@ -1617,25 +1736,39 @@ erts_check_io(ErtsPollThread *psi) */ revents = state->active_events; state->active_events = 0; + + if (state->flags & ERTS_EV_FLAG_IN_SCHEDULER) { + erts_io_control(state, ERTS_POLL_OP_MOD, 0); + state->flags &= ~ERTS_EV_FLAG_IN_SCHEDULER; + } } else { /* Disregard any events that are not active at the moment, for instance this could happen if the driver/nif does select/deselect in rapid succession. */ revents &= state->active_events | ERTS_POLL_EV_NVAL; - state->active_events &= ~revents; - /* Reactivate the poll op if there are still active events */ - if (state->active_events) { - ErtsPollEvents new_events; - DEBUG_PRINT_FD("re-enable %s", state, ev2str(state->active_events)); + if (psi->ps != get_scheduler_pollset(fd) || !ERTS_POLL_USE_SCHEDULER_POLLING) { + ErtsPollEvents reactive_events; + state->active_events &= ~revents; + + reactive_events = state->active_events; + + if (state->flags & ERTS_EV_FLAG_IN_SCHEDULER) + reactive_events &= ~ERTS_POLL_EV_IN; - new_events = erts_io_control(state, ERTS_POLL_OP_MOD, state->active_events); + /* Reactivate the poll op if there are still active events */ + if (reactive_events) { + ErtsPollEvents new_events; + DEBUG_PRINT_FD("re-enable %s", state, ev2str(reactive_events)); - /* Unable to re-enable the fd, signal all callbacks */ - if (new_events & (ERTS_POLL_EV_ERR|ERTS_POLL_EV_NVAL)) { - revents |= state->active_events; - state->active_events = 0; + new_events = erts_io_control(state, ERTS_POLL_OP_MOD, reactive_events); + + /* Unable to re-enable the fd, signal all callbacks */ + if (new_events & (ERTS_POLL_EV_ERR|ERTS_POLL_EV_NVAL)) { + revents |= reactive_events; + state->active_events &= ~reactive_events; + } } } } @@ -1668,7 +1801,6 @@ erts_check_io(ErtsPollThread *psi) case ERTS_EV_TYPE_NIF: { /* Requested via enif_select()... */ struct erts_nif_select_event in = {NIL}; struct erts_nif_select_event out = {NIL}; - ErtsResource* resource = NULL; if (revents & (ERTS_POLL_EV_IN|ERTS_POLL_EV_OUT)) { if (revents & ERTS_POLL_EV_OUT) { @@ -1676,6 +1808,7 @@ erts_check_io(ErtsPollThread *psi) out = state->driver.nif->out; resource = state->driver.stop.resource; state->driver.nif->out.pid = NIL; + state->driver.nif->out.mp = NULL; } } if (revents & ERTS_POLL_EV_IN) { @@ -1683,6 +1816,7 @@ erts_check_io(ErtsPollThread *psi) in = state->driver.nif->in; resource = state->driver.stop.resource; state->driver.nif->in.pid = NIL; + state->driver.nif->in.mp = NULL; } } state->events &= ~revents; @@ -1695,10 +1829,10 @@ erts_check_io(ErtsPollThread *psi) erts_mtx_unlock(fd_mtx(fd)); if (is_not_nil(in.pid)) { - send_event_tuple(&in, resource, am_ready_input); + send_select_msg(&in); } if (is_not_nil(out.pid)) { - send_event_tuple(&out, resource, am_ready_output); + send_select_msg(&out); } continue; } @@ -1711,7 +1845,7 @@ erts_check_io(ErtsPollThread *psi) case ERTS_EV_TYPE_STOP_USE: { #if ERTS_POLL_USE_FALLBACK - ASSERT(psi->ps == get_fallback()); + ASSERT(psi->ps == get_fallback_pollset()); #endif drv_ptr = state->driver.stop.drv_ptr; state->type = ERTS_EV_TYPE_NONE; @@ -2049,12 +2183,17 @@ erts_init_check_io(int *argc, char **argv) for (j=0; j < erts_no_pollsets; j++) pollsetv[j] = erts_poll_create_pollset(j); -#if ERTS_POLL_USE_FALLBACK - flbk_pollset = erts_poll_create_pollset_flbk(-1); + no_poll_threads = erts_no_poll_threads; + + j = -1; + +#if ERTS_POLL_USE_SCHEDULER_POLLING + sched_pollset = erts_poll_create_pollset(j--); + no_poll_threads++; #endif - no_poll_threads = erts_no_poll_threads; #if ERTS_POLL_USE_FALLBACK + flbk_pollset = erts_poll_create_pollset_flbk(j--); no_poll_threads++; #endif @@ -2064,7 +2203,15 @@ erts_init_check_io(int *argc, char **argv) psiv[0].pollres_len = ERTS_CHECK_IO_POLL_RES_LEN; psiv[0].pollres = erts_alloc(ERTS_ALC_T_POLLSET, sizeof(ErtsPollResFd) * ERTS_CHECK_IO_POLL_RES_LEN); - psiv[0].ps = get_fallback(); + psiv[0].ps = get_fallback_pollset(); + psiv++; +#endif + +#if ERTS_POLL_USE_SCHEDULER_POLLING + psiv[0].pollres_len = ERTS_CHECK_IO_POLL_RES_LEN; + psiv[0].pollres = erts_alloc(ERTS_ALC_T_POLLSET, + sizeof(ErtsPollResFd) * ERTS_CHECK_IO_POLL_RES_LEN); + psiv[0].ps = get_scheduler_pollset(0); psiv++; #endif @@ -2121,7 +2268,12 @@ erts_check_io_size(void) int i; #if ERTS_POLL_USE_FALLBACK - erts_poll_info(get_fallback(), &pi); + erts_poll_info(get_fallback_pollset(), &pi); + res += pi.memory_size; +#endif + +#if ERTS_POLL_USE_SCHEDULER_POLLING + erts_poll_info(get_scheduler_pollset(0), &pi); res += pi.memory_size; #endif @@ -2153,13 +2305,21 @@ erts_check_io_info(void *proc) Uint sz, *szp, *hp, **hpp; ErtsPollInfo *piv; Sint i, j = 0, len; - int no_pollsets = erts_no_pollsets + ERTS_POLL_USE_FALLBACK; + int no_pollsets = erts_no_pollsets + ERTS_POLL_USE_FALLBACK + ERTS_POLL_USE_SCHEDULER_POLLING; ERTS_CT_ASSERT(ERTS_POLL_USE_FALLBACK == 0 || ERTS_POLL_USE_FALLBACK == 1); + ERTS_CT_ASSERT(ERTS_POLL_USE_SCHEDULER_POLLING == 0 || ERTS_POLL_USE_SCHEDULER_POLLING == 1); piv = erts_alloc(ERTS_ALC_T_TMP, sizeof(ErtsPollInfo) * no_pollsets); #if ERTS_POLL_USE_FALLBACK - erts_poll_info_flbk(get_fallback(), &piv[0]); + erts_poll_info_flbk(get_fallback_pollset(), &piv[0]); + piv[0].poll_threads = 1; + piv[0].active_fds = 0; + piv++; +#endif + +#if ERTS_POLL_USE_SCHEDULER_POLLING + erts_poll_info(get_scheduler_pollset(0), &piv[0]); piv[0].poll_threads = 1; piv[0].active_fds = 0; piv++; @@ -2213,6 +2373,7 @@ erts_check_io_info(void *proc) sz = 0; piv -= ERTS_POLL_USE_FALLBACK; + piv -= ERTS_POLL_USE_SCHEDULER_POLLING; bld_it: @@ -2317,15 +2478,7 @@ print_events(erts_dsprintf_buf_t *dsbufp, ErtsPollEvents ev) static ERTS_INLINE void print_flags(erts_dsprintf_buf_t *dsbufp, EventStateFlags f) { - const char* delim = ""; - if(f & ERTS_EV_FLAG_USED) { - erts_dsprintf(dsbufp, "%s","USED"); - delim = "|"; - } - if(f & ERTS_EV_FLAG_FALLBACK) { - erts_dsprintf(dsbufp, "%s%s", delim, "FLBK"); - delim = "|"; - } + erts_dsprintf(dsbufp, "%s", flag2str(f)); } #ifdef DEBUG_PRINT_MODE @@ -2673,13 +2826,26 @@ erts_check_io_debug(ErtsCheckIoDebugInfo *ciodip) #if ERTS_POLL_USE_FALLBACK erts_dsprintf(dsbufp, "--- fds in flbk pollset ---------------------------------\n"); - erts_poll_get_selected_events_flbk(get_fallback(), counters.epep, + erts_poll_get_selected_events_flbk(get_fallback_pollset(), counters.epep, drv_ev_state.max_fds); for (fd = 0; fd < len; fd++) { if (drv_ev_state.v[fd].flags & ERTS_EV_FLAG_FALLBACK) doit_erts_check_io_debug(&drv_ev_state.v[fd], &counters, dsbufp); } #endif +#if ERTS_POLL_USE_SCHEDULER_POLLING + erts_dsprintf(dsbufp, "--- fds in scheduler pollset ----------------------------\n"); + erts_poll_get_selected_events(get_scheduler_pollset(0), counters.epep, + drv_ev_state.max_fds); + for (fd = 0; fd < len; fd++) { + if (drv_ev_state.v[fd].flags & ERTS_EV_FLAG_SCHEDULER) { + if (drv_ev_state.v[fd].events && drv_ev_state.v[fd].events != ERTS_POLL_EV_NONE) + counters.epep[fd] &= ~ERTS_POLL_EV_OUT; + doit_erts_check_io_debug(&drv_ev_state.v[fd], &counters, dsbufp); + } + } +#endif + erts_dsprintf(dsbufp, "--- fds in pollset --------------------------------------\n"); for (i = 0; i < erts_no_pollsets; i++) { @@ -2688,8 +2854,15 @@ erts_check_io_debug(ErtsCheckIoDebugInfo *ciodip) drv_ev_state.max_fds); for (fd = 0; fd < len; fd++) { if (!(drv_ev_state.v[fd].flags & ERTS_EV_FLAG_FALLBACK) - && get_pollset_id(fd) == i) + && get_pollset_id(fd) == i) { + if (counters.epep[fd] != ERTS_POLL_EV_NONE && + drv_ev_state.v[fd].flags & ERTS_EV_FLAG_IN_SCHEDULER) { + /* We add the in flag if it is enabled in the scheduler pollset + and get_selected_events works on the platform */ + counters.epep[fd] |= ERTS_POLL_EV_IN; + } doit_erts_check_io_debug(&drv_ev_state.v[fd], &counters, dsbufp); + } } } for (fd = len ; fd < drv_ev_state.max_fds; fd++) { @@ -2736,7 +2909,7 @@ void erts_lcnt_update_cio_locks(int enable) { #endif #if ERTS_POLL_USE_FALLBACK - erts_lcnt_enable_pollset_lock_count_flbk(get_fallback(), enable); + erts_lcnt_enable_pollset_lock_count_flbk(get_fallback_pollset(), enable); #endif for (i = 0; i < erts_no_pollsets; i++) diff --git a/erts/emulator/sys/common/erl_check_io.h b/erts/emulator/sys/common/erl_check_io.h index 443ef1264c..0f3fc4f7a2 100644 --- a/erts/emulator/sys/common/erl_check_io.h +++ b/erts/emulator/sys/common/erl_check_io.h @@ -68,7 +68,7 @@ int erts_check_io_max_files(void); * * @param pt the poll thread structure to use. */ -void erts_check_io(struct erts_poll_thread *pt); +void erts_check_io(struct erts_poll_thread *pt, ErtsMonotonicTime timeout_time); /** * Initialize the check io framework. This function will parse the arguments * and delete any entries that it is interested in. @@ -90,8 +90,11 @@ void erts_check_io_interrupt(struct erts_poll_thread *pt, int set); /** * Create a new poll thread structure that is associated with the number no. * It is the callers responsibility that no is unique. + * + * @param no the id of the pollset thread, -2 = aux thread, -1 = scheduler + * @param tpd the thread progress data of the pollset thread */ -struct erts_poll_thread* erts_create_pollset_thread(int no); +struct erts_poll_thread* erts_create_pollset_thread(int no, ErtsThrPrgrData *tpd); #ifdef ERTS_ENABLE_LOCK_COUNT /** * Toggle lock counting on all check io locks @@ -126,16 +129,6 @@ extern int erts_no_poll_threads; #include "erl_poll.h" #include "erl_port_task.h" -#ifdef __WIN32__ -/* - * Current erts_poll implementation for Windows cannot handle - * active events in the set of events polled. - */ -# define ERTS_CIO_DEFER_ACTIVE_EVENTS 1 -#else -# define ERTS_CIO_DEFER_ACTIVE_EVENTS 1 -#endif - typedef struct { Eterm inport; Eterm outport; @@ -145,12 +138,7 @@ typedef struct { struct erts_nif_select_event { Eterm pid; - Eterm immed; - Uint32 refn[ERTS_REF_NUMBERS]; - Sint32 ddeselect_cnt; /* 0: No delayed deselect in progress - * 1: Do deselect before next poll - * >1: Countdown of ignored events - */ + ErtsMessage *mp; }; typedef struct { diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 539daea419..e1ff0fe80a 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -176,4 +176,61 @@ void hard_dbg_remove_mseg(void* seg, UWord sz); #endif /* HAVE_ERTS_MMAP */ +/* Marks the given memory region as unused without freeing it, letting the OS + * reclaim its physical memory with the promise that we'll get it back (without + * its contents) the next time it's accessed. */ +ERTS_GLB_INLINE void erts_mem_discard(void *p, UWord size); + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +#ifdef VALGRIND + #include <valgrind/memcheck.h> + + ERTS_GLB_INLINE void erts_mem_discard(void *ptr, UWord size) { + VALGRIND_MAKE_MEM_UNDEFINED(ptr, size); + } +#elif defined(DEBUG) + /* Try to provoke crashes by filling the discard region with garbage. It's + * extremely hard to find bugs where we've discarded too much, as the + * region often retains its old contents if it's accessed before the OS + * reclaims it. */ + ERTS_GLB_INLINE void erts_mem_discard(void *ptr, UWord size) { + static const char pattern[] = "DISCARDED"; + char *data; + int i; + + for(i = 0, data = ptr; i < size; i++) { + data[i] = pattern[i % sizeof(pattern)]; + } + } +#elif defined(HAVE_SYS_MMAN_H) + #include <sys/mman.h> + + ERTS_GLB_INLINE void erts_mem_discard(void *ptr, UWord size) { + #ifdef MADV_FREE + /* This is preferred as it doesn't necessarily free the pages right + * away, which is a bit faster than MADV_DONTNEED. */ + madvise(ptr, size, MADV_FREE); + #else + madvise(ptr, size, MADV_DONTNEED); + #endif + } +#elif defined(_WIN32) + #include <winbase.h> + + /* MEM_RESET is defined on all supported versions of Windows, and has the + * same semantics as MADV_FREE. */ + ERTS_GLB_INLINE void erts_mem_discard(void *ptr, UWord size) { + VirtualAlloc(ptr, size, MEM_RESET, PAGE_READWRITE); + } +#else + /* Dummy implementation. */ + ERTS_GLB_INLINE void erts_mem_discard(void *ptr, UWord size) { + (void)ptr; + (void)size; + } +#endif + +#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ + #endif /* ERL_MMAP_H__ */ diff --git a/erts/emulator/sys/common/erl_poll.c b/erts/emulator/sys/common/erl_poll.c index b4d1575ee5..51d50933ff 100644 --- a/erts/emulator/sys/common/erl_poll.c +++ b/erts/emulator/sys/common/erl_poll.c @@ -75,6 +75,7 @@ # define WANT_NONBLOCKING #endif +#include "erl_thr_progress.h" #include "erl_poll.h" #if ERTS_POLL_USE_KQUEUE # include <sys/types.h> @@ -95,7 +96,6 @@ # include <limits.h> # endif #endif -#include "erl_thr_progress.h" #include "erl_driver.h" #include "erl_alloc.h" #include "erl_msacc.h" @@ -121,7 +121,8 @@ /* Define to print info about modifications done to each fd */ #define DEBUG_PRINT_FD(FMT, PS, FD, ...) DEBUG_PRINT("%d: " FMT, PS, FD, ##__VA_ARGS__) /* Define to print entry and exit from erts_poll_wait (can be very spammy) */ -//#define DEBUG_PRINT_WAIT(FMT, PS, ...) DEBUG_PRINT(FMT, PS, ##__VA_ARGS__) +// #define DEBUG_PRINT_WAIT(FMT, PS, ...) DEBUG_PRINT(FMT, PS, ##__VA_ARGS__) +// #define DEBUG_PRINT_WAIT(FMT, PS, ...) do { if ((PS)->id != -1) DEBUG_PRINT(FMT, PS, ##__VA_ARGS__); } while(0) #else #define ERTS_POLL_DEBUG_PRINT 0 @@ -200,7 +201,7 @@ int ERTS_SELECT(int nfds, ERTS_fd_set *readfds, ERTS_fd_set *writefds, #define ERTS_POLL_USE_CONCURRENT_UPDATE (ERTS_POLL_USE_EPOLL || ERTS_POLL_USE_KQUEUE) -#define ERTS_POLL_USE_WAKEUP_PIPE (!ERTS_POLL_USE_CONCURRENT_UPDATE) +#define ERTS_POLL_USE_WAKEUP(ps) (!ERTS_POLL_USE_CONCURRENT_UPDATE || (ps)->id < 0) #if !ERTS_POLL_USE_CONCURRENT_UPDATE @@ -269,6 +270,7 @@ struct ERTS_POLL_EXPORT(erts_pollset) { #if ERTS_POLL_USE_KERNEL_POLL int kp_fd; + int oneshot; #endif /* ERTS_POLL_USE_KERNEL_POLL */ #if ERTS_POLL_USE_POLL @@ -295,12 +297,16 @@ struct ERTS_POLL_EXPORT(erts_pollset) { ErtsPollSetUpdateRequestsBlock *curr_upd_req_block; erts_atomic32_t have_update_requests; erts_mtx_t mtx; - erts_atomic32_t wakeup_state; +#else + int do_wakeup; #endif -#if ERTS_POLL_USE_WAKEUP_PIPE - int wake_fds[2]; +#if ERTS_POLL_USE_TIMERFD + int timer_fd; #endif + ErtsMonotonicTime timeout_time; + erts_atomic32_t wakeup_state; + int wake_fds[2]; }; void erts_silence_warn_unused_result(long unused); @@ -365,63 +371,47 @@ static void print_misc_debug_info(void); uint32_t epoll_events(int kp_fd, int fd); #endif - #define ERTS_POLL_NOT_WOKEN 0 #define ERTS_POLL_WOKEN -1 #define ERTS_POLL_WOKEN_INTR 1 -#if !ERTS_POLL_USE_CONCURRENT_UPDATE static ERTS_INLINE void reset_wakeup_state(ErtsPollSet *ps) { erts_atomic32_set_mb(&ps->wakeup_state, ERTS_POLL_NOT_WOKEN); } -#endif static ERTS_INLINE int is_woken(ErtsPollSet *ps) { -#if !ERTS_POLL_USE_CONCURRENT_UPDATE return erts_atomic32_read_acqb(&ps->wakeup_state) != ERTS_POLL_NOT_WOKEN; -#else - return 0; -#endif } static ERTS_INLINE int is_interrupted_reset(ErtsPollSet *ps) { -#if !ERTS_POLL_USE_CONCURRENT_UPDATE return (erts_atomic32_xchg_acqb(&ps->wakeup_state, ERTS_POLL_NOT_WOKEN) == ERTS_POLL_WOKEN_INTR); -#else - return 0; -#endif } static ERTS_INLINE void woke_up(ErtsPollSet *ps) { -#if !ERTS_POLL_USE_CONCURRENT_UPDATE erts_aint32_t wakeup_state = erts_atomic32_read_acqb(&ps->wakeup_state); if (wakeup_state == ERTS_POLL_NOT_WOKEN) (void) erts_atomic32_cmpxchg_nob(&ps->wakeup_state, ERTS_POLL_WOKEN, ERTS_POLL_NOT_WOKEN); ASSERT(erts_atomic32_read_nob(&ps->wakeup_state) != ERTS_POLL_NOT_WOKEN); -#endif } /* * --- Wakeup pipe ----------------------------------------------------------- */ -#if ERTS_POLL_USE_WAKEUP_PIPE - static ERTS_INLINE void wake_poller(ErtsPollSet *ps, int interrupted) { -#if !ERTS_POLL_USE_CONCURRENT_UPDATE int wake; erts_aint32_t wakeup_state; if (!interrupted) @@ -434,9 +424,9 @@ wake_poller(ErtsPollSet *ps, int interrupted) wake = wakeup_state == ERTS_POLL_NOT_WOKEN; if (wake) -#endif { ssize_t res; + DEBUG_PRINT_WAIT("wake_poller(%d)", ps, interrupted); if (ps->wake_fds[1] < 0) return; /* Not initialized yet */ do { @@ -474,10 +464,8 @@ cleanup_wakeup_pipe(ErtsPollSet *ps) fd, erl_errno_id(errno), errno); } -#if !ERTS_POLL_USE_CONCURRENT_UPDATE if (intr) erts_atomic32_set_nob(&ps->wakeup_state, ERTS_POLL_WOKEN_INTR); -#endif } static void @@ -513,7 +501,67 @@ create_wakeup_pipe(ErtsPollSet *ps) ps->wake_fds[1] = wake_fds[1]; } +/* + * --- timer fd ----------------------------------------------------------- + */ + +#if ERTS_POLL_USE_TIMERFD + +/* We use the timerfd when using epoll_wait to get high accuracy + timeouts, i.e. we want to sleep with < ms accuracy. */ + +static void +create_timerfd(ErtsPollSet *ps) +{ + int do_wake = 0; + int timer_fd = timerfd_create(CLOCK_MONOTONIC,0); + ERTS_POLL_EXPORT(erts_poll_control)(ps, + timer_fd, + ERTS_POLL_OP_ADD, + ERTS_POLL_EV_IN, + &do_wake); + if (ps->internal_fd_limit <= timer_fd) + ps->internal_fd_limit = timer_fd + 1; + ps->timer_fd = timer_fd; +} + +static ERTS_INLINE void +timerfd_set(ErtsPollSet *ps, struct itimerspec *its) +{ +#ifdef DEBUG + struct itimerspec old_its; + int res; + res = timerfd_settime(ps->timer_fd, 0, its, &old_its); + ASSERT(res == 0); + ASSERT(old_its.it_interval.tv_sec == 0 && + old_its.it_interval.tv_nsec == 0 && + old_its.it_value.tv_sec == 0 && + old_its.it_value.tv_nsec == 0); + +#else + timerfd_settime(ps->timer_fd, 0, its, NULL); #endif +} + +static ERTS_INLINE int +timerfd_clear(ErtsPollSet *ps, ErtsPollResFd pr[], int res, int max_res) { + + struct itimerspec its; + /* we always have to clear the timer */ + its.it_interval.tv_sec = 0; + its.it_interval.tv_nsec = 0; + its.it_value.tv_sec = 0; + its.it_value.tv_nsec = 0; + timerfd_settime(ps->timer_fd, 0, &its, NULL); + + /* only timeout fd triggered */ + if (res == 1 && pr[0].data.fd == ps->timer_fd) + return 0; + + return res; +} + +#endif /* ERTS_POLL_USE_TIMERFD */ /* * --- Poll set update requests ---------------------------------------------- @@ -691,9 +739,12 @@ update_pollset(ErtsPollSet *ps, int fd, ErtsPollOp op, ErtsPollEvents events) struct epoll_event epe_templ; struct epoll_event epe; - epe_templ.events = ERTS_POLL_EV_E2N(events) | EPOLLONESHOT; + epe_templ.events = ERTS_POLL_EV_E2N(events); epe_templ.data.fd = fd; + if (ps->oneshot) + epe_templ.events |= EPOLLONESHOT; + #ifdef VALGRIND /* Silence invalid valgrind warning ... */ memset((void *) &epe.data, 0, sizeof(epoll_data_t)); @@ -802,6 +853,7 @@ update_pollset(ErtsPollSet *ps, int fd, ErtsPollOp op, ErtsPollEvents events) int res = 0, len = 0; struct kevent evts[2]; struct timespec ts = {0, 0}; + uint32_t oneshot = 0; if (op == ERTS_POLL_OP_ADD) { /* This is a hack to make the "noshell" option work; kqueue can poll @@ -840,6 +892,9 @@ update_pollset(ErtsPollSet *ps, int fd, ErtsPollOp op, ErtsPollEvents events) man page), but it seems to be the way it works... */ + if (ps->oneshot) + oneshot = EV_DISPATCH; + if (op == ERTS_POLL_OP_DEL) { erts_atomic_dec_nob(&ps->no_of_user_fds); /* We could probably skip this delete, do we want to? */ @@ -849,27 +904,29 @@ update_pollset(ErtsPollSet *ps, int fd, ErtsPollOp op, ErtsPollEvents events) uint32_t flags; erts_atomic_inc_nob(&ps->no_of_user_fds); - flags = EV_ADD|EV_DISPATCH; + flags = EV_ADD|oneshot; flags |= ((events & ERTS_POLL_EV_IN) ? 0 : EV_DISABLE); ERTS_EV_SET(&evts[len++], fd, EVFILT_READ, flags, (void *) ERTS_POLL_EV_IN); - flags = EV_ADD|EV_DISPATCH; + flags = EV_ADD|oneshot; flags |= ((events & ERTS_POLL_EV_OUT) ? 0 : EV_DISABLE); ERTS_EV_SET(&evts[len++], fd, EVFILT_WRITE, flags, (void *) ERTS_POLL_EV_OUT); } else { uint32_t flags; ASSERT(op == ERTS_POLL_OP_MOD); - flags = EV_DISPATCH; + flags = oneshot; flags |= (events & ERTS_POLL_EV_IN) ? EV_ENABLE : EV_DISABLE; ERTS_EV_SET(&evts[len++], fd, EVFILT_READ, flags, (void *) ERTS_POLL_EV_IN); - flags = EV_DISPATCH; + flags = oneshot; flags |= (events & ERTS_POLL_EV_OUT) ? EV_ENABLE : EV_DISABLE; ERTS_EV_SET(&evts[len++], fd, EVFILT_WRITE, flags, (void *) ERTS_POLL_EV_OUT); } #else - uint32_t flags = EV_ADD|EV_ONESHOT; + uint32_t flags = EV_ADD; + + if (ps->oneshot) flags |= EV_ONESHOT; if (op == ERTS_POLL_OP_DEL) { erts_atomic_dec_nob(&ps->no_of_user_fds); @@ -903,14 +960,17 @@ update_pollset(ErtsPollSet *ps, int fd, ErtsPollOp op, ErtsPollEvents events) keventbp += sprintf(keventbp, "kevent(%d, {",ps->kp_fd); for (i = 0; i < len; i++) { const char *flags = "UNKNOWN"; - if (evts[i].flags == EV_DELETE) flags = "EV_DELETE"; + if (evts[i].flags == (EV_DELETE)) flags = "EV_DELETE"; if (evts[i].flags == (EV_ADD|EV_ONESHOT)) flags = "EV_ADD|EV_ONESHOT"; + if (evts[i].flags == (EV_ADD)) flags = "EV_ADD"; #ifdef EV_DISPATCH if (evts[i].flags == (EV_ADD|EV_DISPATCH)) flags = "EV_ADD|EV_DISPATCH"; if (evts[i].flags == (EV_ADD|EV_DISABLE)) flags = "EV_ADD|EV_DISABLE"; if (evts[i].flags == (EV_ENABLE|EV_DISPATCH)) flags = "EV_ENABLE|EV_DISPATCH"; - if (evts[i].flags == EV_DISABLE) flags = "EV_DISABLE"; + if (evts[i].flags == (EV_ENABLE)) flags = "EV_ENABLE"; + if (evts[i].flags == (EV_DISABLE)) flags = "EV_DISABLE"; if (evts[i].flags == (EV_DISABLE|EV_DISPATCH)) flags = "EV_DISABLE|EV_DISABLE"; + if (evts[i].flags == (EV_DISABLE)) flags = "EV_DISABLE"; #endif keventbp += sprintf(keventbp, "%s{%lu, %s, %s}",i > 0 ? ", " : "", @@ -1273,11 +1333,15 @@ poll_control(ErtsPollSet *ps, int fd, ErtsPollOp op, goto done; } #endif -#if ERTS_POLL_USE_WAKEUP_PIPE if (fd == ps->wake_fds[0] || fd == ps->wake_fds[1]) { new_events = ERTS_POLL_EV_NVAL; goto done; } +#if ERTS_POLL_USE_TIMERFD + if (fd == ps->timer_fd) { + new_events = ERTS_POLL_EV_NVAL; + goto done; + } #endif } @@ -1333,11 +1397,8 @@ ERTS_POLL_EXPORT(erts_poll_control)(ErtsPollSet *ps, ERTS_POLLSET_UNLOCK(ps); -#if !ERTS_POLL_USE_CONCURRENT_UPDATE - if (*do_wake) { + if (*do_wake) wake_poller(ps, 0); - } -#endif return res; } @@ -1351,52 +1412,61 @@ ERTS_POLL_EXPORT(erts_poll_control)(ErtsPollSet *ps, static ERTS_INLINE int ERTS_POLL_EXPORT(save_result)(ErtsPollSet *ps, ErtsPollResFd pr[], int max_res, int chk_fds_res, int ebadf) { -#if !ERTS_POLL_USE_CONCURRENT_UPDATE || ERTS_POLL_DEBUG_PRINT || ERTS_POLL_USE_WAKEUP_PIPE int n = chk_fds_res < max_res ? chk_fds_res : max_res, i; int res = n; -#if ERTS_POLL_USE_WAKEUP_PIPE int wake_fd = ps->wake_fds[0]; -#endif - for (i = 0; i < n; i++) { - int fd = ERTS_POLL_RES_GET_FD(&pr[i]); -#ifdef DEBUG_PRINT_MODE - ErtsPollEvents evts = ERTS_POLL_RES_GET_EVTS(pr+i); -#endif + if (ERTS_POLL_USE_WAKEUP(ps) || ERTS_POLL_DEBUG_PRINT || ERTS_POLL_USE_TIMERFD) { + + for (i = 0; i < n; i++) { + int fd = ERTS_POLL_RES_GET_FD(&pr[i]); +#if ERTS_POLL_DEBUG_PRINT + ErtsPollEvents evts = ERTS_POLL_RES_GET_EVTS(pr+i); - DEBUG_PRINT_FD("trig %s (%s)", ps, fd, - ev2str(evts), + if (fd != wake_fd +#if ERTS_POLL_USE_TIMERFD + && fd != ps->timer_fd +#endif + ) + DEBUG_PRINT_FD("trig %s (%s)", ps, fd, + ev2str(evts), #if ERTS_POLL_USE_KQUEUE - "kqueue" + "kqueue" #elif ERTS_POLL_USE_EPOLL - "epoll" + "epoll" #else - "/dev/poll" + "/dev/poll" +#endif + ); #endif - ); -#if ERTS_POLL_USE_WAKEUP_PIPE - if (fd == wake_fd) { - cleanup_wakeup_pipe(ps); - ERTS_POLL_RES_SET_EVTS(&pr[i], ERTS_POLL_EV_NONE); - if (n == 1) - return 0; - } + if (ERTS_POLL_USE_WAKEUP(ps) && fd == wake_fd) { + cleanup_wakeup_pipe(ps); + ERTS_POLL_RES_SET_FD(&pr[i], -1); + ERTS_POLL_RES_SET_EVTS(&pr[i], ERTS_POLL_EV_NONE); + res--; + } +#if ERTS_POLL_USE_TIMERFD + else if (fd == ps->timer_fd) { + ERTS_POLL_RES_SET_FD(&pr[i], -1); + ERTS_POLL_RES_SET_EVTS(&pr[i], ERTS_POLL_EV_NONE); + res--; + } #endif #if !ERTS_POLL_USE_CONCURRENT_UPDATE - else { - /* Reset the events to emulate ONESHOT semantics */ - ps->fds_status[fd].events = 0; - enqueue_update_request(ps, fd); - } + else { + /* Reset the events to emulate ONESHOT semantics */ + ps->fds_status[fd].events = 0; + enqueue_update_request(ps, fd); + } #endif + } } - return res; -#else - ASSERT(chk_fds_res <= max_res); - return chk_fds_res; -#endif + if (res == 0) + return res; + else + return n; } #else /* !ERTS_POLL_USE_KERNEL_POLL */ @@ -1577,19 +1647,168 @@ ERTS_POLL_EXPORT(save_result)(ErtsPollSet *ps, ErtsPollResFd pr[], int max_res, #endif /* !ERTS_POLL_USE_KERNEL_POLL */ +static ERTS_INLINE ErtsMonotonicTime +get_timeout(ErtsPollSet *ps, + int resolution, + ErtsMonotonicTime timeout_time) +{ + ErtsMonotonicTime timeout; + + if (timeout_time == ERTS_POLL_NO_TIMEOUT) { + timeout = 0; + } + else if (timeout_time == ERTS_POLL_INF_TIMEOUT) { + timeout = -1; + } + else { + ErtsMonotonicTime diff_time, current_time; + current_time = erts_get_monotonic_time(NULL); + diff_time = timeout_time - current_time; + if (diff_time <= 0) { + timeout = 0; + } + else { + switch (resolution) { + case 1000: + /* Round up to nearest even milli second */ + timeout = ERTS_MONOTONIC_TO_MSEC(diff_time - 1) + 1; + if (timeout > (ErtsMonotonicTime) INT_MAX) + timeout = (ErtsMonotonicTime) INT_MAX; + timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000); + break; + case 1000000: + /* Round up to nearest even micro second */ + timeout = ERTS_MONOTONIC_TO_USEC(diff_time - 1) + 1; + timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000*1000); + break; + case 1000000000: + /* Round up to nearest even nano second */ + timeout = ERTS_MONOTONIC_TO_NSEC(diff_time - 1) + 1; + timeout -= ERTS_PREMATURE_TIMEOUT(timeout, 1000*1000*1000); + break; + default: + ERTS_INTERNAL_ERROR("Invalid resolution"); + timeout = 0; + break; + } + } + } + return timeout; +} + +#if ERTS_POLL_USE_SELECT + +static ERTS_INLINE int +get_timeout_timeval(ErtsPollSet *ps, + SysTimeval *tvp, + ErtsMonotonicTime timeout_time) +{ + ErtsMonotonicTime timeout = get_timeout(ps, + 1000*1000, + timeout_time); + + if (!timeout) { + tvp->tv_sec = 0; + tvp->tv_usec = 0; + + return 0; + } + else if (timeout == -1) { + return -1; + } + else { + ErtsMonotonicTime sec = timeout/(1000*1000); + tvp->tv_sec = sec; + tvp->tv_usec = timeout - sec*(1000*1000); + + ASSERT(tvp->tv_sec >= 0); + ASSERT(tvp->tv_usec >= 0); + ASSERT(tvp->tv_usec < 1000*1000); + + return 1; + } + +} + +#endif + +#if ERTS_POLL_USE_KQUEUE || (ERTS_POLL_USE_POLL && defined(HAVE_PPOLL)) || ERTS_POLL_USE_TIMERFD + +static ERTS_INLINE int +get_timeout_timespec(ErtsPollSet *ps, + struct timespec *tsp, + ErtsMonotonicTime timeout_time) +{ + ErtsMonotonicTime timeout = get_timeout(ps, + 1000*1000*1000, + timeout_time); + + if (!timeout) { + tsp->tv_sec = 0; + tsp->tv_nsec = 0; + return 0; + } + else if (timeout == -1) { + return -1; + } + else { + ErtsMonotonicTime sec = timeout/(1000*1000*1000); + tsp->tv_sec = sec; + tsp->tv_nsec = timeout - sec*(1000*1000*1000); + + ASSERT(tsp->tv_sec >= 0); + ASSERT(tsp->tv_nsec >= 0); + ASSERT(tsp->tv_nsec < 1000*1000*1000); + + return 1; + } +} + +#endif + +#if ERTS_POLL_USE_TIMERFD + +static ERTS_INLINE int +get_timeout_itimerspec(ErtsPollSet *ps, + struct itimerspec *itsp, + ErtsMonotonicTime timeout_time) +{ + + itsp->it_interval.tv_sec = 0; + itsp->it_interval.tv_nsec = 0; + + return get_timeout_timespec(ps, &itsp->it_value, timeout_time); +} + +#endif + static ERTS_INLINE int -check_fd_events(ErtsPollSet *ps, ErtsPollResFd pr[], int do_wait, int max_res) +check_fd_events(ErtsPollSet *ps, ErtsPollResFd pr[], int max_res, ErtsMonotonicTime timeout_time) { int res; - int timeout = do_wait ? -1 : 0; - DEBUG_PRINT_WAIT("Entering check_fd_events(), do_wait=%d", ps, do_wait); + int timeout; + DEBUG_PRINT_WAIT("Entering check_fd_events(), timeout=%d", ps, timeout_time); { #if ERTS_POLL_USE_EPOLL /* --- epoll ------------------------------- */ +#if ERTS_POLL_USE_TIMERFD + struct itimerspec its; + timeout = get_timeout_itimerspec(ps, &its, timeout_time); + if (timeout > 0) { + timerfd_set(ps, &its); + res = epoll_wait(ps->kp_fd, pr, max_res, -1); + res = timerfd_clear(ps, pr, res, max_res); + } else { + res = epoll_wait(ps->kp_fd, pr, max_res, timeout); + } +#else /* !ERTS_POLL_USE_TIMERFD */ + timeout = (int) get_timeout(ps, 1000, timeout_time); res = epoll_wait(ps->kp_fd, pr, max_res, timeout); - +#endif /* !ERTS_POLL_USE_TIMERFD */ #elif ERTS_POLL_USE_KQUEUE /* --- kqueue ------------------------------ */ - struct timespec ts = {0, 0}; - struct timespec *tsp = timeout ? NULL : &ts; + struct timespec ts; + struct timespec *tsp; + timeout = get_timeout_timespec(ps, &ts, timeout_time); + tsp = timeout < 0 ? NULL : &ts; res = kevent(ps->kp_fd, NULL, 0, pr, max_res, tsp); #elif ERTS_POLL_USE_DEVPOLL /* --- devpoll ----------------------------- */ /* @@ -1601,16 +1820,22 @@ check_fd_events(ErtsPollSet *ps, ErtsPollResFd pr[], int do_wait, int max_res) int nfds = (int) erts_atomic_read_nob(&ps->no_of_user_fds) + 1 /* wakeup pipe */; poll_res.dp_nfds = nfds < max_res ? nfds : max_res; poll_res.dp_fds = pr; - poll_res.dp_timeout = timeout; + poll_res.dp_timeout = (int) get_timeout(ps, 1000, timeout_time); res = ioctl(ps->kp_fd, DP_POLL, &poll_res); - +#elif ERTS_POLL_USE_POLL && defined(HAVE_PPOLL) /* --- ppoll ---------------- */ + struct timespec ts; + struct timespec *tsp = &ts; + timeout = get_timeout_timespec(ps, &ts, timeout_time); + if (timeout < 0) tsp = NULL; + res = ppoll(ps->poll_fds, ps->no_poll_fds, tsp, NULL); #elif ERTS_POLL_USE_POLL /* --- poll --------------------------------- */ - + timeout = (int) get_timeout(ps, 1000, timeout_time); res = poll(ps->poll_fds, ps->no_poll_fds, timeout); - #elif ERTS_POLL_USE_SELECT /* --- select ------------------------------ */ - SysTimeval tv = {0, 0}; - SysTimeval *tvp = timeout ? NULL : &tv; + SysTimeval tv; + SysTimeval *tvp; + timeout = get_timeout_timeval(ps, &tv, timeout_time); + tvp = timeout < 0 ? NULL : &tv; ERTS_FD_COPY(&ps->input_fds, &ps->res_input_fds); ERTS_FD_COPY(&ps->output_fds, &ps->res_output_fds); @@ -1629,7 +1854,9 @@ check_fd_events(ErtsPollSet *ps, ErtsPollResFd pr[], int do_wait, int max_res) int ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet *ps, ErtsPollResFd pr[], - int *len) + int *len, + ErtsThrPrgrData *tpd, + ErtsMonotonicTime timeout_time) { int res, no_fds, used_fds = 0; int ebadf = 0; @@ -1654,61 +1881,65 @@ ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet *ps, } #endif - do_wait = !is_woken(ps) && used_fds == 0; + do_wait = !is_woken(ps) && used_fds == 0 && timeout_time != ERTS_POLL_NO_TIMEOUT; DEBUG_PRINT_WAIT("Entering %s(), do_wait=%d", ps, __FUNCTION__, do_wait); if (do_wait) { - erts_thr_progress_prepare_wait(NULL); + tpd = tpd ? tpd : erts_thr_prgr_data(NULL); + erts_thr_progress_prepare_wait(tpd); ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_SLEEP); - } + } else + timeout_time = ERTS_POLL_NO_TIMEOUT; while (1) { - res = check_fd_events(ps, pr + used_fds, do_wait, no_fds - used_fds); + res = check_fd_events(ps, pr + used_fds, no_fds - used_fds, timeout_time); + if (res != 0) + break; + if (timeout_time == ERTS_POLL_NO_TIMEOUT) + break; + if (erts_get_monotonic_time(NULL) >= timeout_time) + break; + } #if !ERTS_POLL_USE_CONCURRENT_UPDATE - if (res < 0 - && errno == EBADF - && ERTS_POLLSET_HAVE_UPDATE_REQUESTS(ps)) { - /* - * This may have happened because another thread deselected - * a fd in our poll set and then closed it, i.e. the driver - * behaved correctly. We wan't to avoid looking for a bad - * fd, that may even not exist anymore. Therefore, handle - * update requests and try again. This behaviour should only - * happen when using SELECT as the polling mechanism. - */ - ERTS_POLLSET_LOCK(ps); - used_fds += handle_update_requests(ps, pr + used_fds, no_fds - used_fds); - if (used_fds == no_fds) { - *len = used_fds; - ERTS_POLLSET_UNLOCK(ps); - return 0; - } - res = check_fd_events(ps, pr + used_fds, 0, no_fds - used_fds); - /* Keep the lock over the non-blocking poll in order to not - get any nasty races happening. */ + if (res < 0 + && errno == EBADF + && ERTS_POLLSET_HAVE_UPDATE_REQUESTS(ps)) { + /* + * This may have happened because another thread deselected + * a fd in our poll set and then closed it, i.e. the driver + * behaved correctly. We wan't to avoid looking for a bad + * fd, that may even not exist anymore. Therefore, handle + * update requests and try again. This behaviour should only + * happen when using SELECT as the polling mechanism. + */ + ERTS_POLLSET_LOCK(ps); + used_fds += handle_update_requests(ps, pr + used_fds, no_fds - used_fds); + if (used_fds == no_fds) { + *len = used_fds; ERTS_POLLSET_UNLOCK(ps); - if (res == 0) { - errno = EAGAIN; - res = -1; - } + return 0; + } + res = check_fd_events(ps, pr + used_fds, no_fds - used_fds, ERTS_POLL_NO_TIMEOUT); + /* Keep the lock over the non-blocking poll in order to not + get any nasty races happening. */ + ERTS_POLLSET_UNLOCK(ps); + if (res == 0) { + errno = EAGAIN; + res = -1; } -#endif - - if (res != 0) - break; - if (!do_wait) - break; } +#endif if (do_wait) { - erts_thr_progress_finalize_wait(NULL); + erts_thr_progress_finalize_wait(tpd); ERTS_MSACC_UPDATE_CACHE(); ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_CHECK_IO); } - woke_up(ps); + if (ERTS_POLL_USE_WAKEUP(ps)) + woke_up(ps); if (res < 0) { #if ERTS_POLL_USE_SELECT @@ -1719,11 +1950,16 @@ ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet *ps, #endif res = errno; } - else { + else if (res == 0) { + res = used_fds == 0 ? ETIMEDOUT : 0; +#ifdef HARD_DEBUG + check_poll_result(pr, used_fds); +#endif + *len = used_fds; + } else { #if ERTS_POLL_USE_SELECT save_results: #endif - ps_locked = 1; ERTS_POLLSET_LOCK(ps); @@ -1753,12 +1989,13 @@ ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet *ps, void ERTS_POLL_EXPORT(erts_poll_interrupt)(ErtsPollSet *ps, int set) { -#if !ERTS_POLL_USE_CONCURRENT_UPDATE - if (!set) - reset_wakeup_state(ps); - else - wake_poller(ps, 1); -#endif + DEBUG_PRINT_WAIT("poll_interrupt(%d)", ps, set); + if (ERTS_POLL_USE_WAKEUP(ps)) { + if (!set) + reset_wakeup_state(ps); + else + wake_poller(ps, 1); + } } int @@ -1874,10 +2111,20 @@ ERTS_POLL_EXPORT(erts_poll_create_pollset)(int id) if (ps->internal_fd_limit <= kp_fd) ps->internal_fd_limit = kp_fd + 1; ps->kp_fd = kp_fd; + if (ps->id == -1) + ps->oneshot = 0; + else + ps->oneshot = 1; #endif -#if !ERTS_POLL_USE_CONCURRENT_UPDATE + erts_atomic32_init_nob(&ps->wakeup_state, (erts_aint32_t) 0); create_wakeup_pipe(ps); + +#if ERTS_POLL_USE_TIMERFD + create_timerfd(ps); +#endif + +#if !ERTS_POLL_USE_CONCURRENT_UPDATE handle_update_requests(ps, NULL, 0); cleanup_wakeup_pipe(ps); #endif @@ -1992,9 +2239,7 @@ ERTS_POLL_EXPORT(erts_poll_info)(ErtsPollSet *ps, ErtsPollInfo *pip) pip->memory_size = size; pip->poll_set_size = (int) erts_atomic_read_nob(&ps->no_of_user_fds); -#if !ERTS_POLL_USE_CONCURRENT_UPDATE pip->poll_set_size++; /* Wakeup pipe */ -#endif pip->lazy_updates = #if !ERTS_POLL_USE_CONCURRENT_UPDATE @@ -2177,6 +2422,12 @@ ERTS_POLL_EXPORT(erts_poll_get_selected_events)(ErtsPollSet *ps, ASSERT(0); return; } + if (fd == ps->wake_fds[0] || fd == ps->wake_fds[1]) + continue; +#if ERTS_POLL_USE_TIMERFD + if (fd == ps->timer_fd) + continue; +#endif data &= 0xFFFFFFFF; ASSERT(fd == data); /* Events are the events that are being monitored, which of course include diff --git a/erts/emulator/sys/common/erl_poll.h b/erts/emulator/sys/common/erl_poll.h index e1cea7eb8b..d40dabc529 100644 --- a/erts/emulator/sys/common/erl_poll.h +++ b/erts/emulator/sys/common/erl_poll.h @@ -51,6 +51,7 @@ #include "sys.h" #define ERTS_POLL_NO_TIMEOUT ERTS_MONOTONIC_TIME_MIN +#define ERTS_POLL_INF_TIMEOUT ERTS_MONOTONIC_TIME_MAX #ifdef ERTS_ENABLE_KERNEL_POLL # undef ERTS_ENABLE_KERNEL_POLL @@ -130,6 +131,9 @@ #endif #define ERTS_POLL_USE_FALLBACK (ERTS_POLL_USE_KQUEUE || ERTS_POLL_USE_EPOLL) +#define ERTS_POLL_USE_SCHEDULER_POLLING (ERTS_POLL_USE_KQUEUE || ERTS_POLL_USE_EPOLL) +#define ERTS_POLL_SCHEDULER_POLLING_TIMEOUT 10 +#define ERTS_POLL_USE_TIMERFD 0 typedef Uint32 ErtsPollEvents; @@ -156,6 +160,14 @@ typedef enum { #include <sys/epoll.h> +#if ERTS_POLL_USE_EPOLL +#ifdef HAVE_SYS_TIMERFD_H +#include <sys/timerfd.h> +#undef ERTS_POLL_USE_TIMERFD +#define ERTS_POLL_USE_TIMERFD 1 +#endif +#endif + #define ERTS_POLL_EV_E2N(EV) \ ((uint32_t) (EV)) #define ERTS_POLL_EV_N2E(EV) \ @@ -276,7 +288,7 @@ typedef struct _ErtsPollResFd { #endif -#define ERTS_POLL_EV_NONE (UINT_MAX & ~(ERTS_POLL_EV_IN|ERTS_POLL_EV_OUT|ERTS_POLL_EV_NVAL|ERTS_POLL_EV_ERR)) +#define ERTS_POLL_EV_NONE ERTS_POLL_EV_N2E((UINT_MAX & ~(ERTS_POLL_EV_IN|ERTS_POLL_EV_OUT|ERTS_POLL_EV_NVAL|ERTS_POLL_EV_ERR))) #define ev2str(ev) \ (((ev) == 0 || (ev) == ERTS_POLL_EV_NONE) ? "NONE" : \ diff --git a/erts/emulator/sys/common/erl_poll_api.h b/erts/emulator/sys/common/erl_poll_api.h index 1170a549b9..f3a91e54f7 100644 --- a/erts/emulator/sys/common/erl_poll_api.h +++ b/erts/emulator/sys/common/erl_poll_api.h @@ -72,11 +72,15 @@ ErtsPollEvents ERTS_POLL_EXPORT(erts_poll_control)(ErtsPollSet *ps, * @param res an array of fd results that the ready fds are put in. * @param[in] length the length of the res array * @param[out] length the number of ready events returned in res + * @param tpd the thread progress data to note sleep state in + * @param timeout_time the time in native to wake up at * @return 0 on success, else the ERRNO of the error that happened. */ int ERTS_POLL_EXPORT(erts_poll_wait)(ErtsPollSet *ps, ErtsPollResFd res[], - int *length); + int *length, + ErtsThrPrgrData *tpd, + ErtsMonotonicTime timeout_time); /** * Interrupt the thread waiting in the pollset. This function should be called * with set = 0 before any thread calls erts_poll_wait in order to clear any diff --git a/erts/emulator/sys/win32/erl_poll.c b/erts/emulator/sys/win32/erl_poll.c index 39bb4d515e..3843a27a6e 100644 --- a/erts/emulator/sys/win32/erl_poll.c +++ b/erts/emulator/sys/win32/erl_poll.c @@ -1017,10 +1017,12 @@ ErtsPollEvents erts_poll_control(ErtsPollSet *ps, int erts_poll_wait(ErtsPollSet *ps, ErtsPollResFd pr[], - int *len) + int *len, + ErtsThrPrgrData *tpd, + Sint64 timeout_in) { int no_fds; - DWORD timeout = INFINITE; + DWORD timeout = timeout_in == -1 ? INFINITE : timeout_in; EventData* ev; int res = 0; int num = 0; @@ -1056,10 +1058,10 @@ int erts_poll_wait(ErtsPollSet *ps, HARDDEBUGF(("Start waiting %d [%d]",num_h, (int) timeout)); ERTS_POLLSET_UNLOCK(ps); - erts_thr_progress_prepare_wait(NULL); + erts_thr_progress_prepare_wait(tpd); ERTS_MSACC_SET_STATE_CACHED(ERTS_MSACC_STATE_SLEEP); handle = WaitForMultipleObjects(num_h, harr, FALSE, timeout); - erts_thr_progress_finalize_wait(NULL); + erts_thr_progress_finalize_wait(tpd); ERTS_MSACC_POP_STATE(); ERTS_POLLSET_LOCK(ps); HARDDEBUGF(("Stop waiting %d [%d]",num_h, (int) timeout)); diff --git a/erts/emulator/sys/win32/sys.c b/erts/emulator/sys/win32/sys.c index a1c630d68a..b95aadc9b2 100644 --- a/erts/emulator/sys/win32/sys.c +++ b/erts/emulator/sys/win32/sys.c @@ -186,7 +186,9 @@ void sys_primitive_init(HMODULE beam) UWord erts_sys_get_page_size(void) { - return (UWord) 4*1024; /* Guess 4 KB */ + SYSTEM_INFO info; + GetSystemInfo(&info); + return (UWord)info.dwPageSize; } Uint diff --git a/erts/emulator/test/atomics_SUITE.erl b/erts/emulator/test/atomics_SUITE.erl index 8c42354770..a5407c42ee 100644 --- a/erts/emulator/test/atomics_SUITE.erl +++ b/erts/emulator/test/atomics_SUITE.erl @@ -126,6 +126,9 @@ unsigned_limits(Config) when is_list(Config) -> Min = atomics:add_get(Ref, 1, 1), Max = atomics:sub_get(Ref, 1, 1), + atomics:put(Ref, 1, Max), + io:format("Max=~p~n", [atomics:get(Ref, 1)]), + {'EXIT',{badarg,_}} = (catch atomics:add(Ref, 1, Max+1)), IncrMin = -(1 bsl (Bits-1)), ok = atomics:put(Ref, 1, -IncrMin), diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 9e7bcd5255..3eedf2f6a6 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -37,7 +37,8 @@ group_leader_prio/1, group_leader_prio_dirty/1, is_process_alive/1, process_info_blast/1, - os_env_case_sensitivity/1]). + os_env_case_sensitivity/1, + test_length/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -52,7 +53,8 @@ all() -> erl_crash_dump_bytes, min_max, erlang_halt, is_builtin, error_stacktrace, error_stacktrace_during_call_trace, group_leader_prio, group_leader_prio_dirty, - is_process_alive, process_info_blast, os_env_case_sensitivity]. + is_process_alive, process_info_blast, os_env_case_sensitivity, + test_length]. %% Uses erlang:display to test that erts_printf does not do deep recursion display(Config) when is_list(Config) -> @@ -1181,7 +1183,53 @@ consume_msgs() -> after 0 -> ok end. - + +%% Test that length/1 returns the correct result after trapping, and +%% also that the argument is correct in the stacktrace for a badarg +%% exception. + +test_length(_Config) -> + {Start,Inc} = case test_server:timetrap_scale_factor() of + 1 -> {16*4000,3977}; + _ -> {100,1} + end, + Good = lists:reverse(lists:seq(1, Start)), + Bad = Good ++ [bad|cons], + test_length(Start, 10*Start, Inc, Good, Bad), + + %% Test that calling length/1 from a match spec works. + MsList = lists:seq(1, 2*Start), + MsInput = [{tag,Good},{tag,MsList}], + Ms0 = [{{tag,'$1'},[{'>',{length,'$1'},Start}],['$1']}], + Ms = ets:match_spec_compile(Ms0), + [MsList] = ets:match_spec_run(MsInput, Ms), + ok. + +test_length(I, N, Inc, Good, Bad) when I < N -> + Length = id(length), + I = length(Good), + I = erlang:Length(Good), + + %% Test length/1 in guards. + if + length(Good) =:= I -> + ok + end, + if + length(Bad) =:= I -> + error(should_fail); + true -> + ok + end, + + {'EXIT',{badarg,[{erlang,length,[[I|_]],_}|_]}} = (catch length(Bad)), + {'EXIT',{badarg,[{erlang,length,[[I|_]],_}|_]}} = (catch erlang:Length(Bad)), + IncSeq = lists:seq(I + 1, I + Inc), + test_length(I+Inc, N, Inc, + lists:reverse(IncSeq, Good), + lists:reverse(IncSeq, Bad)); +test_length(_, _, _, _, _) -> ok. + %% helpers id(I) -> I. diff --git a/erts/emulator/test/big_SUITE.erl b/erts/emulator/test/big_SUITE.erl index 0a42b09903..3b9b9e5989 100644 --- a/erts/emulator/test/big_SUITE.erl +++ b/erts/emulator/test/big_SUITE.erl @@ -24,7 +24,7 @@ -export([t_div/1, eq_28/1, eq_32/1, eq_big/1, eq_math/1, big_literals/1, borders/1, negative/1, big_float_1/1, big_float_2/1, - bxor_2pow/1, + bxor_2pow/1, band_2pow/1, shift_limit_1/1, powmod/1, system_limit/1, toobig/1, otp_6692/1]). %% Internal exports. @@ -43,7 +43,7 @@ suite() -> all() -> [t_div, eq_28, eq_32, eq_big, eq_math, big_literals, borders, negative, {group, big_float}, shift_limit_1, - bxor_2pow, + bxor_2pow, band_2pow, powmod, system_limit, toobig, otp_6692]. groups() -> @@ -168,7 +168,11 @@ eval({op,_,Op,A0,B0}, LFH) -> Res = eval_op(Op, A, B), erlang:garbage_collect(), Res; -eval({integer,_,I}, _) -> I; +eval({integer,_,I}, _) -> + %% "Parasitic" ("symbiotic"?) test of squaring all numbers + %% found in the test data. + test_squaring(I), + I; eval({call,_,{atom,_,Local},Args0}, LFH) -> Args = eval_list(Args0, LFH), LFH(Local, Args). @@ -192,6 +196,18 @@ eval_op('bxor', A, B) -> A bxor B; eval_op('bsl', A, B) -> A bsl B; eval_op('bsr', A, B) -> A bsr B. +test_squaring(I) -> + %% Multiplying an integer by itself is specially optimized, so we + %% should take special care to test squaring. The optimization + %% will kick in when the two operands have the same address. + Sqr = I * I, + + %% This expression will be multiplied in the usual way, because + %% the the two operands for '*' are stored at different addresses. + Sqr = I * ((I + id(1)) - id(1)), + + ok. + %% Built in test functions fac(0) -> 1; @@ -456,3 +472,38 @@ my_bxor(A, B, N, Acc0) -> false -> Acc0 bor (1 bsl N) end, my_bxor(A bsr 1, B bsr 1, N+1, Acc1). + + +%% ERL-804 +band_2pow(_Config) -> + IL = lists:seq(8*3, 8*16, 4), + JL = lists:seq(0, 64), + [band_2pow_1((1 bsl I), (1 bsl J)) + || I <- IL, J <- JL], + ok. + +band_2pow_1(A, B) -> + for(-1,1, fun(Ad) -> + for(-1,1, fun(Bd) -> + band_2pow_2(A+Ad, B+Bd), + band_2pow_2(-A+Ad, B+Bd), + band_2pow_2(A+Ad, -B+Bd), + band_2pow_2(-A+Ad, -B+Bd) + end) + end). + +band_2pow_2(A, B) -> + Correct = my_band(A, B), + case A band B of + Correct -> ok; + Wrong -> + io:format("~.16# band ~.16#\n", [A,B]), + io:format("Expected ~.16#\n", [Correct]), + io:format("Got ~.16#\n", [Wrong]), + ct:fail({failed, 'band'}) + + end. + +%% Implement band without band +my_band(A, B) -> + bnot ((bnot A) bor (bnot B)). diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 9ffb484eb4..94501dad84 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -1754,7 +1754,7 @@ smp_select0(Config) -> ProcFun = fun()-> io:format("Worker ~p starting\n",[self()]), Port = open_port({spawn, DrvName}, []), smp_select_loop(Port, 100000), - sleep(1000), % wait for driver to handle pending events + smp_select_done(Port), true = erlang:port_close(Port), Master ! {ok,self()}, io:format("Worker ~p finished\n",[self()]) @@ -1784,6 +1784,21 @@ smp_select_loop(Port, N) -> smp_select_loop(Port, N-1) end. +smp_select_done(Port) -> + case erlang:port_control(Port, ?CHKIO_SMP_SELECT, "done") of + "wait" -> + receive + {Port, done} -> + ok + after 10*1000 -> + %% Seems we have a lost ready_input event. + %% Go ahead anyway, port will crash VM when closed. + ok + end; + + "ok" -> ok + end. + smp_select_wait([], _) -> ok; smp_select_wait(Pids, TimeoutMsg) -> diff --git a/erts/emulator/test/driver_SUITE_data/chkio_drv.c b/erts/emulator/test/driver_SUITE_data/chkio_drv.c index ee8f28e8b1..b9ee155b4b 100644 --- a/erts/emulator/test/driver_SUITE_data/chkio_drv.c +++ b/erts/emulator/test/driver_SUITE_data/chkio_drv.c @@ -90,7 +90,7 @@ typedef struct chkio_smp_select { int next_read; int next_write; int first_write; - enum {Closed, Opened, Selected, Waiting} state; + enum {Closed, Opened, Selected, Waiting, WaitingDone} state; int wasSelected; unsigned rand_state; }ChkioSmpSelect; @@ -292,18 +292,20 @@ stop_steal_aux(ChkioDrvData *cddp) static void free_smp_select(ChkioSmpSelect* pip, ErlDrvPort port) { switch (pip->state) { + case WaitingDone: case Waiting: { int word; - fprintf(stderr, "Closing pipe in state Waiting. Event lost?\n"); + fprintf(stderr, "Closing pipe in state Waiting*. Event lost?\r\n"); for (;;) { int bytes = read(pip->read_fd, &word, sizeof(word)); if (bytes != sizeof(word)) { if (bytes != 0) { - fprintf(stderr, "Failed to read from pipe, bytes=%d, errno=%d\n", bytes, errno); + fprintf(stderr, "Failed to read from pipe, bytes=%d, errno=%d\r\n", + bytes, errno); } break; } - fprintf(stderr, "Read from pipe: %d\n", word); + fprintf(stderr, "Read from pipe: %d\r\n", word); } abort(); } @@ -318,6 +320,8 @@ static void free_smp_select(ChkioSmpSelect* pip, ErlDrvPort port) close(pip->write_fd); pip->state = Closed; break; + case Closed: + break; } driver_free(pip); } @@ -383,6 +387,9 @@ chkio_drv_start(ErlDrvPort port, char *command) cddp->id = driver_mk_port(port); cddp->test = CHKIO_STOP; cddp->test_data = NULL; + + drv_use_singleton.fd_stop_select = -2; /* disable stop_select asserts */ + return (ErlDrvData) cddp; #endif } @@ -526,7 +533,7 @@ chkio_drv_ready_input(ErlDrvData drv_data, ErlDrvEvent event) printf("Read event on uninitiated pipe %d\n", fd); abort(); } - if (pip->state != Selected && pip->state != Waiting) { + if (pip->state != Selected && pip->state != Waiting && pip->state != WaitingDone) { printf("Read event on pipe in strange state %d\n", pip->state); abort(); } @@ -536,9 +543,9 @@ chkio_drv_ready_input(ErlDrvData drv_data, ErlDrvEvent event) inPipe = (pip->next_write - pip->next_read); if (inPipe == 0) { bytes = read(pip->read_fd, &word, sizeof(word)); - printf("Unexpected empty pipe, expected %u -> %u, bytes=%d, word=%d, written=%d\n", - pip->next_read, pip->next_write-1, bytes, word, - (pip->next_write - pip->first_write)); + printf("Unexpected empty pipe: ptr=%p, fds=%d->%d, read bytes=%d, word=%d, written=%d\n", + pip, pip->write_fd, pip->read_fd, + bytes, word, (pip->next_write - pip->first_write)); /*abort(); Allow unexpected events as it's been seen to be triggered by epoll on Linux. Most of the time the unwanted events are filtered by @@ -564,7 +571,20 @@ chkio_drv_ready_input(ErlDrvData drv_data, ErlDrvEvent event) TRACEF(("Read %d from fd=%d\n", word, fd)); pip->next_read++; } - pip->state = Selected; /* not Waiting anymore */ + if (pip->state == WaitingDone) { + if (pip->next_write == pip->next_read) { + /* All data read, send {Port, done} */ + ErlDrvTermData spec[] = {ERL_DRV_PORT, driver_mk_port(cddp->port), + ERL_DRV_ATOM, driver_mk_atom("done"), + ERL_DRV_TUPLE, 2}; + erl_drv_output_term(driver_mk_port(cddp->port), + spec, sizeof(spec) / sizeof(spec[0])); + pip->state = Selected; + } + } + else { + pip->state = Selected; /* not Waiting anymore */ + } break; } case CHKIO_DRV_USE: @@ -962,6 +982,16 @@ chkio_drv_control(ErlDrvData drv_data, } case CHKIO_SMP_SELECT: { ChkioSmpSelect* pip = (ChkioSmpSelect*) cddp->test_data; + if (len == 4 && memcmp(buf, "done", 4) == 0) { + if (pip && pip->state == Waiting) { + pip->state = WaitingDone; + res_str = "wait"; + } + else + res_str = "ok"; + res_len = -1; + break; + } if (pip == NULL) { erl_drv_mutex_lock(smp_pipes_mtx); if (smp_pipes) { @@ -1014,7 +1044,6 @@ chkio_drv_control(ErlDrvData drv_data, if (pip->wasSelected && (op & 1)) { TRACEF(("%T: Close pipe [%d->%d]\n", cddp->id, pip->write_fd, pip->read_fd)); - drv_use_singleton.fd_stop_select = -2; /* disable stop_select asserts */ if (driver_select(cddp->port, (ErlDrvEvent)(ErlDrvSInt)pip->read_fd, DO_READ|ERL_DRV_USE, 0) || close(pip->write_fd)) { diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index edad62a9fb..0d0930e124 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -486,6 +486,7 @@ t_on_load(Config) when is_list(Config) -> -define(ERL_NIF_SELECT_WRITE, (1 bsl 1)). -define(ERL_NIF_SELECT_STOP, (1 bsl 2)). -define(ERL_NIF_SELECT_CANCEL, (1 bsl 3)). +-define(ERL_NIF_SELECT_CUSTOM_MSG, (1 bsl 4)). -define(ERL_NIF_SELECT_STOP_CALLED, (1 bsl 0)). -define(ERL_NIF_SELECT_STOP_SCHEDULED, (1 bsl 1)). @@ -496,100 +497,106 @@ t_on_load(Config) when is_list(Config) -> select(Config) when is_list(Config) -> ensure_lib_loaded(Config), + select_do(0, make_ref(), make_ref(), null), + + RefBin = list_to_binary(lists:duplicate(100, $x)), + [select_do(?ERL_NIF_SELECT_CUSTOM_MSG, + small, {a, tuple, with, "some", RefBin}, MSG_ENV) + || MSG_ENV <- [null, alloc_env]], + ok. + +select_do(Flag, Ref, Ref2, MSG_ENV) -> + io:format("select_do(~p, ~p, ~p)\n", [Ref, Ref2, MSG_ENV]), - Ref = make_ref(), - Ref2 = make_ref(), {{R, R_ptr}, {W, W_ptr}} = pipe_nif(), ok = write_nif(W, <<"hej">>), <<"hej">> = read_nif(R, 3), %% Wait for read eagain = read_nif(R, 3), - 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,null,Ref), + 0 = select_nif(R,?ERL_NIF_SELECT_READ bor Flag, R,null,Ref,MSG_ENV), [] = flush(0), ok = write_nif(W, <<"hej">>), - [{select, R, Ref, ready_input}] = flush(), - 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,self(),Ref2), - [{select, R, Ref2, ready_input}] = flush(), + receive_ready(R, Ref, ready_input), + 0 = select_nif(R,?ERL_NIF_SELECT_READ bor Flag,R,self(),Ref2,MSG_ENV), + receive_ready(R, Ref2, ready_input), Papa = self(), Pid = spawn_link(fun() -> - [{select, R, Ref, ready_input}] = flush(), + receive_ready(R, Ref, ready_input), Papa ! {self(), done} end), - 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,Pid,Ref), + 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, Pid, Ref,MSG_ENV), {Pid, done} = receive_any(1000), %% Cancel read - 0 = select_nif(R,?ERL_NIF_SELECT_READ bor ?ERL_NIF_SELECT_CANCEL,R,null,Ref), + 0 = select_nif(R,?ERL_NIF_SELECT_READ bor ?ERL_NIF_SELECT_CANCEL,R,null,Ref,null), <<"hej">> = read_nif(R, 3), - 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,null,Ref), + 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, null, Ref, MSG_ENV), ?ERL_NIF_SELECT_READ_CANCELLED = - select_nif(R,?ERL_NIF_SELECT_READ bor ?ERL_NIF_SELECT_CANCEL,R,null,Ref), + select_nif(R,?ERL_NIF_SELECT_READ bor ?ERL_NIF_SELECT_CANCEL,R,null,Ref,null), ok = write_nif(W, <<"hej again">>), [] = flush(0), <<"hej again">> = read_nif(R, 9), %% Wait for write Written = write_full(W, $a), - 0 = select_nif(W,?ERL_NIF_SELECT_WRITE,W,self(),Ref), + 0 = select_nif(W, ?ERL_NIF_SELECT_WRITE bor Flag, W, self(), Ref, MSG_ENV), [] = flush(0), Written = read_nif(R,byte_size(Written)), - [{select, W, Ref, ready_output}] = flush(), + receive_ready(W, Ref, ready_output), %% Cancel write - 0 = select_nif(W,?ERL_NIF_SELECT_WRITE bor ?ERL_NIF_SELECT_CANCEL,W,null,Ref), + 0 = select_nif(W, ?ERL_NIF_SELECT_WRITE bor ?ERL_NIF_SELECT_CANCEL, W, null, Ref, null), Written2 = write_full(W, $b), - 0 = select_nif(W,?ERL_NIF_SELECT_WRITE,W,null,Ref), + 0 = select_nif(W, ?ERL_NIF_SELECT_WRITE bor Flag, W, null, Ref, MSG_ENV), ?ERL_NIF_SELECT_WRITE_CANCELLED = - select_nif(W,?ERL_NIF_SELECT_WRITE bor ?ERL_NIF_SELECT_CANCEL,W,null,Ref), + select_nif(W, ?ERL_NIF_SELECT_WRITE bor ?ERL_NIF_SELECT_CANCEL, W, null, Ref, null), Written2 = read_nif(R,byte_size(Written2)), [] = flush(0), %% Close write and wait for EOF eagain = read_nif(R, 1), - check_stop_ret(select_nif(W,?ERL_NIF_SELECT_STOP,W,null,Ref)), + check_stop_ret(select_nif(W, ?ERL_NIF_SELECT_STOP, W, null, Ref, null)), [{fd_resource_stop, W_ptr, _}] = flush(), {1, {W_ptr,_}} = last_fd_stop_call(), true = is_closed_nif(W), [] = flush(0), - 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,self(),Ref), - [{select, R, Ref, ready_input}] = flush(), + 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, self(), Ref, MSG_ENV), + receive_ready(R, Ref, ready_input), eof = read_nif(R,1), - check_stop_ret(select_nif(R,?ERL_NIF_SELECT_STOP,R,null,Ref)), + check_stop_ret(select_nif(R, ?ERL_NIF_SELECT_STOP, R, null, Ref, null)), [{fd_resource_stop, R_ptr, _}] = flush(), {1, {R_ptr,_}} = last_fd_stop_call(), true = is_closed_nif(R), - select_2(Config). + select_2(Flag, Ref, Ref2, MSG_ENV). -select_2(Config) -> +select_2(Flag, Ref1, Ref2, MSG_ENV) -> erlang:garbage_collect(), {_,_,2} = last_resource_dtor_call(), - Ref1 = make_ref(), - Ref2 = make_ref(), {{R, R_ptr}, {W, W_ptr}} = pipe_nif(), %% Change ref eagain = read_nif(R, 1), - 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,null,Ref1), - 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,self(),Ref2), + 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, null, Ref1, MSG_ENV), + 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, self(), Ref2, MSG_ENV), [] = flush(0), ok = write_nif(W, <<"hej">>), - [{select, R, Ref2, ready_input}] = flush(), + receive_ready(R, Ref2, ready_input), <<"hej">> = read_nif(R, 3), %% Change pid eagain = read_nif(R, 1), - 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,null,Ref1), + 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, null, Ref1, MSG_ENV), Papa = self(), spawn_link(fun() -> - 0 = select_nif(R,?ERL_NIF_SELECT_READ,R,null,Ref1), + 0 = select_nif(R, ?ERL_NIF_SELECT_READ bor Flag, R, null, Ref1, MSG_ENV), [] = flush(0), Papa ! sync, - [{select, R, Ref1, ready_input}] = flush(), + receive_ready(R, Ref1, ready_input), <<"hej">> = read_nif(R, 3), Papa ! done end), @@ -598,24 +605,30 @@ select_2(Config) -> done = receive_any(), [] = flush(0), - check_stop_ret(select_nif(R,?ERL_NIF_SELECT_STOP,R,null,Ref1)), + check_stop_ret(select_nif(R,?ERL_NIF_SELECT_STOP,R,null,Ref1, null)), [{fd_resource_stop, R_ptr, _}] = flush(), {1, {R_ptr,_}} = last_fd_stop_call(), true = is_closed_nif(R), %% Stop without previous read/write select - ?ERL_NIF_SELECT_STOP_CALLED = select_nif(W,?ERL_NIF_SELECT_STOP,W,null,Ref1), + ?ERL_NIF_SELECT_STOP_CALLED = select_nif(W,?ERL_NIF_SELECT_STOP,W,null,Ref1,null), [{fd_resource_stop, W_ptr, 1}] = flush(), {1, {W_ptr,1}} = last_fd_stop_call(), true = is_closed_nif(W), - select_3(Config). + select_3(). -select_3(_Config) -> +select_3() -> erlang:garbage_collect(), {_,_,2} = last_resource_dtor_call(), ok. +receive_ready(R, Ref, IOatom) when is_reference(Ref) -> + [{select, R, Ref, IOatom}] = flush(); +receive_ready(_, Msg, _) -> + [Got] = flush(), + {true,_,_} = {Got=:=Msg, Got, Msg}. + %% @doc The stealing child process for the select_steal test. Duplicates given %% W/RFds and runs select on them to steal select_steal_child_process(Parent, RFd) -> @@ -624,7 +637,7 @@ select_steal_child_process(Parent, RFd) -> Ref2 = make_ref(), %% Try to select from the child pid (steal from parent) - ?assertEqual(0, select_nif(R2Fd, ?ERL_NIF_SELECT_READ, R2Fd, null, Ref2)), + ?assertEqual(0, select_nif(R2Fd, ?ERL_NIF_SELECT_READ, R2Fd, null, Ref2, null)), ?assertEqual([], flush(0)), ?assertEqual(eagain, read_nif(R2Fd, 1)), @@ -632,7 +645,7 @@ select_steal_child_process(Parent, RFd) -> Parent ! {self(), stage1}, % signal parent to send the <<"stolen1">> %% Receive <<"stolen1">> via enif_select - ?assertEqual(0, select_nif(R2Fd, ?ERL_NIF_SELECT_READ, R2Fd, null, Ref2)), + ?assertEqual(0, select_nif(R2Fd, ?ERL_NIF_SELECT_READ, R2Fd, null, Ref2, null)), ?assertMatch([{select, R2Fd, Ref2, ready_input}], flush()), ?assertEqual(<<"stolen1">>, read_nif(R2Fd, 7)), @@ -650,7 +663,7 @@ select_steal(Config) when is_list(Config) -> {{RFd, RPtr}, {WFd, WPtr}} = pipe_nif(), %% Bind the socket to current pid in enif_select - ?assertEqual(0, select_nif(RFd, ?ERL_NIF_SELECT_READ, RFd, null, Ref)), + ?assertEqual(0, select_nif(RFd, ?ERL_NIF_SELECT_READ, RFd, null, Ref, null)), ?assertEqual([], flush(0)), %% Spawn a process and do some stealing @@ -664,15 +677,15 @@ select_steal(Config) when is_list(Config) -> ?assertMatch([{Pid, done}], flush(1)), % synchronize with the child %% Try to select from the parent pid (steal back) - ?assertEqual(0, select_nif(RFd, ?ERL_NIF_SELECT_READ, RFd, Pid, Ref)), + ?assertEqual(0, select_nif(RFd, ?ERL_NIF_SELECT_READ, RFd, Pid, Ref, null)), %% Ensure that no data is hanging and close. %% Rfd is stolen at this point. - check_stop_ret(select_nif(WFd, ?ERL_NIF_SELECT_STOP, WFd, null, Ref)), + check_stop_ret(select_nif(WFd, ?ERL_NIF_SELECT_STOP, WFd, null, Ref, null)), ?assertMatch([{fd_resource_stop, WPtr, _}], flush()), {1, {WPtr, 1}} = last_fd_stop_call(), - check_stop_ret(select_nif(RFd, ?ERL_NIF_SELECT_STOP, RFd, null, Ref)), + check_stop_ret(select_nif(RFd, ?ERL_NIF_SELECT_STOP, RFd, null, Ref, null)), ?assertMatch([{fd_resource_stop, RPtr, _}], flush()), {1, {RPtr, _DirectCall}} = last_fd_stop_call(), @@ -3396,7 +3409,7 @@ term_to_binary_nif(_, _) -> ?nif_stub. binary_to_term_nif(_, _, _) -> ?nif_stub. port_command_nif(_, _) -> ?nif_stub. format_term_nif(_,_) -> ?nif_stub. -select_nif(_,_,_,_,_) -> ?nif_stub. +select_nif(_,_,_,_,_,_) -> ?nif_stub. dupe_resource_nif(_) -> ?nif_stub. pipe_nif() -> ?nif_stub. write_nif(_,_) -> ?nif_stub. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index f2ce6dbe67..21af4b05b3 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -2486,7 +2486,8 @@ static ERL_NIF_TERM select_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv enum ErlNifSelectFlags mode; void* obj; ErlNifPid nifpid, *pid = NULL; - ERL_NIF_TERM ref; + ERL_NIF_TERM ref_or_msg; + ErlNifEnv* msg_env = NULL; int retval; if (!get_fd(env, argv[0], &fdr) @@ -2501,11 +2502,27 @@ static ERL_NIF_TERM select_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv return enif_make_badarg(env); pid = &nifpid; } - ref = argv[4]; + ref_or_msg = argv[4]; + if (argv[5] != atom_null) { + msg_env = enif_alloc_env(); + ref_or_msg = enif_make_copy(msg_env, ref_or_msg); + } fdr->was_selected = 1; enif_self(env, &fdr->pid); - retval = enif_select(env, fdr->fd, mode, obj, pid, ref); + switch (mode) { + case ERL_NIF_SELECT_CUSTOM_MSG | ERL_NIF_SELECT_READ: + retval = enif_select_read(env, fdr->fd, obj, pid, ref_or_msg, msg_env); + break; + case ERL_NIF_SELECT_CUSTOM_MSG | ERL_NIF_SELECT_WRITE: + retval = enif_select_write(env, fdr->fd, obj, pid, ref_or_msg, msg_env); + break; + default: + retval = enif_select(env, fdr->fd, mode, obj, pid, ref_or_msg); + } + + if (msg_env) + enif_free_env(msg_env); return enif_make_int(env, retval); } @@ -3565,7 +3582,7 @@ static ErlNifFunc nif_funcs[] = {"binary_to_term_nif", 3, binary_to_term}, {"port_command_nif", 2, port_command}, {"format_term_nif", 2, format_term}, - {"select_nif", 5, select_nif}, + {"select_nif", 6, select_nif}, #ifndef __WIN32__ {"pipe_nif", 0, pipe_nif}, {"write_nif", 2, write_nif}, diff --git a/erts/emulator/test/num_bif_SUITE.erl b/erts/emulator/test/num_bif_SUITE.erl index f15217814a..6b834705cf 100644 --- a/erts/emulator/test/num_bif_SUITE.erl +++ b/erts/emulator/test/num_bif_SUITE.erl @@ -504,6 +504,10 @@ t_integer_to_string(Config) when is_list(Config) -> test_its("A", 10, 16), test_its("D4BE", 54462, 16), test_its("-D4BE", -54462, 16), + test_its("FFFFFFFFFF", 1099511627775, 16), + test_its("123456789ABCDEF123456789ABCDEF123456789ABCDEF", + 108977460683796539709587792812439445667270661579197935, + 16), lists:foreach(fun(Value) -> {'EXIT', {badarg, _}} = @@ -515,12 +519,14 @@ t_integer_to_string(Config) when is_list(Config) -> ok. test_its(List,Int) -> - Int = list_to_integer(List), - Int = binary_to_integer(list_to_binary(List)). + List = integer_to_list(Int), + Binary = list_to_binary(List), + Binary = integer_to_binary(Int). test_its(List,Int,Base) -> - Int = list_to_integer(List, Base), - Int = binary_to_integer(list_to_binary(List), Base). + List = integer_to_list(Int, Base), + Binary = list_to_binary(List), + Binary = integer_to_binary(Int, Base). %% Tests binary_to_integer/1. diff --git a/erts/emulator/test/persistent_term_SUITE.erl b/erts/emulator/test/persistent_term_SUITE.erl index 58cd3276b0..58038e24b7 100644 --- a/erts/emulator/test/persistent_term_SUITE.erl +++ b/erts/emulator/test/persistent_term_SUITE.erl @@ -21,7 +21,7 @@ -module(persistent_term_SUITE). -include_lib("common_test/include/ct.hrl"). --export([all/0,suite/0, +-export([all/0,suite/0,init_per_suite/1,end_per_suite/1, basic/1,purging/1,sharing/1,get_trapping/1, info/1,info_trapping/1,killed_while_trapping/1, off_heap_values/1,keys/1,collisions/1, @@ -39,39 +39,49 @@ all() -> killed_while_trapping,off_heap_values,keys,collisions, init_restart]. +init_per_suite(Config) -> + %% Put a term in the dict so that we know that the testcases handle + %% stray terms left by stdlib or other test suites. + persistent_term:put(init_per_suite, {?MODULE}), + Config. + +end_per_suite(Config) -> + persistent_term:erase(init_per_suite), + Config. + basic(_Config) -> Chk = chk(), N = 777, Seq = lists:seq(1, N), - par(2, N, Seq), - seq(3, Seq), - seq(3, Seq), %Same values. + par(2, N, Seq, Chk), + seq(3, Seq, Chk), + seq(3, Seq, Chk), %Same values. _ = [begin Key = {?MODULE,{key,I}}, true = persistent_term:erase(Key), false = persistent_term:erase(Key), {'EXIT',{badarg,_}} = (catch persistent_term:get(Key)) end || I <- Seq], - [] = [P || {{?MODULE,_},_}=P <- persistent_term:get()], + [] = [P || {{?MODULE,_},_}=P <- pget(Chk)], chk(Chk). -par(C, N, Seq) -> +par(C, N, Seq, Chk) -> _ = [spawn_link(fun() -> ok = persistent_term:put({?MODULE,{key,I}}, {value,C*I}) end) || I <- Seq], - Result = wait(N), + Result = wait(N, Chk), _ = [begin Double = C*I, {{?MODULE,{key,I}},{value,Double}} = Res end || {I,Res} <- lists:zip(Seq, Result)], ok. -seq(C, Seq) -> +seq(C, Seq, Chk) -> _ = [ok = persistent_term:put({?MODULE,{key,I}}, {value,C*I}) || I <- Seq], - All = persistent_term:get(), - All = [P || {{?MODULE,_},_}=P <- persistent_term:get()], + All = pget(Chk), + All = [P || {{?MODULE,_},_}=P <- All], All = [{Key,persistent_term:get(Key)} || {Key,_} <- All], Result = lists:sort(All), _ = [begin @@ -80,15 +90,15 @@ seq(C, Seq) -> end || {I,Res} <- lists:zip(Seq, Result)], ok. -wait(N) -> - All = [P || {{?MODULE,_},_}=P <- persistent_term:get()], +wait(N, Chk) -> + All = [P || {{?MODULE,_},_}=P <- pget(Chk)], case length(All) of N -> All = [{Key,persistent_term:get(Key)} || {Key,_} <- All], lists:sort(All); _ -> receive after 10 -> ok end, - wait(N) + wait(N, Chk) end. %% Make sure that terms that have been erased are copied into all @@ -200,23 +210,23 @@ get_trapping(_Config) -> _ -> 1000 end, spawn_link(fun() -> get_trapping_create(N) end), - All = do_get_trapping(N, []), + All = do_get_trapping(N, [], Chk), N = get_trapping_check_result(lists:sort(All), 1), erlang:garbage_collect(), get_trapping_erase(N), chk(Chk). -do_get_trapping(N, Prev) -> - case persistent_term:get() of +do_get_trapping(N, Prev, Chk) -> + case pget(Chk) of Prev when length(Prev) >= N -> All = [P || {{?MODULE,{get_trapping,_}},_}=P <- Prev], case length(All) of N -> All; - _ -> do_get_trapping(N, Prev) + _ -> do_get_trapping(N, Prev, Chk) end; New -> receive after 1 -> ok end, - do_get_trapping(N, New) + do_get_trapping(N, New, Chk) end. get_trapping_create(0) -> @@ -331,25 +341,25 @@ info_trapping(_Config) -> _ -> 1000 end, spawn_link(fun() -> info_trapping_create(N) end), - All = do_info_trapping(N, 0), + All = do_info_trapping(N, 0, Chk), N = info_trapping_check_result(lists:sort(All), 1), erlang:garbage_collect(), info_trapping_erase(N), chk(Chk). -do_info_trapping(N, PrevMem) -> +do_info_trapping(N, PrevMem, Chk) -> case info_info() of - {N,Mem} -> + {M,Mem} when M >= N -> true = Mem >= PrevMem, - All = [P || {{?MODULE,{info_trapping,_}},_}=P <- persistent_term:get()], + All = [P || {{?MODULE,{info_trapping,_}},_}=P <- pget(Chk)], case length(All) of N -> All; - _ -> do_info_trapping(N, PrevMem) + _ -> do_info_trapping(N, PrevMem, Chk) end; {_,Mem} -> true = Mem >= PrevMem, receive after 1 -> ok end, - do_info_trapping(N, Mem) + do_info_trapping(N, Mem, Chk) end. info_trapping_create(0) -> @@ -462,17 +472,17 @@ collisions(_Config) -> _ = [V = persistent_term:get(K) || {K,V} <- Kvs], %% Now delete the persistent terms in random order. - collisions_delete(lists:keysort(2, Kvs)), + collisions_delete(lists:keysort(2, Kvs), Chk), chk(Chk). -collisions_delete([{Key,Val}|Kvs]) -> +collisions_delete([{Key,Val}|Kvs], Chk) -> Val = persistent_term:get(Key), true = persistent_term:erase(Key), - true = lists:sort(persistent_term:get()) =:= lists:sort(Kvs), + true = lists:sort(pget(Chk)) =:= lists:sort(Kvs), _ = [V = persistent_term:get(K) || {K,V} <- Kvs], - collisions_delete(Kvs); -collisions_delete([]) -> + collisions_delete(Kvs, Chk); +collisions_delete([], _) -> ok. colliding_keys() -> @@ -589,15 +599,16 @@ do_test_init_restart_cmd(File) -> %% and after each test case. chk() -> - persistent_term:info(). + {persistent_term:info(), persistent_term:get()}. -chk(Chk) -> - Chk = persistent_term:info(), +chk({Info, _Initial} = Chk) -> + Info = persistent_term:info(), Key = {?MODULE,?FUNCTION_NAME}, - ok = persistent_term:put(Key, {term,Chk}), + ok = persistent_term:put(Key, {term,Info}), Term = persistent_term:get(Key), true = persistent_term:erase(Key), chk_not_stuck(Term), + [persistent_term:erase(K) || {K, _} <- pget(Chk)], ok. chk_not_stuck(Term) -> @@ -612,3 +623,6 @@ chk_not_stuck(Term) -> _ -> ok end. + +pget({_, Initial}) -> + persistent_term:get() -- Initial. diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index f4b1d885fe..edf08ce0bd 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -2104,6 +2104,13 @@ spawn_opt_max_heap_size(_Config) -> error_logger:add_report_handler(?MODULE, self()), + %% flush any prior messages in error_logger + Pid = spawn(fun() -> ok = nok end), + receive + {error, _, {emulator, _, [Pid|_]}} -> + flush() + end, + %% Test that numerical limit works max_heap_size_test(1024, 1024, true, true), @@ -2208,6 +2215,13 @@ receive_unexpected() -> ok end. +flush() -> + receive + _M -> flush() + after 0 -> + ok + end. + %% error_logger report handler proxy init(Pid) -> {ok, Pid}. diff --git a/erts/emulator/test/scheduler_SUITE.erl b/erts/emulator/test/scheduler_SUITE.erl index f04efb9003..2e0dfa42f3 100644 --- a/erts/emulator/test/scheduler_SUITE.erl +++ b/erts/emulator/test/scheduler_SUITE.erl @@ -1450,26 +1450,29 @@ poll_threads(Config) when is_list(Config) -> {Conc, PollType, KP} = get_ioconfig(Config), {Sched, SchedOnln, _} = get_sstate(Config, ""), - [1, 1] = get_ionum(Config,"+IOt 2 +IOp 2"), - [1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 5 +IOp 5"), - - [1, 1] = get_ionum(Config, "+S 2 +IOPt 100 +IOPp 100"), - if Conc -> - [5] = get_ionum(Config,"+IOt 5 +IOp 1"), - [3, 2] = get_ionum(Config,"+IOt 5 +IOp 2"), - [2, 2, 2, 2, 2] = get_ionum(Config,"+IOt 10 +IOPp 50"), + [1, 1, 1] = get_ionum(Config,"+IOt 2 +IOp 2"), + [1, 1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 5 +IOp 5"), + [1, 1, 1] = get_ionum(Config, "+S 2 +IOPt 100 +IOPp 100"), - [2] = get_ionum(Config, "+S 2 +IOPt 100"), - [4] = get_ionum(Config, "+S 4 +IOPt 100"), - [4] = get_ionum(Config, "+S 4:2 +IOPt 100"), - [4, 4] = get_ionum(Config, "+S 8 +IOPt 100 +IOPp 25"), + [5, 1] = get_ionum(Config,"+IOt 5 +IOp 1"), + [3, 2, 1] = get_ionum(Config,"+IOt 5 +IOp 2"), + [2, 2, 2, 2, 2, 1] = get_ionum(Config,"+IOt 10 +IOPp 50"), + + [2, 1] = get_ionum(Config, "+S 2 +IOPt 100"), + [4, 1] = get_ionum(Config, "+S 4 +IOPt 100"), + [4, 1] = get_ionum(Config, "+S 4:2 +IOPt 100"), + [4, 4, 1] = get_ionum(Config, "+S 8 +IOPt 100 +IOPp 25"), fail = get_ionum(Config, "+IOt 1 +IOp 2"), ok; not Conc -> + [1, 1] = get_ionum(Config,"+IOt 2 +IOp 2"), + [1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 5 +IOp 5"), + [1, 1] = get_ionum(Config, "+S 2 +IOPt 100 +IOPp 100"), + [1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 5 +IOp 1"), [1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 5 +IOp 2"), [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] = get_ionum(Config,"+IOt 10 +IOPp 50"), diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl index fab2f45f28..4e6baa9e0e 100644 --- a/erts/emulator/test/signal_SUITE.erl +++ b/erts/emulator/test/signal_SUITE.erl @@ -85,7 +85,7 @@ xm_sig_order_proc() -> receive may_not_reach -> exit(bad_signal_order); may_reach -> ok - after 0 -> ok + after 0 -> erlang:yield() end, xm_sig_order_proc(). diff --git a/erts/emulator/test/system_info_SUITE.erl b/erts/emulator/test/system_info_SUITE.erl index 8ea2d88ec4..55b1162cfb 100644 --- a/erts/emulator/test/system_info_SUITE.erl +++ b/erts/emulator/test/system_info_SUITE.erl @@ -37,8 +37,9 @@ -export([process_count/1, system_version/1, misc_smoke_tests/1, heap_size/1, wordsize/1, memory/1, ets_limit/1, atom_limit/1, - ets_count/1, - atom_count/1]). + ets_count/1, atom_count/1, system_logger/1]). + +-export([init/1, handle_event/2, handle_call/2]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -46,8 +47,8 @@ suite() -> all() -> [process_count, system_version, misc_smoke_tests, - ets_count, - heap_size, wordsize, memory, ets_limit, atom_limit, atom_count]. + ets_count, heap_size, wordsize, memory, ets_limit, atom_limit, atom_count, + system_logger]. %%% %%% The test cases ------------------------------------------------------------- @@ -578,3 +579,78 @@ atom_count(Config) when is_list(Config) -> true = Limit >= Count2, true = Count2 > Count1, ok. + + +system_logger(Config) when is_list(Config) -> + + TC = self(), + + ok = error_logger:add_report_handler(?MODULE, [TC]), + + generate_log_event(), + + flush(1, report_handler), + + Initial = erlang:system_info(system_logger), + + {Logger,_} = spawn_monitor(fun F() -> receive M -> TC ! {system_logger,M}, F() end end), + + Initial = erlang:system_flag(system_logger, Logger), + Logger = erlang:system_info(system_logger), + + generate_log_event(), + flush(1, system_logger), + + Logger = erlang:system_flag(system_logger, Logger), + + generate_log_event(), + flush(1, system_logger), + + exit(Logger, die), + receive {'DOWN',_,_,_,_} -> ok end, + + generate_log_event(), + flush(1, report_handler), + + logger = erlang:system_info(system_logger), + + logger = erlang:system_flag(system_logger, undefined), + generate_log_event(), + flush(), + + undefined = erlang:system_flag(system_logger, Initial), + + ok. + +flush() -> + receive + M -> + ct:fail({unexpected_message, M}) + after 0 -> + ok + end. + +flush(0, _Pat) -> + flush(); +flush(Cnt, Pat) -> + receive + M when element(1,M) =:= Pat -> + ct:log("~p",[M]), + flush(Cnt-1, Pat) + after 500 -> + ct:fail({missing, Cnt, Pat}) + end. + +generate_log_event() -> + {_Pid, Ref} = spawn_monitor(fun() -> ok = nok end), + receive {'DOWN', Ref, _, _, _} -> ok end. + +init([To]) -> + {ok, To}. + +handle_call(Msg, State) -> + {ok, Msg, State}. + +handle_event(Event, State) -> + State ! {report_handler, Event}, + {ok, State}. diff --git a/erts/emulator/utils/beam_makeops b/erts/emulator/utils/beam_makeops index da994fae3e..f73e2362bf 100755 --- a/erts/emulator/utils/beam_makeops +++ b/erts/emulator/utils/beam_makeops @@ -1840,12 +1840,57 @@ sub do_pack_one { } # - # Return if there is nothing to pack. - # - if ($packable_args == 0) { - return (-1); - } elsif ($packable_args == 1 and $options == 0) { - return (-1); + # Check whether any packing can be done. + # + my $nothing_to_pack = $packable_args == 0 || + $packable_args == 1 && $options == 0; + if ($nothing_to_pack) { + # The packing engine in the loader processes the operands from + # right to left. Rightmost operands that are not packed must + # be stacked and then unstacked. + # + # Because instructions may be broken up into micro + # instructions, we might not see all operands at once. So + # there could be a micro instructions that packs the operands + # to the left of the current micro instruction. If that is the + # case, it is essential that we generate stacking and + # unstacking instructions even when no packing is + # possible. (build_pack_spec() will remove any unecessary + # stacking and unstacking operations.) + # + # Here is an example. Say that we have this instruction: + # + # i_plus x x j d + # + # that comprises two micro instructions: + # + # i_plus.fetch x x + # i_plus.execute j d + # + # This function (do_pack_one()) will be called twice, once to pack + # 'x' and 'x', and once to pack 'j' and 'd'. + # + # On a 32-bit machine, the 'j' and 'd' operands can't be + # packed because 'j' requires a full word. The two 'x' + # operands in the i_plus.fetch micro instruction will be + # packed, though, so we must generate instructions for packing + # and unpacking the 'j' and 'd' operands. + my $down = ''; + my $up = ''; + foreach my $arg (@args) { + my $push = 'g'; + if ($type_bit{$arg} & $type_bit{'q'}) { + # The operand may be a literal. + $push = 'q'; + } elsif ($type_bit{$arg} & $type_bit{'f'}) { + # The operand may be a failure label. + $push = 'f'; + } + $down = "$push${down}"; + $up = "${up}p"; + } + my $pack_spec = "$down:$up"; + return (1, ['',$pack_spec,@args]); } # diff --git a/erts/preloaded/ebin/atomics.beam b/erts/preloaded/ebin/atomics.beam Binary files differindex 1de97fa668..ef402b5fee 100644 --- a/erts/preloaded/ebin/atomics.beam +++ b/erts/preloaded/ebin/atomics.beam diff --git a/erts/preloaded/ebin/counters.beam b/erts/preloaded/ebin/counters.beam Binary files differindex 4e1a3566f7..674d0d27fa 100644 --- a/erts/preloaded/ebin/counters.beam +++ b/erts/preloaded/ebin/counters.beam diff --git a/erts/preloaded/ebin/erl_init.beam b/erts/preloaded/ebin/erl_init.beam Binary files differindex 93f23377b5..bac2cbb15b 100644 --- a/erts/preloaded/ebin/erl_init.beam +++ b/erts/preloaded/ebin/erl_init.beam diff --git a/erts/preloaded/ebin/erl_prim_loader.beam b/erts/preloaded/ebin/erl_prim_loader.beam Binary files differindex 42629bbe8e..61dbaa5a73 100644 --- a/erts/preloaded/ebin/erl_prim_loader.beam +++ b/erts/preloaded/ebin/erl_prim_loader.beam diff --git a/erts/preloaded/ebin/erl_tracer.beam b/erts/preloaded/ebin/erl_tracer.beam Binary files differindex a74e87d19c..ec4d6153d1 100644 --- a/erts/preloaded/ebin/erl_tracer.beam +++ b/erts/preloaded/ebin/erl_tracer.beam diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam Binary files differindex 54557d9d18..1b0cb5b50c 100644 --- a/erts/preloaded/ebin/erlang.beam +++ b/erts/preloaded/ebin/erlang.beam diff --git a/erts/preloaded/ebin/erts_code_purger.beam b/erts/preloaded/ebin/erts_code_purger.beam Binary files differindex 3cbfd94a43..669149df82 100644 --- a/erts/preloaded/ebin/erts_code_purger.beam +++ b/erts/preloaded/ebin/erts_code_purger.beam diff --git a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam Binary files differindex 43604e0480..6d3528c2dc 100644 --- a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam +++ b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam Binary files differindex 543104638b..b3af713809 100644 --- a/erts/preloaded/ebin/erts_internal.beam +++ b/erts/preloaded/ebin/erts_internal.beam diff --git a/erts/preloaded/ebin/erts_literal_area_collector.beam b/erts/preloaded/ebin/erts_literal_area_collector.beam Binary files differindex 1f7fbef85a..fc2bf6f6bd 100644 --- a/erts/preloaded/ebin/erts_literal_area_collector.beam +++ b/erts/preloaded/ebin/erts_literal_area_collector.beam diff --git a/erts/preloaded/ebin/init.beam b/erts/preloaded/ebin/init.beam Binary files differindex bcbf4a1469..bc1d341f31 100644 --- a/erts/preloaded/ebin/init.beam +++ b/erts/preloaded/ebin/init.beam diff --git a/erts/preloaded/ebin/persistent_term.beam b/erts/preloaded/ebin/persistent_term.beam Binary files differindex 79ef03b9a6..e94ef983be 100644 --- a/erts/preloaded/ebin/persistent_term.beam +++ b/erts/preloaded/ebin/persistent_term.beam diff --git a/erts/preloaded/ebin/prim_buffer.beam b/erts/preloaded/ebin/prim_buffer.beam Binary files differindex 70675f47ee..cf671bf8f4 100644 --- a/erts/preloaded/ebin/prim_buffer.beam +++ b/erts/preloaded/ebin/prim_buffer.beam diff --git a/erts/preloaded/ebin/prim_eval.beam b/erts/preloaded/ebin/prim_eval.beam Binary files differindex 5aa5a7f384..24911123f9 100644 --- a/erts/preloaded/ebin/prim_eval.beam +++ b/erts/preloaded/ebin/prim_eval.beam diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam Binary files differindex 37a1a563a9..b9f4791ba9 100644 --- a/erts/preloaded/ebin/prim_file.beam +++ b/erts/preloaded/ebin/prim_file.beam diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam Binary files differindex 23a8e8f210..1e6eb3a37f 100644 --- a/erts/preloaded/ebin/prim_inet.beam +++ b/erts/preloaded/ebin/prim_inet.beam diff --git a/erts/preloaded/ebin/prim_zip.beam b/erts/preloaded/ebin/prim_zip.beam Binary files differindex 8483d11aed..d319d7a343 100644 --- a/erts/preloaded/ebin/prim_zip.beam +++ b/erts/preloaded/ebin/prim_zip.beam diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam Binary files differindex f3737a524e..9610c94ac2 100644 --- a/erts/preloaded/ebin/zlib.beam +++ b/erts/preloaded/ebin/zlib.beam diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 261b731900..65716def11 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -2526,6 +2526,9 @@ subtract(_,_) -> OldSchedulersOnline when SchedulersOnline :: pos_integer(), OldSchedulersOnline :: pos_integer(); + (system_logger, Logger) -> PrevLogger when + Logger :: logger | undefined | pid(), + PrevLogger :: logger | undefined | pid(); (trace_control_word, TCW) -> OldTCW when TCW :: non_neg_integer(), OldTCW :: non_neg_integer(); @@ -2534,7 +2537,7 @@ subtract(_,_) -> %% These are deliberately not documented (internal_cpu_topology, term()) -> term(); (sequential_tracer, pid() | port() | {module(), term()} | false) -> pid() | port() | false; - (1,0) -> true. + (reset_seq_trace,true) -> true. system_flag(_Flag, _Value) -> erlang:nif_error(undefined). @@ -2731,8 +2734,9 @@ tuple_to_list(_Tuple) -> (schedulers | schedulers_online) -> pos_integer(); (smp_support) -> boolean(); (start_time) -> integer(); - (system_version) -> string(); (system_architecture) -> string(); + (system_logger) -> logger | undefined | pid(); + (system_version) -> string(); (threads) -> boolean(); (thread_pool_size) -> non_neg_integer(); (time_correction) -> true | false; @@ -3395,60 +3399,15 @@ get_cookie() -> -spec integer_to_list(Integer, Base) -> string() when Integer :: integer(), Base :: 2..36. -integer_to_list(I, 10) -> - erlang:integer_to_list(I); -integer_to_list(I, Base) - when erlang:is_integer(I), erlang:is_integer(Base), - Base >= 2, Base =< 1+$Z-$A+10 -> - if I < 0 -> - [$-|integer_to_list(-I, Base, [])]; - true -> - integer_to_list(I, Base, []) - end; -integer_to_list(I, Base) -> - erlang:error(badarg, [I, Base]). - -integer_to_list(I0, Base, R0) -> - D = I0 rem Base, - I1 = I0 div Base, - R1 = if D >= 10 -> - [D-10+$A|R0]; - true -> - [D+$0|R0] - end, - if I1 =:= 0 -> - R1; - true -> - integer_to_list(I1, Base, R1) - end. +integer_to_list(_I, _Base) -> + erlang:nif_error(undefined). -spec integer_to_binary(Integer, Base) -> binary() when Integer :: integer(), Base :: 2..36. -integer_to_binary(I, 10) -> - erlang:integer_to_binary(I); -integer_to_binary(I, Base) - when erlang:is_integer(I), erlang:is_integer(Base), - Base >= 2, Base =< 1+$Z-$A+10 -> - if I < 0 -> - <<$-,(integer_to_binary(-I, Base, <<>>))/binary>>; - true -> - integer_to_binary(I, Base, <<>>) - end; -integer_to_binary(I, Base) -> - erlang:error(badarg, [I, Base]). - -integer_to_binary(I0, Base, R0) -> - D = I0 rem Base, - I1 = I0 div Base, - R1 = if - D >= 10 -> <<(D-10+$A),R0/binary>>; - true -> <<(D+$0),R0/binary>> - end, - if - I1 =:= 0 -> R1; - true -> integer_to_binary(I1, Base, R1) - end. +integer_to_binary(_I, _Base) -> + erlang:nif_error(undefined). + -record(cpu, {node = -1, processor = -1, diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src index 13fbbc7ff7..9de81cae27 100644 --- a/erts/preloaded/src/erts.app.src +++ b/erts/preloaded/src/erts.app.src @@ -40,7 +40,7 @@ {registered, []}, {applications, []}, {env, []}, - {runtime_dependencies, ["stdlib-3.5", "kernel-6.1", "sasl-@OTP-13468@"]} + {runtime_dependencies, ["stdlib-3.5", "kernel-6.1", "sasl-3.3"]} ]}. %% vim: ft=erlang diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index 8f29a569f2..305b524438 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -97,6 +97,8 @@ -export([counters_new/1, counters_get/2, counters_add/3, counters_put/3, counters_info/1]). +-export([spawn_system_process/3]). + %% %% Await result of send to port %% @@ -726,3 +728,10 @@ counters_put(_Ref, _Ix, _Value) -> -spec counters_info(reference()) -> #{}. counters_info(_Ref) -> erlang:nif_error(undef). + +-spec spawn_system_process(Mod, Func, Args) -> pid() when + Mod :: atom(), + Func :: atom(), + Args :: list(). +spawn_system_process(_Mod, _Func, _Args) -> + erlang:nif_error(undefined). diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 5fc22bc582..0994e2a9f4 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -46,7 +46,7 @@ -define(MIN_READLINE_SIZE, 256). -define(LARGEFILESIZE, (1 bsl 63)). --export([copy/3]). +-export([copy/3, start/0]). -include("file_int.hrl"). @@ -85,14 +85,21 @@ is_translatable(_) -> %% This is a janitor process used to close files whose controlling process has %% died. The emulator will be torn down if this is killed. -delayed_close_loop() -> +start() -> + helper_loop(). + +helper_loop() -> receive {close, FRef} when is_reference(FRef) -> delayed_close_nif(FRef); _ -> ok end, - delayed_close_loop(). + helper_loop(). -%% +on_load() -> + %% This is spawned as a system process to prevent init:restart/0 from + %% killing it. + Pid = erts_internal:spawn_system_process(?MODULE, start, []), + ok = erlang:load_nif(atom_to_list(?MODULE), Pid). %% Returns {error, Reason} | {ok, BytesCopied} copy(#file_descriptor{module = ?MODULE} = Source, @@ -103,14 +110,6 @@ copy(#file_descriptor{module = ?MODULE} = Source, %% XXX Should be moved down to the driver for optimization. file:copy_opened(Source, Dest, Length). -on_load() -> - Pid = spawn(fun() -> - process_flag(trap_exit, true), - delayed_close_loop() - end), - true = register(erts_prim_file, Pid), - ok = erlang:load_nif(atom_to_list(?MODULE), Pid). - open(Name, Modes) -> %% The try/catch pattern seen here is used throughout the file to adhere to %% the public file interface, which has leaked through for ages because of diff --git a/erts/vsn.mk b/erts/vsn.mk index 643a8a2e76..c579b6364a 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 10.1.3 +VSN = 10.2.2 # Port number 4365 in 4.2 # Port number 4366 in 4.3 |