diff options
Diffstat (limited to 'erts')
49 files changed, 1998 insertions, 1665 deletions
diff --git a/erts/aclocal.m4 b/erts/aclocal.m4 index 86799186fd..013bfe5652 100644 --- a/erts/aclocal.m4 +++ b/erts/aclocal.m4 @@ -128,13 +128,13 @@ MIXED_MSYS=no AC_MSG_CHECKING(for mixed cygwin or msys and native VC++ environment) if test "X$host" = "Xwin32" -a "x$GCC" != "xyes"; then if test -x /usr/bin/msys-?.0.dll; then - CFLAGS="-O2" + CFLAGS="$CFLAGS -O2" MIXED_MSYS=yes AC_MSG_RESULT([MSYS and VC]) MIXED_MSYS_VC=yes CPPFLAGS="$CPPFLAGS -DERTS_MIXED_MSYS_VC" elif test -x /usr/bin/cygpath; then - CFLAGS="-O2" + CFLAGS="$CFLAGS -O2" MIXED_CYGWIN=yes AC_MSG_RESULT([Cygwin and VC]) MIXED_CYGWIN_VC=yes @@ -162,7 +162,7 @@ if test "x$MIXED_MSYS" != "xyes"; then AC_MSG_CHECKING(for mixed cygwin and native MinGW environment) if test "X$host" = "Xwin32" -a "x$GCC" = x"yes"; then if test -x /usr/bin/cygpath; then - CFLAGS="-O2" + CFLAGS="$CFLAGS -O2" MIXED_CYGWIN=yes AC_MSG_RESULT([yes]) MIXED_CYGWIN_MINGW=yes diff --git a/erts/configure.in b/erts/configure.in index ac2fae70ce..81ecad4f51 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -3858,15 +3858,15 @@ if test "$enable_lttng_test" = "yes" ; then AC_COMPILE_IFELSE( [AC_LANG_PROGRAM( [#include <lttng/tracepoint.h> - #define TRACEPOINT_PROVIDER com_ericsson_otp + #define TRACEPOINT_PROVIDER org_erlang_otp TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, dummy, TP_ARGS(int, my_int), TP_FIELDS(ctf_integer(int, my_int, my_int))) #define TRACEPOINT_CREATE_PROBES #define TRACEPOINT_DEFINE], - [if(tracepoint_enabled(com_ericsson_otp,dummy)) do {} while(0)])], + [if(tracepoint_enabled(org_erlang_otp,dummy)) do {} while(0)])], [AC_MSG_RESULT([yes])], [AC_MSG_ERROR([no (available in lttng-ust v2.7)])]) if test "x$ac_cv_header_lttng_tracepoint_h" = "xyes" \ diff --git a/erts/doc/src/absform.xml b/erts/doc/src/absform.xml index bfabb7f042..0b04f8f70e 100644 --- a/erts/doc/src/absform.xml +++ b/erts/doc/src/absform.xml @@ -152,9 +152,11 @@ <list type="bulleted"> <item>If L is an atom literal, then Rep(L) = <c>{atom,LINE,L}</c>.</item> + <item>If L is a character literal, then + Rep(L) = <c>{char,LINE,L}</c>.</item> <item>If L is a float literal, then Rep(L) = <c>{float,LINE,L}</c>.</item> - <item>If L is an integer or character literal, then + <item>If L is an integer literal, then Rep(L) = <c>{integer,LINE,L}</c>.</item> <item>If L is a string literal consisting of the characters <c>C_1</c>, ..., <c>C_k</c>, then diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 5d5bfb141f..7b90a1ccca 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -643,8 +643,7 @@ <p>Sets the default binary virtual heap size of processes to the size <c><![CDATA[Size]]></c>.</p> </item> - <marker id="+hmax"/> - <tag><c><![CDATA[+hmax Size]]></c></tag> + <tag><marker id="+hmax"/><c><![CDATA[+hmax Size]]></c></tag> <item> <p>Sets the default maximum heap size of processes to the size <c><![CDATA[Size]]></c>. If <c>+hmax</c> is not given, the default is <c>0</c> @@ -653,8 +652,7 @@ <seealso marker="erlang#process_flag_max_heap_size"> <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p> </item> - <marker id="+hmaxel"/> - <tag><c><![CDATA[+hmaxel true|false]]></c></tag> + <tag><marker id="+hmaxel"/><c><![CDATA[+hmaxel true|false]]></c></tag> <item> <p>Sets whether to send an error logger message for processes that reach the maximum heap size or not. If <c>+hmaxel</c> is not given, the default is <c>true</c>. @@ -662,8 +660,7 @@ <seealso marker="erlang#process_flag_max_heap_size"> <c>process_flag(max_heap_size, MaxHeapSize)</c></seealso>.</p> </item> - <marker id="+hmaxk"/> - <tag><c><![CDATA[+hmaxk true|false]]></c></tag> + <tag><marker id="+hmaxk"/><c><![CDATA[+hmaxk true|false]]></c></tag> <item> <p>Sets whether to kill processes that reach the maximum heap size or not. If <c>+hmaxk</c> is not given, the default is <c>true</c>. For more information, @@ -676,7 +673,7 @@ <p>Sets the initial process dictionary size of processes to the size <c><![CDATA[Size]]></c>.</p> </item> - <tag><marker id="+hmqd"><c>+hmqd off_heap|on_heap</c></marker></tag> + <tag><marker id="+hmqd"/><c>+hmqd off_heap|on_heap</c></tag> <item><p> Sets the default value for the process flag <c>message_queue_data</c>. If <c>+hmqd</c> is not diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml index 175b7f6bfb..82215ead46 100644 --- a/erts/doc/src/erl_driver.xml +++ b/erts/doc/src/erl_driver.xml @@ -348,14 +348,14 @@ all will work as before.</p> </item> <tag><marker id="time_measurement"/>Time Measurement</tag> - <item><p>Support for time measurement in drivers: + <item><p>Support for time measurement in drivers:</p> <list> <item><seealso marker="#ErlDrvTime"><c>ErlDrvTime</c></seealso></item> <item><seealso marker="#ErlDrvTimeUnit"><c>ErlDrvTimeUnit</c></seealso></item> <item><seealso marker="#erl_drv_monotonic_time"><c>erl_drv_monotonic_time()</c></seealso></item> <item><seealso marker="#erl_drv_time_offset"><c>erl_drv_time_offset()</c></seealso></item> <item><seealso marker="#erl_drv_convert_time_unit"><c>erl_drv_convert_time_unit()</c></seealso></item> - </list></p> + </list> </item> </taglist> </section> diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index 4efd155b09..8b02b3bae1 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -518,13 +518,15 @@ ok </item> <tag><marker id="reload"/>int (*reload)(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)</tag> - <note><p>The reload mechanism is <em>deprecated</em>. It was only intended - as a development feature. Do not use it as an upgrade method for - live production systems. It might be removed in future releases. Be sure - to pass <c>reload</c> as <c>NULL</c> to <seealso marker="#ERL_NIF_INIT">ERL_NIF_INIT</seealso> - to disable it when not used.</p> - </note> - <item><p><c>reload</c> is called when the NIF library is loaded + + <item> + <note><p>The reload mechanism is <em>deprecated</em>. It was only intended + as a development feature. Do not use it as an upgrade method for + live production systems. It might be removed in future releases. Be sure + to pass <c>reload</c> as <c>NULL</c> to <seealso marker="#ERL_NIF_INIT">ERL_NIF_INIT</seealso> + to disable it when not used.</p> + </note> + <p><c>reload</c> is called when the NIF library is loaded and there is already a previously loaded library for this module code.</p> <p>Works the same as <c>load</c>. The only difference is that @@ -583,9 +585,9 @@ ok <p/> <code type="none"> typedef struct { - const char* <em>name</em>; - unsigned <em>arity</em>; - ERL_NIF_TERM (*<em>fptr</em>)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + const char* name; + unsigned arity; + ERL_NIF_TERM (*fptr)(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); unsigned flags; } ErlNifFunc; </code> @@ -618,8 +620,8 @@ typedef struct { <p/> <code type="none"> typedef struct { - unsigned <em>size</em>; - unsigned char* <em>data</em>; + unsigned size; + unsigned char* data; } ErlNifBinary; </code> <p><c>ErlNifBinary</c> contains transient information about an @@ -1101,15 +1103,6 @@ typedef enum { Erlang operators <c>=:=</c> and <c>=/=</c>.</p></desc> </func> - <func><name><ret>int</ret><nametext>enif_is_on_dirty_scheduler(ErlNifEnv* env)</nametext></name> - <fsummary>Check to see if executing on a dirty scheduler thread</fsummary> - <desc> - <p>Check to see if the current NIF is executing on a dirty scheduler thread. If - executing on a dirty scheduler thread true returned; otherwise false.</p> - <p>This function can only be used from a NIF-calling thread, and with an - environment corresponding to currently executing processes.</p> - </desc> - </func> <func><name><ret>int</ret><nametext>enif_is_pid(ErlNifEnv* env, ERL_NIF_TERM term)</nametext></name> <fsummary>Determine if a term is a pid</fsummary> <desc><p>Return true if <c>term</c> is a pid.</p></desc> @@ -1820,7 +1813,25 @@ enif_map_iterator_destroy(env, &iter); <desc><p>Same as <seealso marker="erl_driver#erl_drv_thread_self">erl_drv_thread_self</seealso>. </p></desc> </func> - + <func><name><ret>int</ret><nametext>enif_thread_type(void)</nametext></name> + <fsummary>Determine type of current thread</fsummary> + <desc> + <p>Determine the type of currently executing thread. A positive value + indicates a scheduler thread while a negative value or zero indicates + another type of thread. Currently the following specific types exist + (which may be extended in the future):</p> + <taglist> + <tag><c>ERL_NIF_THR_UNDEFINED</c></tag> + <item><p>Undefined thread that is not a scheduler thread.</p></item> + <tag><c>ERL_NIF_THR_NORMAL_SCHEDULER</c></tag> + <item><p>A normal scheduler thread.</p></item> + <tag><c>ERL_NIF_THR_DIRTY_CPU_SCHEDULER</c></tag> + <item><p>A dirty CPU scheduler thread.</p></item> + <tag><c>ERL_NIF_THR_DIRTY_IO_SCHEDULER</c></tag> + <item><p>A dirty I/O scheduler thread.</p></item> + </taglist> + </desc> + </func> <func> <name><ret>ErlNifTime</ret><nametext>enif_time_offset(ErlNifTimeUnit time_unit)</nametext></name> <fsummary>Get current Time Offset</fsummary> diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index e0c3fed0c2..6289f033b2 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -2916,107 +2916,105 @@ os_prompt% </pre> <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"/> <fsummary>Starts monitoring.</fsummary> <type name="registered_name"/> <type name="registered_process_identifier"/> <type name="monitor_process_identifier"/> + <type name="monitor_port_identifier"/> <desc> - <p>Send a monitor request of type <c><anno>Type</anno></c> to the - entity identified by <c><anno>Item</anno></c>. The caller of - <c>monitor/2</c> will later be notified by a monitor message on the - following format if the monitored state is changed:</p> + <p>Sends a monitor request of type <c><anno>Type</anno></c> to the + entity identified by <c><anno>Item</anno></c>. If the monitored entity + does not exist or when it dies, the caller of <c>monitor/2</c> will + be notified by a message on the following format:</p> <code type="none">{Tag, <anno>MonitorRef</anno>, <anno>Type</anno>, Object, Info}</code> <note><p>The monitor request is an asynchronous signal. That is, it takes time before the signal reaches its destination.</p></note> - <p>Valid <c><anno>Type</anno></c>s:</p> - <taglist> - <tag><marker id="monitor_process"/><c>process</c></tag> - <item> - <p>Monitor the existence of the process identified by - <c><anno>Item</anno></c>. Valid - <c><anno>Item</anno></c>s in combination with the - <c>process <anno>Type</anno></c> can be any of the following:</p> - <taglist> - <tag><c>pid()</c></tag> - <item> - <p>The process identifier of the process to monitor.</p> - </item> - <tag><c>{RegisteredName, Node}</c></tag> - <item> - <p>A tuple consisting of a registered name of a process and - a node name. The process residing on the node <c>Node</c> - with the registered name <c>{RegisteredName, Node}</c> will - be monitored.</p> - </item> - <tag><c>RegisteredName</c></tag> - <item> - <p>The process locally registered as <c>RegisteredName</c> - will become monitored.</p> - </item> - </taglist> - <note><p>When a registered name is used, the - process that has the registered name when the - monitor request reach its destination will be monitored. - The monitor is not effected if the registered name is - unregistered, or unregistered and later registered on another - process.</p></note> - <p>The monitor is triggered either when the monitored process - terminates, is non existing, or if the connection to it is - lost. In the case the connection to it is lost, we do not know - if it still exist or not. After this type of monitor has been - triggered, the monitor is automatically removed.</p> - <p>When the monitor is triggered a <c>'DOWN'</c> message is - sent to the monitoring process. A <c>'DOWN'</c> message has - the following pattern:</p> - <code type="none">{'DOWN', MonitorRef, Type, Object, Info}</code> - <p>Here <c>MonitorRef</c> and <c>Type</c> are the same as - described earlier, and:</p> - <taglist> - <tag><c>Object</c></tag> - <item> - <p>equals:</p> - <taglist> - <tag><c><anno>Item</anno></c></tag> - <item>If <c><anno>Item</anno></c> is specified by a - process identifier.</item> - <tag><c>{RegisteredName, Node}</c></tag> - <item>If <c><anno>Item</anno></c> is specified as - <c>RegisteredName</c>, or <c>{RegisteredName, Node}</c> - where <c>Node</c> corresponds to the node that the - monitored process resides on.</item> - </taglist> - </item> - <tag><c>Info</c></tag> - <item> - <p>Either the exit reason of the process, <c>noproc</c> - (non-existing process), or <c>noconnection</c> (no - connection to the node where the monitored process - resides).</p></item> - </taglist> - <p>The monitoring is turned off when the <c>'DOWN'</c> - message is sent or when - <seealso marker="#demonitor/1">demonitor/1</seealso> - is called.</p> - <p>If an attempt is made to monitor a process on an older node - (where remote process monitoring is not implemented or - where remote process monitoring by registered name is not - implemented), the call fails with <c>badarg</c>.</p> - <note> - <p>The format of the <c>'DOWN'</c> message changed in ERTS - version 5.2 (OTP R9B) for monitoring - <em>by registered name</em>. Element <c>Object</c> of - the <c>'DOWN'</c> message could in earlier versions - sometimes be the process identifier of the monitored process and sometimes - be the registered name. Now element <c>Object</c> is - always a tuple consisting of the registered name and - the node name. Processes on new nodes (ERTS version 5.2 - or higher) always get <c>'DOWN'</c> messages on - the new format even if they are monitoring processes on old - nodes. Processes on old nodes always get <c>'DOWN'</c> - messages on the old format.</p> - </note> - </item> - <tag><marker id="monitor_time_offset"/><c>time_offset</c></tag> + + <p><c><anno>Type</anno></c> can be one of the following atoms: + <c>process</c>, <c>port</c> or <c>time_offset</c>.</p> + + <p>A monitor is triggered only once, after that it is removed from + both monitoring process and the monitored entity. + Monitors are fired when the monitored process or port terminates, + does not exist at the moment of creation, or if the connection to + it is lost. In the case with connection, we lose knowledge about + the fact if it still exists or not. The monitoring is also turned off + when <seealso marker="#demonitor/1">demonitor/1</seealso> + is called.</p> + + <p>When monitoring by name please note, that the <c>RegisteredName</c> + is resolved to <c>pid()</c> or <c>port()</c> only once + at the moment of monitor instantiation, later changes to the name + registration will not affect the existing monitor.</p> + + <p>When a monitor is triggered, a <c>'DOWN'</c> message that has the + following pattern <c>{'DOWN', MonitorRef, Type, Object, Info}</c> + is sent to the monitoring process.</p> + + <p>In monitor message <c>MonitorRef</c> and <c>Type</c> are the same as + described earlier, and:</p> + <taglist> + <tag><c>Object</c></tag> + <item> + <p>The monitored entity, which triggered the event. When monitoring + a local process or port, <c>Object</c> will be equal to the + <c>pid()</c> or <c>port()</c> that was being monitored. When + monitoring process or port by name, <c>Object</c> will have format + <c>{RegisteredName, Node}</c> where <c>RegisteredName</c> is the + name which has been used with <c>monitor/2</c> call and + <c>Node</c> is local or remote node name (for ports monitored by + name, <c>Node</c> is always local node name).</p> + </item> + <tag><c>Info</c></tag> + <item> + <p>Either the exit reason of the process, <c>noproc</c> + (process or port did not exist at the time of monitor creation), + or <c>noconnection</c> (no connection to the node where the + monitored process resides). </p></item> + </taglist> + + <p>If an attempt is made to monitor a process on an older node + (where remote process monitoring is not implemented or + where remote process monitoring by registered name is not + implemented), the call fails with <c>badarg</c>.</p> + <note> + <p>The format of the <c>'DOWN'</c> message changed in ERTS + version 5.2 (OTP R9B) for monitoring + <em>by registered name</em>. Element <c>Object</c> of + the <c>'DOWN'</c> message could in earlier versions + sometimes be the process identifier of the monitored process and sometimes + be the registered name. Now element <c>Object</c> is + always a tuple consisting of the registered name and + the node name. Processes on new nodes (ERTS version 5.2 + or higher) always get <c>'DOWN'</c> messages on + the new format even if they are monitoring processes on old + nodes. Processes on old nodes always get <c>'DOWN'</c> + messages on the old format.</p> + </note> + + <taglist> + <tag>Monitoring a <marker id="monitor_process"/><c>process</c></tag> + <item> + <p>Creates monitor between the current process and another + process identified by <c><anno>Item</anno></c>, which can be a + <c>pid()</c> (local or remote), an atom <c>RegisteredName</c> or + a tuple <c>{RegisteredName, Node}</c> for a registered process, + located elsewhere.</p> + </item> + + <tag>Monitoring a <marker id="monitor_port"/><c>port</c></tag> + <item> + <p>Creates monitor between the current process and a port + identified by <c><anno>Item</anno></c>, which can be a + <c>port()</c> (only local), an atom <c>RegisteredName</c> or + a tuple <c>{RegisteredName, Node}</c> for a registered port, + located on this node. Note, that attempt to monitor a remote port + will result in <c>badarg</c>.</p> + </item> + + <tag>Monitoring a + <marker id="monitor_time_offset"/><c>time_offset</c></tag> <item> <p>Monitor changes in <seealso marker="#time_offset/0">time offset</seealso> @@ -3072,15 +3070,17 @@ os_prompt% </pre> Note that you can observe the change of the time offset when calling <c>erlang:time_offset()</c> before you get the <c>'CHANGE'</c> message.</p> - </item> </taglist> + <p>Making several calls to <c>monitor/2</c> for the same - <c><anno>Item</anno></c> and/or <c><anno>Type</anno></c> is not - an error; it results in as many independent monitoring instances.</p> + <c><anno>Item</anno></c> and/or <c><anno>Type</anno></c> is not + an error; it results in as many independent monitoring instances.</p> + <p>The monitor functionality is expected to be extended. That is, - other <c><anno>Type</anno></c>s and <c><anno>Item</anno></c>s - are expected to be supported in a future release.</p> + other <c><anno>Type</anno></c>s and <c><anno>Item</anno></c>s + are expected to be supported in a future release.</p> + <note> <p>If or when <c>monitor/2</c> is extended, other possible values for <c>Tag</c>, <c>Object</c> and @@ -4150,6 +4150,22 @@ os_prompt% </pre> <func> <name name="port_info" arity="2" clause_i="8"/> + <fsummary>Which processes are monitoring this port.</fsummary> + <desc> + <p>Returns list of pids that are monitoring given port at the + moment.</p> + <p>If the port identified by <c><anno>Port</anno></c> is not open, + <c>undefined</c> is returned. If the port is closed and the + calling process was previously linked to the port, the exit + signal from the port is guaranteed to be delivered before + <c>port_info/2</c> returns <c>undefined</c>.</p> + <p>Failure: <c>badarg</c> if <c><anno>Port</anno></c> is not a local + port identifier, or an atom.</p> + </desc> + </func> + + <func> + <name name="port_info" arity="2" clause_i="9"/> <fsummary>Information about the name of a port.</fsummary> <desc> <p><c><anno>Name</anno></c> is the command name set by @@ -4165,7 +4181,7 @@ os_prompt% </pre> </func> <func> - <name name="port_info" arity="2" clause_i="9"/> + <name name="port_info" arity="2" clause_i="10"/> <fsummary>Information about the OS pid of a port.</fsummary> <desc> <p><c><anno>OsPid</anno></c> is the process identifier (or equivalent) @@ -4184,7 +4200,7 @@ os_prompt% </pre> </func> <func> - <name name="port_info" arity="2" clause_i="10"/> + <name name="port_info" arity="2" clause_i="11"/> <fsummary>Information about the output of a port.</fsummary> <desc> <p><c><anno>Bytes</anno></c> is the total number of bytes written @@ -4203,7 +4219,7 @@ os_prompt% </pre> </func> <func> - <name name="port_info" arity="2" clause_i="11"/> + <name name="port_info" arity="2" clause_i="12"/> <fsummary>Information about the parallelism hint of a port.</fsummary> <desc> <p><c><anno>Boolean</anno></c> corresponds to the port parallelism @@ -4214,7 +4230,7 @@ os_prompt% </pre> </func> <func> - <name name="port_info" arity="2" clause_i="12"/> + <name name="port_info" arity="2" clause_i="13"/> <fsummary>Information about the queue size of a port.</fsummary> <desc> <p><c><anno>Bytes</anno></c> is the total number @@ -4231,7 +4247,7 @@ os_prompt% </pre> </func> <func> - <name name="port_info" arity="2" clause_i="13"/> + <name name="port_info" arity="2" clause_i="14"/> <fsummary>Information about the registered name of a port.</fsummary> <desc> <p><c><anno>RegisteredName</anno></c> is the registered name of @@ -4345,86 +4361,86 @@ os_prompt% </pre> <marker id="process_flag_max_heap_size"/> <func> <name name="process_flag" arity="2" clause_i="5"/> - <type name="max_heap_size"/> <fsummary>Sets process flag <c>max_heap_size</c> for the calling process.</fsummary> + <type name="max_heap_size"/> <desc> <p> This flag sets the maximum heap size for the calling process. If <c><anno>MaxHeapSize</anno></c> is an integer, the system default values for <c>kill</c> and <c>error_logger</c> are used. - <taglist> - <tag><c>size</c></tag> - <item> - <p> - The maximum size in words of the process. If set to zero, the - heap size limit is disabled. Badarg will be thrown if the value is - smaller than - <seealso marker="#process_flag_min_heap_size"><c>min_heap_size</c></seealso>. - The size check is only done when a garbage collection is triggered. - </p> - <p> - <c>size</c> is the entire heap of the process when garbage collection - is triggered, this includes all generational heaps, the process stack, - any <seealso marker="#process_flag_message_queue_data"> - messages that are considered to be part of the heap</seealso> and any - extra memory that the garbage collector needs during collection. - </p> - <p> - <c>size</c> is the same as can be retrieved using - <seealso marker="#process_info_total_heap_size"> - <c>erlang:process_info(Pid, total_heap_size)</c></seealso>, - or by adding <c>heap_block_size</c>, <c>old_heap_block_size</c> - and <c>mbuf_size</c> from <seealso marker="#process_info_garbage_collection_info"> - <c>erlang:process_info(Pid, garbage_collection_info)</c></seealso>. - </p> - </item> - <tag><c>kill</c></tag> - <item> - <p> - When set to <c>true</c> the runtime system will send an - untrappable exit signal with reason <c>kill</c> to the process - if the maximum heap size is reached. The garbage collection - that triggered the <c>kill</c> will not be completed, instead the - process will exit as soon as is possible. When set to <c>false</c> - no exit signal will be sent to the process, instead it will - continue executing. - </p> - <p> - If <c>kill</c> is not defined in the map - the system default will be used. The default system default - is <c>true</c>. It can be changed by either the erl - <seealso marker="erl#+hmaxk">+hmaxk</seealso> option, - or <seealso marker="#system_flag_max_heap_size"><c> - erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>. - </p> - </item> - <tag><c>error_logger</c></tag> - <item> - <p> - When set to <c>true</c> the runtime system will send a - message to the current <seealso marker="kernel:error_logger"><c>error_logger</c></seealso> - containing details about the process when the maximum - heap size is reached. One <c>error_logger</c> report will - be sent each time the limit is reached. - </p> - <p> - If <c>error_logger</c> is not defined in the map the system - default will be used. The default system default is <c>true</c>. - It can be changed by either the erl <seealso marker="erl#+hmaxel">+hmaxel</seealso> - option, or <seealso marker="#system_flag_max_heap_size"><c> - erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>. - </p> - </item> + </p> + <taglist> + <tag><c>size</c></tag> + <item> + <p> + The maximum size in words of the process. If set to zero, the + heap size limit is disabled. Badarg will be thrown if the value is + smaller than + <seealso marker="#process_flag_min_heap_size"><c>min_heap_size</c></seealso>. + The size check is only done when a garbage collection is triggered. + </p> <p> - The heap size of a process is quite hard to predict, especially the - amount of memory that is used during the garbage collection. When - contemplating using this option, it is recommended to first run - it in production with <c>kill</c> set to <c>false</c> and inspect - the <c>error_logger</c> reports to see what the normal peak sizes - of the processes in the system is and then tune the value - accordingly. + <c>size</c> is the entire heap of the process when garbage collection + is triggered, this includes all generational heaps, the process stack, + any <seealso marker="#process_flag_message_queue_data"> + messages that are considered to be part of the heap</seealso> and any + extra memory that the garbage collector needs during collection. </p> - </taglist> + <p> + <c>size</c> is the same as can be retrieved using + <seealso marker="#process_info_total_heap_size"> + <c>erlang:process_info(Pid, total_heap_size)</c></seealso>, + or by adding <c>heap_block_size</c>, <c>old_heap_block_size</c> + and <c>mbuf_size</c> from <seealso marker="#process_info_garbage_collection_info"> + <c>erlang:process_info(Pid, garbage_collection_info)</c></seealso>. + </p> + </item> + <tag><c>kill</c></tag> + <item> + <p> + When set to <c>true</c> the runtime system will send an + untrappable exit signal with reason <c>kill</c> to the process + if the maximum heap size is reached. The garbage collection + that triggered the <c>kill</c> will not be completed, instead the + process will exit as soon as is possible. When set to <c>false</c> + no exit signal will be sent to the process, instead it will + continue executing. + </p> + <p> + If <c>kill</c> is not defined in the map + the system default will be used. The default system default + is <c>true</c>. It can be changed by either the erl + <seealso marker="erl#+hmaxk">+hmaxk</seealso> option, + or <seealso marker="#system_flag_max_heap_size"><c> + erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>. + </p> + </item> + <tag><c>error_logger</c></tag> + <item> + <p> + When set to <c>true</c> the runtime system will send a + message to the current <seealso marker="kernel:error_logger"><c>error_logger</c></seealso> + containing details about the process when the maximum + heap size is reached. One <c>error_logger</c> report will + be sent each time the limit is reached. + </p> + <p> + If <c>error_logger</c> is not defined in the map the system + default will be used. The default system default is <c>true</c>. + It can be changed by either the erl <seealso marker="erl#+hmaxel">+hmaxel</seealso> + option, or <seealso marker="#system_flag_max_heap_size"><c> + erlang:system_flag(max_heap_size, MaxHeapSize)</c></seealso>. + </p> + </item> + </taglist> + <p> + The heap size of a process is quite hard to predict, especially the + amount of memory that is used during the garbage collection. When + contemplating using this option, it is recommended to first run + it in production with <c>kill</c> set to <c>false</c> and inspect + the <c>error_logger</c> reports to see what the normal peak sizes + of the processes in the system is and then tune the value + accordingly. </p> </desc> </func> @@ -4781,8 +4797,10 @@ os_prompt% </pre> The content of <c><anno>GCInfo</anno></c> can be changed without prior notice.</p> </item> - <marker id="process_info_garbage_collection_info"/> - <tag><c>{garbage_collection_info, <anno>GCInfo</anno>}</c></tag> + <tag> + <marker id="process_info_garbage_collection_info"/> + <c>{garbage_collection_info, <anno>GCInfo</anno>}</c> + </tag> <item> <p><c><anno>GCInfo</anno></c> is a list containing miscellaneous detailed information about garbage collection for this process. @@ -4865,10 +4883,19 @@ os_prompt% </pre> <p>A list of monitors (started by <c>monitor/2</c>) that are active for the process. For a local process monitor or a remote process monitor by a process - identifier, the list item is <c>{process, <anno>Pid</anno>}</c>. - For a remote process - monitor by name, the list item is - <c>{process, {<anno>RegName</anno>, <anno>Node</anno>}}</c>.</p> + identifier, the list consists of:</p> + <taglist> + <tag><c>{process, <anno>Pid</anno>}</c></tag> + <item>Process is monitored by pid.</item> + <tag><c>{process, {<anno>RegName</anno>, <anno>Node</anno>}}</c></tag> + <item>Local or remote process is monitored by name.</item> + <tag><c>{port, PortId}</c></tag> + <item>Local port is monitored by port id.</item> + <tag><c>{port, {<anno>RegName</anno>, <anno>Node</anno>}}</c></tag> + <item>Local port is monitored by name. Please note, that + remote port monitors are not supported, so <c>Node</c> will + always be the local node name.</item> + </taglist> </item> <tag><c>{message_queue_data, <anno>MQD</anno>}</c></tag> <item> @@ -4961,8 +4988,10 @@ os_prompt% </pre> total suspend count on <c><anno>Suspendee</anno></c>, only the parts contributed by <c><anno>Pid</anno></c>.</p> </item> - <marker id="process_info_total_heap_size"/> - <tag><c>{total_heap_size, <anno>Size</anno>}</c></tag> + <tag> + <marker id="process_info_total_heap_size"/> + <c>{total_heap_size, <anno>Size</anno>}</c> + </tag> <item> <p><c><anno>Size</anno></c> is the total size, in words, of all heap fragments of the process. This includes the process stack and @@ -6606,8 +6635,8 @@ ok <marker id="system_flag_max_heap_size"></marker> <func> <name name="system_flag" arity="2" clause_i="8"/> - <type name="max_heap_size"/> <fsummary>Sets system flag <c>max_heap_size</c></fsummary> + <type name="max_heap_size"/> <desc> <p> Sets the default maximum heap size settings for processes. @@ -7111,9 +7140,9 @@ ok <name name="system_info" arity="1" clause_i="37"/> <name name="system_info" arity="1" clause_i="38"/> <name name="system_info" arity="1" clause_i="39"/> + <fsummary>Information about the default process heap settings.</fsummary> <type name="message_queue_data"/> <type name="max_heap_size"/> - <fsummary>Information about the default process heap settings.</fsummary> <desc> <taglist> <tag><c>fullsweep_after</c></tag> @@ -7158,7 +7187,7 @@ ok where <c><anno>MinHeapSize</anno></c> is the current system-wide minimum heap size for spawned processes.</p> </item> - <tag><marker id="system_info_message_queue_data"><c>message_queue_data</c></marker></tag> + <tag><marker id="system_info_message_queue_data"/><c>message_queue_data</c></tag> <item> <p>Returns the default value of the <c>message_queue_data</c> process flag which is either <c>off_heap</c>, or <c>on_heap</c>. @@ -7639,7 +7668,7 @@ ok and <seealso marker="#system_info_schedulers">erlang:system_info(schedulers)</seealso>.</p> </item> - <tag><marker id="system_info_otp_release"><c>otp_release</c></marker></tag> + <tag><marker id="system_info_otp_release"/><c>otp_release</c></tag> <item> <marker id="system_info_otp_release"></marker> <p>Returns a string containing the OTP release number of the @@ -8592,21 +8621,21 @@ timestamp() -> <tag><c>send</c></tag> <item> <p>Traces sending of messages.</p> - <p>Message tags: <c><seealso marker="#trace_3_trace_messages_send">send</seealso></c> and - <c><seealso marker="#trace_3_trace_messages_send_to_non_existing_process">send_to_non_existing_process</seealso></c>.</p> + <p>Message tags: <seealso marker="#trace_3_trace_messages_send"><c>send</c></seealso> and + <seealso marker="#trace_3_trace_messages_send_to_non_existing_process"><c>send_to_non_existing_process</c></seealso>.</p> </item> <tag><c>'receive'</c></tag> <item> <p>Traces receiving of messages.</p> - <p>Message tags: <c><seealso marker="#trace_3_trace_messages_receive">'receive'</seealso></c>.</p> + <p>Message tags: <seealso marker="#trace_3_trace_messages_receive"><c>'receive'</c></seealso>.</p> </item> <tag><c>call</c></tag> <item> <p>Traces certain function calls. Specify which function calls to trace by calling <seealso marker="#trace_pattern/3">erlang:trace_pattern/3</seealso>.</p> - <p>Message tags: <c><seealso marker="#trace_3_trace_messages_call">call</seealso></c> and - <c><seealso marker="#trace_3_trace_messages_return_from">return_from</seealso></c>.</p> + <p>Message tags: <seealso marker="#trace_3_trace_messages_call"><c>call</c></seealso> and + <seealso marker="#trace_3_trace_messages_return_from"><c>return_from</c></seealso>.</p> </item> <tag><c>silent</c></tag> <item> @@ -8624,9 +8653,9 @@ timestamp() -> specification function <c>{silent,Bool}</c>, giving a high degree of control of which functions with which arguments that trigger the trace.</p> - <p>Message tags: <c><seealso marker="#trace_3_trace_messages_call">call</seealso></c>, - <c><seealso marker="#trace_3_trace_messages_return_from">return_from</seealso></c>, and - <c><seealso marker="#trace_3_trace_messages_return_to">return_to</seealso></c>. Or rather, the absence of.</p> + <p>Message tags: <seealso marker="#trace_3_trace_messages_call"><c>call</c></seealso>, + <seealso marker="#trace_3_trace_messages_return_from"><c>return_from</c></seealso>, and + <seealso marker="#trace_3_trace_messages_return_to"><c>return_to</c></seealso>. Or rather, the absence of.</p> </item> <tag><c>return_to</c></tag> <item> @@ -8647,43 +8676,43 @@ timestamp() -> <p>To get trace messages containing return values from functions, use the <c>{return_trace}</c> match specification action instead.</p> - <p>Message tags: <c><seealso marker="#trace_3_trace_messages_return_to">return_to</seealso></c>.</p> + <p>Message tags: <seealso marker="#trace_3_trace_messages_return_to"><c>return_to</c></seealso>.</p> </item> <tag><c>procs</c></tag> <item> <p>Traces process-related events.</p> - <p>Message tags: <c><seealso marker="#trace_3_trace_messages_spawn">spawn</seealso></c>, - <c><seealso marker="#trace_3_trace_messages_spawned">spawned</seealso></c>, - <c><seealso marker="#trace_3_trace_messages_exit">exit</seealso></c>, - <c><seealso marker="#trace_3_trace_messages_register">register</seealso></c>, - <c><seealso marker="#trace_3_trace_messages_unregister">unregister</seealso></c>, - <c><seealso marker="#trace_3_trace_messages_link">link</seealso></c>, - <c><seealso marker="#trace_3_trace_messages_unlink">unlink</seealso></c>, - <c><seealso marker="#trace_3_trace_messages_getting_linked">getting_linked</seealso></c>, and - <c><seealso marker="#trace_3_trace_messages_getting_unlinked">getting_unlinked</seealso></c>.</p> + <p>Message tags: <seealso marker="#trace_3_trace_messages_spawn"><c>spawn</c></seealso>, + <seealso marker="#trace_3_trace_messages_spawned"><c>spawned</c></seealso>, + <seealso marker="#trace_3_trace_messages_exit"><c>exit</c></seealso>, + <seealso marker="#trace_3_trace_messages_register"><c>register</c></seealso>, + <seealso marker="#trace_3_trace_messages_unregister"><c>unregister</c></seealso>, + <seealso marker="#trace_3_trace_messages_link"><c>link</c></seealso>, + <seealso marker="#trace_3_trace_messages_unlink"><c>unlink</c></seealso>, + <seealso marker="#trace_3_trace_messages_getting_linked"><c>getting_linked</c></seealso>, and + <seealso marker="#trace_3_trace_messages_getting_unlinked"><c>getting_unlinked</c></seealso>.</p> </item> <tag><c>ports</c></tag> <item> <p>Traces port-related events.</p> - <p>Message tags: <c><seealso marker="#trace_3_trace_messages_open">open</seealso></c>, - <c><seealso marker="#trace_3_trace_messages_closed">closed</seealso></c>, - <c><seealso marker="#trace_3_trace_messages_register">register</seealso></c>, - <c><seealso marker="#trace_3_trace_messages_unregister">unregister</seealso></c>, - <c><seealso marker="#trace_3_trace_messages_getting_linked">getting_linked</seealso></c>, and - <c><seealso marker="#trace_3_trace_messages_getting_unlinked">getting_unlinked</seealso></c>.</p> + <p>Message tags: <seealso marker="#trace_3_trace_messages_open"><c>open</c></seealso>, + <seealso marker="#trace_3_trace_messages_closed"><c>closed</c></seealso>, + <seealso marker="#trace_3_trace_messages_register"><c>register</c></seealso>, + <seealso marker="#trace_3_trace_messages_unregister"><c>unregister</c></seealso>, + <seealso marker="#trace_3_trace_messages_getting_linked"><c>getting_linked</c></seealso>, and + <seealso marker="#trace_3_trace_messages_getting_unlinked"><c>getting_unlinked</c></seealso>.</p> </item> <tag><c>running</c></tag> <item> <p>Traces scheduling of processes.</p> - <p>Message tags: <c><seealso marker="#trace_3_trace_messages_in_proc">in</seealso></c> and - <c><seealso marker="#trace_3_trace_messages_out_proc">out</seealso></c>.</p> + <p>Message tags: <seealso marker="#trace_3_trace_messages_in_proc"><c>in</c></seealso> and + <seealso marker="#trace_3_trace_messages_out_proc"><c>out</c></seealso>.</p> </item> <tag><c>exiting</c></tag> <item> <p>Traces scheduling of exiting processes.</p> - <p>Message tags: <c><seealso marker="#trace_3_trace_messages_in_exiting_proc">in_exiting</seealso></c>, - <c><seealso marker="#trace_3_trace_messages_out_exiting_proc">out_exiting</seealso></c>, and - <c><seealso marker="#trace_3_trace_messages_out_exited_proc">out_exited</seealso></c>.</p> + <p>Message tags: <seealso marker="#trace_3_trace_messages_in_exiting_proc"><c>in_exiting</c></seealso>, + <seealso marker="#trace_3_trace_messages_out_exiting_proc"><c>out_exiting</c></seealso>, and + <seealso marker="#trace_3_trace_messages_out_exited_proc"><c>out_exited</c></seealso>.</p> </item> <tag><c>running_procs</c></tag> <item> @@ -8691,21 +8720,21 @@ timestamp() -> However this option also includes schedule events when the process executes within the context of a port without being scheduled out itself.</p> - <p>Message tags: <c><seealso marker="#trace_3_trace_messages_in_proc">in</seealso></c> and - <c><seealso marker="#trace_3_trace_messages_out_proc">out</seealso></c>.</p> + <p>Message tags: <seealso marker="#trace_3_trace_messages_in_proc"><c>in</c></seealso> and + <seealso marker="#trace_3_trace_messages_out_proc"><c>out</c></seealso>.</p> </item> <tag><c>running_ports</c></tag> <item> <p>Traces scheduling of ports.</p> - <p>Message tags: <c><seealso marker="#trace_3_trace_messages_in_port">in</seealso></c> and - <c><seealso marker="#trace_3_trace_messages_out_port">out</seealso></c>.</p> + <p>Message tags: <seealso marker="#trace_3_trace_messages_in_port"><c>in</c></seealso> and + <seealso marker="#trace_3_trace_messages_out_port"><c>out</c></seealso>.</p> </item> <tag><c>garbage_collection</c></tag> <item> <p>Traces garbage collections of processes.</p> - <p>Message tags: <c><seealso marker="#trace_3_trace_messages_gc_minor_start">gc_minor_start</seealso></c>, - <c><seealso marker="#trace_3_trace_messages_gc_max_heap_size">gc_max_heap_size</seealso></c> and - <c><seealso marker="#trace_3_trace_messages_gc_minor_end">gc_minor_end</seealso></c>.</p> + <p>Message tags: <seealso marker="#trace_3_trace_messages_gc_minor_start"><c>gc_minor_start</c></seealso>, + <seealso marker="#trace_3_trace_messages_gc_max_heap_size"><c>gc_max_heap_size</c></seealso> and + <seealso marker="#trace_3_trace_messages_gc_minor_end"><c>gc_minor_end</c></seealso>.</p> </item> <tag><c>timestamp</c></tag> <item> @@ -8733,7 +8762,7 @@ timestamp() -> <seealso marker="time_correction#Erlang_Monotonic_Time">Erlang monotonic time</seealso> time-stamp in all trace messages. The time-stamp (Ts) has the same format and value as produced by - <c><seealso marker="#monotonic_time-1">erlang:monotonic_time(nano_seconds)</seealso></c>. + <seealso marker="#monotonic_time-1"><c>erlang:monotonic_time(nano_seconds)</c></seealso>. This flag overrides the <c>cpu_timestamp</c> flag.</p> </item> <tag><c>strict_monotonic_timestamp</c></tag> @@ -8743,8 +8772,8 @@ timestamp() -> monotonic time</seealso> and a monotonically increasing integer in all trace messages. The time-stamp (Ts) has the same format and value as produced by - <c>{<seealso marker="#monotonic_time-1">erlang:monotonic_time(nano_seconds)</seealso>, - <seealso marker="#unique_integer-1">erlang:unique_integer([monotonic])</seealso>}</c>. + <c>{</c><seealso marker="#monotonic_time-1"><c>erlang:monotonic_time(nano_seconds)</c></seealso><c>,</c> + <seealso marker="#unique_integer-1"><c>erlang:unique_integer([monotonic])</c></seealso><c>}</c>. This flag overrides the <c>cpu_timestamp</c> flag.</p> </item> <tag><c>arity</c></tag> diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 2b2898f0c1..7501ccd9ce 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -32,699 +32,6 @@ <p>This document describes the changes made to the ERTS application.</p> -<section><title>Erts 8.0</title> - - <section><title>Fixed Bugs and Malfunctions</title> - <list> - <item> - <p>The handling of <c>on_load</c> functions has been - improved. The major improvement is that if a code upgrade - fails because the <c>on_load</c> function fails, the - previous version of the module will now be retained.</p> - <p> - Own Id: OTP-12593</p> - </item> - <item> - <p><c>is_builtin(erlang, apply, 3)</c> will now return - <c>true</c>.</p> - <p> - Own Id: OTP-13034</p> - </item> - <item> - <p> - Fix <c>enif_get_list_length</c> to return false if list - is improper or have length larger than <c>UINT_MAX</c> - (did return true and an incorrect length value).</p> - <p> - Own Id: OTP-13288 Aux Id: PR913 </p> - </item> - <item> - <p> - Cleanup hipe signal handling code for x86 and make it - more portable.</p> - <p> - Own Id: OTP-13341 Aux Id: PR951 </p> - </item> - <item> - <p> - Use fsync instead of fdatasync on Mac OSX.</p> - <p> - Own Id: OTP-13411</p> - </item> - <item> - <p> - Make sure to create a crash dump when running out of - memory. This was accidentally removed in the erts-7.3 - release.</p> - <p> - Own Id: OTP-13419</p> - </item> - <item> - <p> - A bug has been fixed where if erlang was started +B on a - unix platform it would be killed by a SIGUSR2 signal when - creating a crash dump.</p> - <p> - Own Id: OTP-13425</p> - </item> - <item> - <p> - Fix race between <c>process_flag(trap_exit,true)</c> and - a received exit signal.</p> - <p> - A process could terminate due to exit signal even though - <c>process_flag(trap_exit,true)</c> had returned. A very - specific timing between call to <c>process_flag/2</c> and - exit signal from another scheduler was required for this - to happen.</p> - <p> - Own Id: OTP-13452</p> - </item> - <item> - <p>Don't search for non-existing Map keys twice</p> - <p>For <c>maps:get/2,3</c> and <c>maps:find/2</c>, - searching for an immediate key, e.g. an atom, in a small - map, the search was performed twice if the key did not - exist.</p> - <p> - Own Id: OTP-13459</p> - </item> - <item> - <p> - When a abnormally large distribution message is about to - be sent, the VM has been changed to create a crash dump - instead of a core dump.</p> - <p> - Own Id: OTP-13474</p> - </item> - <item> - <p> - Fix <c>erlang:process_info/2</c> type specification</p> - <p> - Own Id: OTP-13485 Aux Id: ERL-123 </p> - </item> - <item> - <p> - Fix bug in <c>open_port/2</c> with option <c>{args, - List}</c>. A vm crash could be caused by an improper - <c>List</c>.</p> - <p> - Own Id: OTP-13489 Aux Id: ERL-127 </p> - </item> - <item> - <p> - Don't crash on terminating processes with - <c>erlang:system_profile/1,2</c></p> - <p> - Own Id: OTP-13494 Aux Id: ERL-126 </p> - </item> - <item> - <p> - Fixed bugs where the reduction counter was not handled - correct.</p> - <p> - Own Id: OTP-13512</p> - </item> - <item> - <p> - Fixed typo in description of the <c>EPMD_DUMP_REQ</c> - response.</p> - <p> - Own Id: OTP-13517</p> - </item> - <item> - <p> - Fixed a bug where a process flagged as sensitive would - sometimes record its save_calls when it shouldn't.</p> - <p> - Own Id: OTP-13540</p> - </item> - <item> - <p> - Update configure scripts to not use hardcoded path for - /bin/pwd and /bin/rm.</p> - <p> - Own Id: OTP-13562</p> - </item> - <item> - <p> - When passing a larger binary than the outputv callback of - a linked-in driver can handle in one io vector slot, the - binary is now split into multiple slots in the io vector. - This change only effects system where the max size of an - io vector slot is smaller then the word size of the - system (e.g. Windows).</p> - <p> - This change means that it is now possible on Windows to - send binaries that are larger than 4GB to port_comnmand, - which is what is used for file:write, gen_tcp:send etc.</p> - <p> - Own Id: OTP-13628</p> - </item> - </list> - </section> - - - <section><title>Improvements and New Features</title> - <list> - <item> - <p> - The tracing support has been extended to allow a <seealso - marker="erl_tracer">tracer module</seealso> to be the - trace event handler instead of a process or port. The - <seealso marker="erl_tracer">tracer module</seealso> - makes it possible for trace tools to filter or manipulate - trace event data without the trace event first haing to - be copied from the traced process or port.</p> - <p> - With the introduction of this feature, - <c>erlang:trace(all|existing, _, _)</c> now also returns - the tracer process as part of the number of processes on - which tracing is enabled. The is incompatible with the - previous releases.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-10267</p> - </item> - <item> - <p> - Introduce LTTng tracing of Erlang Runtime System</p> - <p> - For LTTng to be enabled OTP needs to be built with - configure option <c>--with-dynamic-trace=lttng</c>.</p> - <p> - This feature introduces tracepoints for schedulers, - drivers, memory carriers, memory and async thread pool.</p> - <p> For a list of all tracepoints, see <seealso - marker="runtime_tools:LTTng">Runtime Tools User's - Guide</seealso> .</p> - <p> - Own Id: OTP-10282</p> - </item> - <item> - <p> - Add microstate accounting</p> - <p> - Microstate accounting is a way to track which state the - different threads within ERTS are in. The main usage area - is to pin point performance bottlenecks by checking which - states the threads are in and then from there figuring - out why and where to optimize.</p> - <p> - Since checking whether microstate accounting is on or off - is relatively expensive only a few of the states are - enabled by default and more states can be enabled through - configure.</p> - <p> - There is a convinence module called msacc that has been - added to runtime_tools that can assist in gathering and - interpreting the data from Microstate accounting.</p> - <p> - For more information see <seealso - marker="erts:erlang#statistics_microstate_accounting">erlang:statistics(microstate_accounting, - _)</seealso> and the <seealso - marker="runtime_tools:msacc">msacc</seealso> module in - runtime_tools.</p> - <p> - Own Id: OTP-12345</p> - </item> - <item> - <p> - The port of Erlang/OTP to the realtime operating system - OSE has been removed.</p> - <p> - Own Id: OTP-12573</p> - </item> - <item> - <p> - Sharing preserved copy for messages and exit signals</p> - <p> - Enable sharing preserved copy with configure option - <c>--enable-sharing-preserving</c>. This will preserve - sharing, within the process, when communication with - other processes in the Erlang node. There is a trade-off, - the copy is more costly but this cost can be reclaimed if - there is a lot of sharing in the message. With this - feature enabled literals will not be copied in a send - except during a purge phase of the module where the - literals are located. This feature is considered - experimental in 19.0.</p> - <p> - Own Id: OTP-12590 Aux Id: OTP-10251 </p> - </item> - <item> - <p> - Halfword BEAM has been removed.</p> - <p> - Own Id: OTP-12883</p> - </item> - <item> - <p> - Added <seealso - marker="kernel:os#perf_counter/1">os:perf_counter/1</seealso>.</p> - <p> - The perf_counter is a very very cheap and high resolution - timer that can be used to timestamp system events. It - does not have monoticity guarantees, but should on most - OS's expose a monotonous time.</p> - <p> - Own Id: OTP-12908</p> - </item> - <item> - <p> - Support for a fragmented young heap generation. That is, - the young heap generation can consist of multiple non - continuous memory areas. The main reason for this change - is to avoid extra copying of messages that could not be - allocated directly on the receivers heap.</p> - <p> - Own Id: OTP-13047</p> - </item> - <item> - <p> - Erlang linked-in driver can now force the call to - open_port to block until a call to erl_drv_init_ack is - made inside the driver. This is useful when you want to - do some asynchronous initialization, for example getting - configuration from a pipe, and you want the initial - open_port call to fail if the configuration is incomplete - or wrong. See the erl_driver documentation for more - details on the API.</p> - <p> - Own Id: OTP-13086</p> - </item> - <item> - <p> - Erlang linked-in drivers can now set their own pid's as - seen in <c>erlang:port_info/1</c> by using the - <c>erl_drv_set_pid</c> function. For more details see the - erl_driver documentation.</p> - <p> - Own Id: OTP-13087</p> - </item> - <item> - <p> - The functionality behind <c>erlang:open_port/2</c> when - called with spawn or spawn_executable has been redone so - that the forking of the new program is done in a separate - process called erl_child_setup. This allows for a much - more robust implementation that uses less memory and does - not block the entire emulator if the program to be - started is on an un-accessible NFS. Benchmarks have shown - this approach to be about 3-5 times as fast as the old - approach where the fork/vfork was done by erts. This is a - pure stability and performance fix, however some error - messages may have changed, which is why it is marked as a - backwards incompatible change.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-13088</p> - </item> - <item> - <p>Improved yielding strategy in the implementation of - the following native functions:</p> <list> - <item><c>erlang:binary_to_list/1</c></item> - <item><c>erlang:binary_to_list/3</c></item> - <item><c>erlang:bitstring_to_list/1</c></item> - <item><c>erlang:list_to_binary/1</c></item> - <item><c>erlang:iolist_to_binary/1</c></item> - <item><c>erlang:list_to_bitstring/1</c></item> - <item><c>binary:list_to_bin/1</c></item> </list> <p>This - in order to improve performance of these functions.</p> - <p> - Own Id: OTP-13096</p> - </item> - <item> - <p> - All garbage collections of processes now bump reductions. - Also the amount of reductions bumped when garbage - collecting has been adjusted. It now better corresponds - to the amount of work performed. This in order to improve - the real time characteristics of the system.</p> - <p> - Own Id: OTP-13097</p> - </item> - <item> - <p>New functions that can load multiple functions at once - have been added to the '<c>code</c>' module. The - functions are <c>code:atomic_load/1</c>, - <c>code:prepare_loading/1</c>, - <c>code:finish_loading/1</c>, and - <c>code:ensure_modules_loaded/1</c>.</p> - <p> - Own Id: OTP-13111</p> - </item> - <item> - <p>The <c>-boot_var</c> option for <c>erl</c> now only - supports a single key and single value (as documented). - The option used to allow multiple key/value pairs, but - that behavior was undocumented.</p> - <p>The function <c>erl_prim_loader:start/3</c> has been - removed. Its documentation has also been removed.</p> - <p>The undocumented and unsupported function - <c>erl_prim_loader:get_files/2</c> has been removed.</p> - <p> - Own Id: OTP-13112</p> - </item> - <item> - <p> - Low level BIF <c>erlang:purge_module/1</c> is made more - robust against incorrect use. Lingering processes that - still refer the old code are now killed before the module - is purged to prevent fatal VM behavior.</p> - <p> - Own Id: OTP-13122</p> - </item> - <item> - <p> - Improved dirty scheduler implementation. For more - information see the <seealso - marker="erl_nif#dirty_nifs">NIF documentation</seealso>.</p> - <p> - Note that support for determining whether dirty NIF - support exist or not at compile time using the C - preprocessor macro <c>ERL_NIF_DIRTY_SCHEDULER_SUPPORT</c> - has been removed.</p> - <p> - Own Id: OTP-13123</p> - </item> - <item> - <p> - Various optimizations done to process dictionary access.</p> - <p> - Own Id: OTP-13167</p> - </item> - <item> - <p> - Added max_heap_size process flag. See erlang:process_flag - for more details.</p> - <p> - Own Id: OTP-13174</p> - </item> - <item> - <p> - Allow dynamic drivers and NIF libraries to be built with - gcc option <c>-fvisibility=hidden</c> for faster loading - and more optimized code.</p> - <p> - Own Id: OTP-13227</p> - </item> - <item> - <p> - Add <c>erlang:process_info(Pid, - garbage_collection_info)</c> which returns extended - garbage_collection information. For more details see the - documentation.</p> - <p> - Own Id: OTP-13265</p> - </item> - <item> - <p> - The functions <c>erlang:list_to_integer/1</c> and - <c>string:to_integer/1</c> have been optimized for large - inputs.</p> - <p> - Own Id: OTP-13293</p> - </item> - <item> - <p> - Improved memory allocation strategy for hipe native code - on x86_64 (amd64) architectures by reserving enough low - virtual address space needed for the HiPE/AMD64 small - code model. The default virtual address area for hipe - code is set to 512Mb, but can be changed with emulator - flag <c>+MXscs</c>.</p> - <p> - Own Id: OTP-13359</p> - </item> - <item> - <p> - Introduction of configurable management of data referred - to by the message queue of a process. Each process can be - configured individually.</p> - <p> - It is now possible to configure the message queue of a - process, so that all data referred by it will be kept - outside of the heap, and by this prevent this data from - being part of garbage collections.</p> - <p> - For more information see the documentation of <seealso - marker="erlang#process_flag_message_queue_data"><c>process_flag(message_queue_data, - MQD)</c></seealso>.</p> - <p> - Own Id: OTP-13366 Aux Id: OTP-13047 </p> - </item> - <item> - <p> - Processes now yield when scanning large message queues - and not finding a matching message. This in order to - improve real time characteristics.</p> - <p> - Own Id: OTP-13401</p> - </item> - <item> - <p> - Optimized an erts internal function that is used to - traverse erlang terms. The internal function was mainly - used by term_to_binary and comparison of terms. - Benchmarks have shown up to a 10% performance increase in - those functions after the optimization.</p> - <p> - Own Id: OTP-13440</p> - </item> - <item> - <p> - Add the following NIF API functions:</p> - <p> - <list> <item><seealso - marker="erl_nif#enif_cpu_time"><c>enif_cpu_time</c></seealso></item> - <item><seealso - marker="erl_nif#enif_now_time"><c>enif_now_time</c></seealso></item> - <item><seealso - marker="erl_nif#enif_make_unique_integer"><c>enif_make_unique_integer</c></seealso></item> - <item><seealso - marker="erl_nif#enif_is_process_alive"><c>enif_is_process_alive</c></seealso></item> - <item><seealso - marker="erl_nif#enif_is_port_alive"><c>enif_is_port_alive</c></seealso></item> - <item><seealso - marker="erl_nif#enif_term_to_binary"><c>enif_term_to_binary</c></seealso></item> - <item><seealso - marker="erl_nif#enif_binary_to_term"><c>enif_binary_to_term</c></seealso></item> - <item><seealso - marker="erl_nif#enif_port_command"><c>enif_port_command</c></seealso></item> - </list></p> - <p> - for details of what each function does, see the erl_nif - documentation.</p> - <p> - Own Id: OTP-13442</p> - </item> - <item> - <p> - Optimize <c>'++'</c> operator and <c>lists:append/2</c> - by using a single pass to build a new list while checking - for properness.</p> - <p> - Own Id: OTP-13487</p> - </item> - <item> - <p> - Handle terms (pids,ports and refs) from nodes with a - 'creation' value larger than 3. This is a preparation of - the distribution protocol to allow OTP 19 nodes to - correctly communicate with future nodes (20 or higher). - The 'creation' value differentiates different - incarnations of the same node (name).</p> - <p> - Own Id: OTP-13488</p> - </item> - <item> - <p> - Don't send unasked for systemd notifications in epmd</p> - <p> - Own Id: OTP-13493 Aux Id: PR-999 </p> - </item> - <item> - <p> - The enif_send API has been extended to allow NULL to be - used as the message environment. When used this way, a - message environent is implicitly created and the given - term is copied into that environment before sending. This - can be an optimization if many small messages are being - sent by the nif.</p> - <p> - Own Id: OTP-13495</p> - </item> - <item> - <p> - The tracing support has been extended to allow tracing on - ports. Ports can be traced on using the 'ports', 'send' - and 'receive' trace flags.</p> - <p> - The first argument of <seealso - marker="erts:erlang#trace/3">erlang:trace/3</seealso> has - been extended so that <c>'all'</c>, <c>'existing'</c> and - <c>'new'</c> now include both processes and ports. New - <c>Tracee</c> variants, <c>'all_processes'</c>, - <c>'all_ports'</c>, <c>'existing_processes'</c> etc have - been added to specify only processes or ports.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-13496</p> - </item> - <item> - <p> - When the <c>'procs'</c> trace flag is enabled, a - <c>'spawned'</c> trace event is now also generated by a - newly created process. The previous event <c>'spawn'</c> - remains, but as it is generated by the process that did - the spawn, it is not guaranteed that it is ordered with - other trace events from the newly spawned process. So - when tracking the lifetime of a process this new event - should be used as the creation event.</p> - <p> - This new trace event is marked as an incompatabiliy - because tools that expect certain trace events when - enabling 'procs' will have to updated.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-13497</p> - </item> - <item> - <p> - Add the <seealso - marker="erts:erlang#match_spec_test/3">erlang:match_spec_test/3</seealso> - function. The functions allows the testing of match - specifications for both tracing and ets tables. It can be - used to test that a match specification does the expected - filtering on specific data. It also returns more verbose - error reasons for incorrectly constructed match - specifications.</p> - <p> - Own Id: OTP-13501</p> - </item> - <item> - <p> - The erts internal tracing support has been changed to - have much less overhead and be more scalable.</p> - <p> - This rewrite does not break any backwards - incompatabilities, but it does change the ordering of - some trace messages when compared to previous releases. - It should be noted that this only applies to trace - messages sent to processes or ports, it does not apply to - the new tracer module. However in future releases they - may also be effected by this.</p> - <p> - Trace messages are only guaranteed to be ordered from one - traced process or port. In previous releases this was not - visible as a <c>'send'</c> trace message would always - arrive before the corresponding <c>'receive'</c> trace - message that is no longer always the case. This also - means that timestamped trace messages may seem to arrive - out of order as the timestamp is taken when the event is - triggered and not when it is put in the queue of the - tracer.</p> - <p> - Own Id: OTP-13503</p> - </item> - <item> - <p> - Add possibility to filter <c>send</c> and <c>receive</c> - trace with match specifications.</p> - <p> - Own Id: OTP-13507</p> - </item> - <item> - <p> - Add <c>maps:update_with/3,4</c> and <c>maps:take/2</c></p> - <p> - Own Id: OTP-13522 Aux Id: PR-1025 </p> - </item> - <item> - <p> - Introduce LTTng tracing via Erlang tracing.</p> - <p> - For LTTng to be enabled OTP needs to be built with - configure option <c>--with-dynamic-trace=lttng</c>.</p> - <p>The dynamic trace module <c>dyntrace</c> is now - capable to be used as a LTTng sink for Erlang tracing. - For a list of all tracepoints, see <seealso - marker="runtime_tools:LTTng">Runtime Tools User's - Guide</seealso> .</p> - <p>This feature also introduces an incompatible change in - trace tags. The trace tags <c>gc_start</c> and - <c>gc_end</c> has been split into <c>gc_minor_start</c>, - <c>gc_minor_end</c> and <c>gc_major_start</c>, - <c>gc_major_end</c>.</p> - <p> - *** POTENTIAL INCOMPATIBILITY ***</p> - <p> - Own Id: OTP-13532</p> - </item> - <item> - <p> - Print heap pointers for garbing processes during - crashdump</p> - <p> - Own Id: OTP-13541 Aux Id: PR-1026 </p> - </item> - <item> - <p> - Changed and improved low level memory statistics returned - by <c>erlang:system_info/1</c>. The info for - <c>erts_mmap</c> has been moved from <c>mseg_alloc</c> to - its own section returned by <c>{allocator, - erts_mmap}</c>.</p> - <p> - Own Id: OTP-13560</p> - </item> - <item> - <p> - Add enif_snprintf to the NIF API</p> - <p> - The fucntion <c>enif_snprintf</c> is similar to - <c>snprintf</c> call but can handle formating of Erlang - terms via <c>%T</c> format specifier.</p> - <p> - Own Id: OTP-13580</p> - </item> - <item> - <p>The warning in the documentation for - <c>erlang:raise/3</c> has been removed. It is now - officially perfectly fine to use raise/3 in production - code. (Thanks to Per Hedeland.)</p> - <p> - Own Id: OTP-13599</p> - </item> - <item> - <p> - Add <c>-start_epmd</c> command line option, this lets you - disable automatic starting of epmd when starting a - distributed node.</p> - <p> - Add <c>-epmd_module</c> command line option, this lets - you specify a module to register and lookup node names - in. The default module is <c>erl_epmd</c>.</p> - <p> - Own Id: OTP-13627</p> - </item> - <item> - <p> - <c>erlang:halt</c> now truncates strings longer than 200 - characters instead of failing with <c>badarg</c>.</p> - <p> - Own Id: OTP-13630</p> - </item> - </list> - </section> - -</section> - <section><title>Erts 7.3.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index 0a59f8785c..4716460a6b 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -5318,10 +5318,14 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) ASSERT(BeamOp(op_call_nif) == (BeamInstr *) *I); - if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p) - && (ERTS_TRACE_FLAGS(c_p) & F_SENSITIVE) == 0) { - c_p->fcalls = REDS_IN(c_p) = 0; - } + /* + * Set fcalls even though we ignore it, so we don't + * confuse code accessing it... + */ + if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) + c_p->fcalls = 0; + else + c_p->fcalls = CONTEXT_REDS; SWAPIN; @@ -5341,7 +5345,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) NULL, fun_buf); } else { erts_snprintf(fun_buf, sizeof(DTRACE_CHARBUF_NAME(fun_buf)), - "<unknown/%p>", next); + "<unknown/%p>", *I); } } @@ -5351,8 +5355,10 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) } { - Eterm nif_bif_result; - Eterm bif_nif_arity; +#ifdef DEBUG + Eterm result; +#endif + Eterm arity; { /* @@ -5374,7 +5380,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) c_p->current = I-3; /* current and vbf set to please handle_error */ SWAPOUT; PROCESS_MAIN_CHK_LOCKS(c_p); - bif_nif_arity = I[-1]; + arity = I[-1]; ERTS_SMP_UNREQ_PROC_MAIN_LOCK(c_p); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); @@ -5383,50 +5389,39 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) NifF* fp = vbf = (NifF*) I[1]; struct enif_environment_t env; ASSERT(!c_p->scheduler_data); - erts_pre_dirty_nif(esdp, &env, c_p, (struct erl_module_nif*)I[2], NULL); - nif_bif_result = (*fp)(&env, bif_nif_arity, reg); - if (env.exception_thrown) - nif_bif_result = THE_NON_VALUE; + + erts_pre_dirty_nif(esdp, &env, c_p, + (struct erl_module_nif*)I[2], NULL); + +#ifdef DEBUG + result = +#else + (void) +#endif + (*fp)(&env, arity, reg); + erts_post_nif(&env); + + ASSERT(!is_value(result)); + ASSERT(c_p->freason == TRAP); + ASSERT(!(c_p->flags & F_HIBERNATE_SCHED)); + PROCESS_MAIN_CHK_LOCKS(c_p); + ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); ERTS_MSACC_SET_STATE_CACHED_M_X(ERTS_MSACC_STATE_EMULATOR); - if (env.exiting) { - ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); + if (env.exiting) goto do_dirty_schedule; - } ASSERT(!ERTS_PROC_IS_EXITING(c_p)); } + DTRACE_NIF_RETURN(c_p, (Eterm)I[-3], (Eterm)I[-2], (Uint)I[-1]); - ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p); ERTS_HOLE_CHECK(c_p); - if (ERTS_IS_GC_DESIRED(c_p)) { - nif_bif_result = erts_gc_after_bif_call(c_p, - nif_bif_result, - reg, bif_nif_arity); - } - SWAPIN; /* There might have been a garbage collection. */ - if (is_value(nif_bif_result)) { - r(0) = nif_bif_result; - CHECK_TERM(r(0)); - I = c_p->cp; - c_p->cp = 0; - Goto(*I); - } else if (c_p->freason == TRAP) { - I = c_p->i; - ASSERT(!(c_p->flags & F_HIBERNATE_SCHED)); - goto context_switch; - } - I = handle_error(c_p, c_p->cp, reg, vbf); + SWAPIN; + I = c_p->i; + goto context_switch; } } - if (I == 0) { - goto do_dirty_schedule; - } else { - ASSERT(!is_value(r(0))); - SWAPIN; - goto context_switch; - } #endif /* ERTS_DIRTY_SCHEDULERS */ } diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index b18910e2c7..fc14061a44 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -282,20 +282,17 @@ res_no_proc: { } } -#define ERTS_DEMONITOR_FALSE 2 -#define ERTS_DEMONITOR_TRUE 1 -#define ERTS_DEMONITOR_BADARG 0 -#define ERTS_DEMONITOR_YIELD_TRUE -1 -#define ERTS_DEMONITOR_INTERNAL_ERROR -2 - -static int +/* This function is allowed to return range of values handled by demonitor/1-2 + * Namely: atoms true, false, yield, internal_error, badarg or THE_NON_VALUE + */ +static Eterm remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to) { ErtsDSigData dsd; ErtsMonitor *dmon; ErtsMonitor *mon; int code; - int res; + Eterm res = am_false; #ifndef ERTS_SMP int stale_mon = 0; #endif @@ -328,7 +325,7 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to) mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref); erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_LINK); - res = ERTS_DEMONITOR_TRUE; + res = am_true; break; case ERTS_DSIG_PREP_CONNECTED: @@ -352,7 +349,7 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to) * This is possible when smp support is enabled. * 'DOWN' message just arrived. */ - res = ERTS_DEMONITOR_TRUE; + res = am_true; } else { /* @@ -367,16 +364,13 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to) : mon->pid), ref, 0); - res = (code == ERTS_DSIG_SEND_YIELD - ? ERTS_DEMONITOR_YIELD_TRUE - : ERTS_DEMONITOR_TRUE); + res = (code == ERTS_DSIG_SEND_YIELD ? am_yield : am_true); erts_destroy_monitor(dmon); - } break; default: ASSERT(! "Invalid dsig prepare result"); - return ERTS_DEMONITOR_INTERNAL_ERROR; + return am_internal_error; } #ifndef ERTS_SMP @@ -404,27 +398,96 @@ remote_demonitor(Process *c_p, DistEntry *dep, Eterm ref, Eterm to) return res; } -static int demonitor(Process *c_p, Eterm ref, Eterm *multip) +static ERTS_INLINE void +demonitor_local_process(Process *c_p, Eterm ref, Eterm to, Eterm *res) +{ + Process *rp = erts_pid2proc_opt(c_p, + ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, + to, + ERTS_PROC_LOCK_LINK, + ERTS_P2P_FLG_ALLOW_OTHER_X); + ErtsMonitor *mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref); + +#ifndef ERTS_SMP + ASSERT(mon); +#else + if (!mon) + *res = am_false; + else +#endif + { + *res = am_true; + erts_destroy_monitor(mon); + } + if (rp) { + ErtsMonitor *rmon; + rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref); + if (rp != c_p) + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); + if (rmon != NULL) + erts_destroy_monitor(rmon); + } + else { + ERTS_SMP_ASSERT_IS_NOT_EXITING(c_p); + } +} + +static ERTS_INLINE BIF_RETTYPE +demonitor_local_port(Process *origin, Eterm ref, Eterm target) { - ErtsMonitor *mon = NULL; /* The monitor entry to delete */ - Process *rp; /* Local target process */ - Eterm to = NIL; /* Monitor link traget */ - DistEntry *dep = NULL; /* Target's distribution entry */ - int deref_de = 0; - int res; - int unlock_link = 1; + BIF_RETTYPE res = am_false; + Port *port = erts_port_lookup_raw(target); + + if (!port) { + BIF_ERROR(origin, BADARG); + } + erts_smp_proc_unlock(origin, ERTS_PROC_LOCK_LINK); + + if (port) { + Eterm trap_ref; + switch (erts_port_demonitor(origin, ERTS_PORT_DEMONITOR_NORMAL, + port, ref, &trap_ref)) { + case ERTS_PORT_OP_DROPPED: + case ERTS_PORT_OP_BADARG: + break; + case ERTS_PORT_OP_SCHEDULED: + BIF_TRAP3(await_port_send_result_trap, origin, trap_ref, + am_busy_port, am_true); + /* the busy_port atom will never be returned, because it cannot be + * returned from erts_port_(de)monitor, but just in case if in future + * internal API changes - you may see this atom */ + default: + break; + } + } + else { + ERTS_SMP_ASSERT_IS_NOT_EXITING(origin); + } + BIF_RET(res); +} +/* Can return atom true, false, yield, internal_error, badarg or + * THE_NON_VALUE if error occured or trap has been set up + */ +static +BIF_RETTYPE demonitor(Process *c_p, Eterm ref, Eterm *multip) +{ + ErtsMonitor *mon = NULL; /* The monitor entry to delete */ + Eterm to = NIL; /* Monitor link traget */ + DistEntry *dep = NULL; /* Target's distribution entry */ + int deref_de = 0; + BIF_RETTYPE res = am_false; + int unlock_link = 1; erts_smp_proc_lock(c_p, ERTS_PROC_LOCK_LINK); if (is_not_internal_ref(ref)) { - res = ERTS_DEMONITOR_BADARG; + res = am_badarg; goto done; /* Cannot be this monitor's ref */ } mon = erts_lookup_monitor(ERTS_P_MONITORS(c_p), ref); if (!mon) { - res = ERTS_DEMONITOR_FALSE; goto done; } @@ -432,70 +495,50 @@ static int demonitor(Process *c_p, Eterm ref, Eterm *multip) case MON_TIME_OFFSET: *multip = am_true; erts_demonitor_time_offset(ref); - res = ERTS_DEMONITOR_TRUE; + res = am_true; break; case MON_ORIGIN: to = mon->pid; *multip = am_false; if (is_atom(to)) { - /* Monitoring a name at node to */ - ASSERT(is_node_name_atom(to)); - dep = erts_sysname_to_connected_dist_entry(to); - ASSERT(dep != erts_this_dist_entry); - if (dep) - deref_de = 1; + /* Monitoring a name at node to */ + ASSERT(is_node_name_atom(to)); + dep = erts_sysname_to_connected_dist_entry(to); + ASSERT(dep != erts_this_dist_entry); + if (dep) + deref_de = 1; + } else if (is_port(to)) { + if (port_dist_entry(to) != erts_this_dist_entry) { + goto badarg; + } + res = demonitor_local_port(c_p, ref, to); + unlock_link = 0; + goto done; } else { - ASSERT(is_pid(to)); - dep = pid_dist_entry(to); + ASSERT(is_pid(to)); + dep = pid_dist_entry(to); } if (dep != erts_this_dist_entry) { - res = remote_demonitor(c_p, dep, ref, to); - /* remote_demonitor() unlocks link lock on c_p */ - unlock_link = 0; + res = remote_demonitor(c_p, dep, ref, to); + /* remote_demonitor() unlocks link lock on c_p */ + unlock_link = 0; } else { /* Local monitor */ - if (deref_de) { - deref_de = 0; - erts_deref_dist_entry(dep); - } - dep = NULL; - rp = erts_pid2proc_opt(c_p, - ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, - to, - ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X); - mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref); -#ifndef ERTS_SMP - ASSERT(mon); -#else - if (!mon) - res = ERTS_DEMONITOR_FALSE; - else -#endif - { - res = ERTS_DEMONITOR_TRUE; - erts_destroy_monitor(mon); - } - if (rp) { - ErtsMonitor *rmon; - rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref); - if (rp != c_p) - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - if (rmon != NULL) - erts_destroy_monitor(rmon); - } - else { - ERTS_SMP_ASSERT_IS_NOT_EXITING(c_p); - } - + if (deref_de) { + deref_de = 0; + erts_deref_dist_entry(dep); + } + dep = NULL; + demonitor_local_process(c_p, ref, to, &res); } break; - default: - res = ERTS_DEMONITOR_BADARG; + default /* case */ : +badarg: + res = am_badarg; /* will be converted to error by caller */ *multip = am_false; break; } - done: +done: if (unlock_link) erts_smp_proc_unlock(c_p, ERTS_PROC_LOCK_LINK); @@ -506,21 +549,20 @@ static int demonitor(Process *c_p, Eterm ref, Eterm *multip) } ERTS_SMP_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p)); - return res; + BIF_RET(res); } BIF_RETTYPE demonitor_1(BIF_ALIST_1) { Eterm multi; switch (demonitor(BIF_P, BIF_ARG_1, &multi)) { - case ERTS_DEMONITOR_FALSE: - case ERTS_DEMONITOR_TRUE: - BIF_RET(am_true); - case ERTS_DEMONITOR_YIELD_TRUE: - ERTS_BIF_YIELD_RETURN(BIF_P, am_true); - case ERTS_DEMONITOR_BADARG: - BIF_ERROR(BIF_P, BADARG); - case ERTS_DEMONITOR_INTERNAL_ERROR: + case am_false: + case am_true: BIF_RET(am_true); + case THE_NON_VALUE: BIF_RET(THE_NON_VALUE); + case am_yield: ERTS_BIF_YIELD_RETURN(BIF_P, am_true); + case am_badarg: BIF_ERROR(BIF_P, BADARG); + + case am_internal_error: default: ASSERT(! "demonitor(): internal error"); BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); @@ -529,11 +571,11 @@ BIF_RETTYPE demonitor_1(BIF_ALIST_1) BIF_RETTYPE demonitor_2(BIF_ALIST_2) { - Eterm res = am_true; - Eterm multi = am_false; - int info = 0; - int flush = 0; - Eterm list = BIF_ARG_2; + BIF_RETTYPE res = am_true; + Eterm multi = am_false; + int info = 0; + int flush = 0; + Eterm list = BIF_ARG_2; while (is_list(list)) { Eterm* consp = list_val(list); @@ -554,24 +596,27 @@ BIF_RETTYPE demonitor_2(BIF_ALIST_2) goto badarg; switch (demonitor(BIF_P, BIF_ARG_1, &multi)) { - case ERTS_DEMONITOR_FALSE: + case THE_NON_VALUE: + /* If other error occured or trap has been set up - pass through */ + BIF_RET(THE_NON_VALUE); + case am_false: if (info) res = am_false; if (flush) { - flush_messages: +flush_messages: BIF_TRAP3(flush_monitor_messages_trap, BIF_P, BIF_ARG_1, multi, res); } - case ERTS_DEMONITOR_TRUE: + case am_true: if (multi == am_true && flush) goto flush_messages; BIF_RET(res); - case ERTS_DEMONITOR_YIELD_TRUE: + case am_yield: ERTS_BIF_YIELD_RETURN(BIF_P, am_true); - case ERTS_DEMONITOR_BADARG: - badarg: + case am_badarg: +badarg: BIF_ERROR(BIF_P, BADARG); - case ERTS_DEMONITOR_INTERNAL_ERROR: + case am_internal_error: default: ASSERT(! "demonitor(): internal error"); BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); @@ -615,14 +660,13 @@ erts_queue_monitor_message(Process *p, erts_queue_message(p, *p_locksp, msgp, tup, am_system); } -static BIF_RETTYPE +static Eterm local_pid_monitor(Process *p, Eterm target, Eterm mon_ref, int boolean) { - BIF_RETTYPE ret; - Process *rp; + Eterm ret = mon_ref; + Process *rp; ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK; - ERTS_BIF_PREP_RET(ret, mon_ref); if (target == p->common.id) { return ret; } @@ -658,40 +702,112 @@ local_pid_monitor(Process *p, Eterm target, Eterm mon_ref, int boolean) } static BIF_RETTYPE -local_name_monitor(Process *p, Eterm target_name) +local_port_monitor(Process *origin, Eterm target) { - BIF_RETTYPE ret; - Eterm mon_ref; - ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK; - Process *rp; + BIF_RETTYPE ref = erts_make_ref(origin); + Port *port = erts_sig_lookup_port(origin, target); + ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN; - mon_ref = erts_make_ref(p); - ERTS_BIF_PREP_RET(ret, mon_ref); - erts_smp_proc_lock(p, ERTS_PROC_LOCK_LINK); - rp = erts_whereis_process(p, p_locks, target_name, ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X); - if (!rp) { - DeclareTmpHeap(lhp,3,p); + if (!port) { +res_no_proc: + /* Send the DOWN message immediately. Ref is made on the fly because + * caller has never seen it yet. */ + erts_queue_monitor_message(origin, &p_locks, ref, + am_port, target, am_noproc); + } + else { + switch (erts_port_monitor(origin, port, target, &ref)) { + case ERTS_PORT_OP_DROPPED: + case ERTS_PORT_OP_BADARG: + goto res_no_proc; + case ERTS_PORT_OP_SCHEDULED: + BIF_TRAP3(await_port_send_result_trap, origin, ref, + am_busy_port, ref); + /* the busy_port atom will never be returned, because it cannot be + * returned from erts_port_monitor, but just in case if in future + * internal API changes - you may see this atom */ + default: + break; + } + } + erts_smp_proc_unlock(origin, p_locks & ~ERTS_PROC_LOCK_MAIN); + BIF_RET(ref); +} + +/* Type = process | port :: atom(), 1st argument passed to erlang:monitor/2 + */ +static BIF_RETTYPE +local_name_monitor(Process *self, Eterm type, Eterm target_name) +{ + BIF_RETTYPE ret = erts_make_ref(self); + + ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK; + Process *proc = NULL; + Port *port = NULL; + + erts_smp_proc_lock(self, ERTS_PROC_LOCK_LINK); + + erts_whereis_name(self, p_locks, target_name, + &proc, ERTS_PROC_LOCK_LINK, + ERTS_P2P_FLG_ALLOW_OTHER_X, + &port, 0); + + /* If the name is not registered, + * or if we asked for proc and got a port, + * or if we asked for port and got a proc, + * we just send the 'DOWN' message. + */ + if ((!proc && !port) || + (type == am_process && port) || + (type == am_port && proc)) { + DeclareTmpHeap(lhp,3,self); Eterm item; - UseTmpHeap(3,p); - erts_smp_proc_unlock(p, ERTS_PROC_LOCK_LINK); + UseTmpHeap(3,self); + + erts_smp_proc_unlock(self, ERTS_PROC_LOCK_LINK); p_locks &= ~ERTS_PROC_LOCK_LINK; + item = TUPLE2(lhp, target_name, erts_this_dist_entry->sysname); - erts_queue_monitor_message(p, &p_locks, - mon_ref, am_process, item, am_noproc); - UnUseTmpHeap(3,p); - } - else if (rp != p) { - erts_add_monitor(&ERTS_P_MONITORS(p), MON_ORIGIN, mon_ref, rp->common.id, - target_name); - erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, mon_ref, p->common.id, - target_name); - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); + erts_queue_monitor_message(self, &p_locks, + ret, + type, /* = process|port :: atom() */ + item, am_noproc); + UnUseTmpHeap(3,self); + } + else if (port) { + erts_smp_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN); + p_locks &= ~ERTS_PROC_LOCK_MAIN; + + switch (erts_port_monitor(self, port, target_name, &ret)) { + case ERTS_PORT_OP_DONE: + return ret; + case ERTS_PORT_OP_SCHEDULED: { /* Scheduled a signal */ + ASSERT(is_internal_ref(ret)); + BIF_TRAP3(await_port_send_result_trap, self, + ret, am_true, ret); + /* bif_trap returns */ + } break; + default: + goto badarg; + } + } + else if (proc != self) { + erts_add_monitor(&ERTS_P_MONITORS(self), MON_ORIGIN, ret, + proc->common.id, target_name); + erts_add_monitor(&ERTS_P_MONITORS(proc), MON_TARGET, ret, + self->common.id, target_name); + erts_smp_proc_unlock(proc, ERTS_PROC_LOCK_LINK); } - erts_smp_proc_unlock(p, p_locks & ~ERTS_PROC_LOCK_MAIN); - - return ret; + if (p_locks) { + erts_smp_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN); + } + BIF_RET(ret); +badarg: + if (p_locks) { + erts_smp_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN); + } + BIF_ERROR(self, BADARG); } static BIF_RETTYPE @@ -758,7 +874,7 @@ remote_monitor(Process *p, Eterm bifarg1, Eterm bifarg2, break; } - return ret; + BIF_RET(ret); } BIF_RETTYPE monitor_2(BIF_ALIST_2) @@ -772,8 +888,9 @@ BIF_RETTYPE monitor_2(BIF_ALIST_2) switch (BIF_ARG_1) { case am_time_offset: { Eterm ref; - if (BIF_ARG_2 != am_clock_service) - goto error; + if (BIF_ARG_2 != am_clock_service) { + goto badarg; + } ref = erts_make_ref(BIF_P); erts_smp_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK); erts_add_monitor(&ERTS_P_MONITORS(BIF_P), MON_TIME_OFFSET, @@ -783,46 +900,57 @@ BIF_RETTYPE monitor_2(BIF_ALIST_2) BIF_RET(ref); } case am_process: + case am_port: break; default: - goto error; + goto badarg; } - if (is_internal_pid(target)) { - local_pid: - ret = local_pid_monitor(BIF_P, target, erts_make_ref(BIF_P), 0); - } else if (is_external_pid(target)) { + if (is_internal_pid(target) && BIF_ARG_1 == am_process) { +local_pid: + ret = local_pid_monitor(BIF_P, target, erts_make_ref(BIF_P), 0); + } else if (is_external_pid(target) && BIF_ARG_1 == am_process) { dep = external_pid_dist_entry(target); if (dep == erts_this_dist_entry) goto local_pid; ret = remote_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, dep, target, 0); + } else if (is_internal_port(target) && BIF_ARG_1 == am_port) { +local_port: + ret = local_port_monitor(BIF_P, target); + } else if (is_external_port(target) && BIF_ARG_1 == am_port) { + dep = external_port_dist_entry(target); + if (dep == erts_this_dist_entry) { + goto local_port; + } + goto badarg; /* No want remote port */ } else if (is_atom(target)) { - ret = local_name_monitor(BIF_P, target); + ret = local_name_monitor(BIF_P, BIF_ARG_1, target); } else if (is_tuple(target)) { Eterm *tp = tuple_val(target); Eterm remote_node; Eterm name; - if (arityval(*tp) != 2) - goto error; + if (arityval(*tp) != 2) { + goto badarg; + } remote_node = tp[2]; name = tp[1]; if (!is_atom(remote_node) || !is_atom(name)) { - goto error; + goto badarg; } if (!erts_is_alive && remote_node != am_Noname) { - goto error; /* Remote monitor from (this) undistributed node */ + goto badarg; /* Remote monitor from (this) undistributed node */ } dep = erts_sysname_to_connected_dist_entry(remote_node); if (dep == erts_this_dist_entry) { deref_de = 1; - ret = local_name_monitor(BIF_P, name); + ret = local_name_monitor(BIF_P, BIF_ARG_1, name); } else { if (dep) deref_de = 1; ret = remote_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, dep, name, 1); } } else { - error: +badarg: ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG); } if (deref_de) { diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index b410578d37..3fb866733c 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -361,8 +361,13 @@ erts_print_system_version(int to, void *arg, Process *c_p) } typedef struct { + /* {Entity,Node} = {monitor.Name,monitor.Pid} for external by name + * {Entity,Node} = {monitor.Pid,NIL} for external/external by pid + * {Entity,Node} = {monitor.Name,erlang:node()} for internal by name */ Eterm entity; Eterm node; + /* pid is actual target being monitored, no matter pid/port or name */ + Eterm pid; } MonitorInfo; typedef struct { @@ -420,21 +425,27 @@ static void collect_one_origin_monitor(ErtsMonitor *mon, void *vmicp) EXTEND_MONITOR_INFOS(micp); if (is_atom(mon->pid)) { /* external by name */ micp->mi[micp->mi_i].entity = mon->name; - micp->mi[micp->mi_i].node = mon->pid; - micp->sz += 3; /* need one 2-tuple */ + micp->mi[micp->mi_i].node = mon->pid; + micp->sz += 3; /* need one 2-tuple */ } else if (is_external_pid(mon->pid)) { /* external by pid */ micp->mi[micp->mi_i].entity = mon->pid; - micp->mi[micp->mi_i].node = NIL; - micp->sz += NC_HEAP_SIZE(mon->pid); + micp->mi[micp->mi_i].node = NIL; + micp->sz += NC_HEAP_SIZE(mon->pid); } else if (!is_nil(mon->name)) { /* internal by name */ micp->mi[micp->mi_i].entity = mon->name; - micp->mi[micp->mi_i].node = erts_this_dist_entry->sysname; - micp->sz += 3; /* need one 2-tuple */ + micp->mi[micp->mi_i].node = erts_this_dist_entry->sysname; + micp->sz += 3; /* need one 2-tuple */ } else { /* internal by pid */ micp->mi[micp->mi_i].entity = mon->pid; - micp->mi[micp->mi_i].node = NIL; + micp->mi[micp->mi_i].node = NIL; /* no additional heap space needed */ } + + /* have always pid at hand, to assist with figuring out if its a port or + * a process, when we monitored by name and process_info is requested. + * See: erl_bif_info.c:process_info_aux section for am_monitors */ + micp->mi[micp->mi_i].pid = mon->pid; + micp->mi_i++; micp->sz += 2 + 3; /* For a cons cell and a 2-tuple */ } @@ -1190,37 +1201,49 @@ process_info_aux(Process *BIF_P, case am_monitors: { MonitorInfoCollection mic; - int i; + int i; INIT_MONITOR_INFOS(mic); - erts_doforall_monitors(ERTS_P_MONITORS(rp),&collect_one_origin_monitor,&mic); - hp = HAlloc(BIF_P, 3 + mic.sz); + erts_doforall_monitors(ERTS_P_MONITORS(rp), + &collect_one_origin_monitor, &mic); + hp = HAlloc(BIF_P, 3 + mic.sz); res = NIL; for (i = 0; i < mic.mi_i; i++) { if (is_atom(mic.mi[i].entity)) { /* Monitor by name. - * Build {process, {Name, Node}} and cons it. + * Build {process|port, {Name, Node}} and cons it. */ Eterm t1, t2; + /* If pid is an atom, then it is a remote named monitor, which + has to be a process */ + Eterm m_type = is_port(mic.mi[i].pid) ? am_port : am_process; + ASSERT(is_pid(mic.mi[i].pid) + || is_port(mic.mi[i].pid) + || is_atom(mic.mi[i].pid)); t1 = TUPLE2(hp, mic.mi[i].entity, mic.mi[i].node); hp += 3; - t2 = TUPLE2(hp, am_process, t1); + t2 = TUPLE2(hp, m_type, t1); hp += 3; res = CONS(hp, t2, res); - hp += 2; + hp += 2; } else { - /* Monitor by pid. Build {process, Pid} and cons it. */ + /* Monitor by pid. Build {process|port, Pid} and cons it. */ Eterm t; Eterm pid = STORE_NC(&hp, &MSO(BIF_P), mic.mi[i].entity); - t = TUPLE2(hp, am_process, pid); + + Eterm m_type = is_port(mic.mi[i].pid) ? am_port : am_process; + ASSERT(is_pid(mic.mi[i].pid) + || is_port(mic.mi[i].pid)); + + t = TUPLE2(hp, m_type, pid); hp += 3; res = CONS(hp, t, res); - hp += 2; + hp += 2; } } - DESTROY_MONITOR_INFOS(mic); + DESTROY_MONITOR_INFOS(mic); break; } @@ -2880,7 +2903,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) */ Eterm -erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm item) +erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, + Eterm item) { Eterm res = THE_NON_VALUE; @@ -2928,8 +2952,8 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm ite Eterm item; INIT_MONITOR_INFOS(mic); - - erts_doforall_monitors(ERTS_P_MONITORS(prt), &collect_one_origin_monitor, &mic); + erts_doforall_monitors(ERTS_P_MONITORS(prt), + &collect_one_origin_monitor, &mic); if (szp) *szp += mic.sz; @@ -2938,14 +2962,16 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm ite res = NIL; for (i = 0; i < mic.mi_i; i++) { Eterm t; - item = STORE_NC(hpp, ohp, mic.mi[i].entity); - t = TUPLE2(*hpp, am_process, item); + Eterm m_type; + + item = STORE_NC(hpp, ohp, mic.mi[i].entity); + m_type = is_port(item) ? am_port : am_process; + t = TUPLE2(*hpp, m_type, item); *hpp += 3; res = CONS(*hpp, t, res); *hpp += 2; } - } - + } // hpp DESTROY_MONITOR_INFOS(mic); if (szp) { @@ -2953,6 +2979,32 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm ite goto done; } } + else if (item == am_monitored_by) { + MonitorInfoCollection mic; + int i; + Eterm item; + + INIT_MONITOR_INFOS(mic); + erts_doforall_monitors(ERTS_P_MONITORS(prt), + &collect_one_target_monitor, &mic); + if (szp) + *szp += mic.sz; + + if (hpp) { + res = NIL; + for (i = 0; i < mic.mi_i; ++i) { + item = STORE_NC(hpp, ohp, mic.mi[i].entity); + res = CONS(*hpp, item, res); + *hpp += 2; + } + } // hpp + DESTROY_MONITOR_INFOS(mic); + + if (szp) { + res = am_true; + goto done; + } + } else if (item == am_name) { int count = sys_strlen(prt->name); diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index 37f4e1de49..90e78a9b0b 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -139,6 +139,12 @@ sig_lookup_port(Process *c_p, Eterm id_or_name) return lookup_port(c_p, id_or_name, ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); } +/* Non-inline copy of sig_lookup_port to be exported */ +Port *erts_sig_lookup_port(Process *c_p, Eterm id_or_name) +{ + return lookup_port(c_p, id_or_name, ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP); +} + static ERTS_INLINE Port * data_lookup_port(Process *c_p, Eterm id_or_name) { @@ -1411,7 +1417,7 @@ BIF_RETTYPE decode_packet_3(BIF_ALIST_3) trunc_len = val; goto next_option; case am_line_delimiter: - if (type == TCP_PB_LINE_LF && val >= 0 && val <= 255) { + if (type == TCP_PB_LINE_LF && val <= 255) { delimiter = (char)val; goto next_option; } diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c index 074ac6d64e..74979f984a 100644 --- a/erts/emulator/beam/erl_db_hash.c +++ b/erts/emulator/beam/erl_db_hash.c @@ -95,8 +95,7 @@ /* * The following symbols can be manipulated to "tune" the linear hash array */ -#define GROW_LIMIT(NACTIVE) ((NACTIVE)*2) -#define SHRINK_LIMIT(NACTIVE) ((NACTIVE) / 2) +#define CHAIN_LEN 6 /* Medium bucket chain len */ /* Number of slots per segment */ #define SEGSZ_EXP 8 @@ -464,7 +463,7 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle); static ERTS_INLINE void try_shrink(DbTableHash* tb) { int nactive = NACTIVE(tb); - if (nactive > SEGSZ && NITEMS(tb) < SHRINK_LIMIT(nactive) + if (nactive > SEGSZ && NITEMS(tb) < (nactive * CHAIN_LEN) && !IS_FIXED(tb)) { shrink(tb, nactive); } @@ -671,8 +670,8 @@ int db_create_hash(Process *p, DbTable *tbl) tb->nsegs = NSEG_1; tb->nslots = SEGSZ; -#ifdef ERTS_SMP erts_smp_atomic_init_nob(&tb->is_resizing, 0); +#ifdef ERTS_SMP if (tb->common.type & DB_FINE_LOCKED) { erts_smp_rwmtx_opt_t rwmtx_opt = ERTS_SMP_RWMTX_OPT_DEFAULT_INITER; int i; @@ -863,7 +862,7 @@ Lnew: WUNLOCK_HASH(lck); { int nactive = NACTIVE(tb); - if (nitems > GROW_LIMIT(nactive) && !IS_FIXED(tb)) { + if (nitems > nactive * (CHAIN_LEN+1) && !IS_FIXED(tb)) { grow(tb, nactive); } } @@ -2251,12 +2250,12 @@ static int db_free_table_continue_hash(DbTable *tbl) done /= 2; while(tb->nslots != 0) { - done += 1 + SEGSZ/64 + free_seg(tb, 1); + free_seg(tb, 1); /* * If we have done enough work, get out here. */ - if (done >= DELETE_RECORD_LIMIT) { + if (++done >= (DELETE_RECORD_LIMIT / CHAIN_LEN / SEGSZ)) { return 0; /* Not done */ } } @@ -2605,22 +2604,23 @@ static Eterm build_term_list(Process* p, HashDbTerm* ptr1, HashDbTerm* ptr2, static ERTS_INLINE int begin_resizing(DbTableHash* tb) { -#ifdef ERTS_SMP if (DB_USING_FINE_LOCKING(tb)) - return !erts_atomic_xchg_acqb(&tb->is_resizing, 1); - else - ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&tb->common.rwlock)); -#endif - return 1; + return !erts_smp_atomic_xchg_acqb(&tb->is_resizing, 1); + else { + if (erts_smp_atomic_read_nob(&tb->is_resizing)) + return 0; + erts_smp_atomic_set_nob(&tb->is_resizing, 1); + return 1; + } } static ERTS_INLINE void done_resizing(DbTableHash* tb) { -#ifdef ERTS_SMP if (DB_USING_FINE_LOCKING(tb)) - erts_atomic_set_relb(&tb->is_resizing, 0); -#endif + erts_smp_atomic_set_relb(&tb->is_resizing, 0); + else + erts_smp_atomic_set_nob(&tb->is_resizing, 0); } /* Grow table with one new bucket. @@ -2871,7 +2871,7 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj, int nitems = erts_smp_atomic_inc_read_nob(&tb->common.nitems); int nactive = NACTIVE(tb); - if (nitems > GROW_LIMIT(nactive) && !IS_FIXED(tb)) { + if (nitems > nactive * (CHAIN_LEN + 1) && !IS_FIXED(tb)) { grow(tb, nactive); } } diff --git a/erts/emulator/beam/erl_db_hash.h b/erts/emulator/beam/erl_db_hash.h index 081ff8fafc..e654363cd5 100644 --- a/erts/emulator/beam/erl_db_hash.h +++ b/erts/emulator/beam/erl_db_hash.h @@ -60,8 +60,8 @@ typedef struct db_table_hash { /* List of slots where elements have been deleted while table was fixed */ erts_smp_atomic_t fixdel; /* (FixedDeletion*) */ erts_smp_atomic_t nactive; /* Number of "active" slots */ -#ifdef ERTS_SMP erts_smp_atomic_t is_resizing; /* grow/shrink in progress */ +#ifdef ERTS_SMP DbTableHashFineLocks* locks; #endif #ifdef VALGRIND diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index 4fd82bad10..039f97ef43 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -2658,18 +2658,21 @@ done: } int -enif_is_on_dirty_scheduler(ErlNifEnv* env) +enif_thread_type(void) { - int scheduler; - Process *c_p; + ErtsSchedulerData *esdp = erts_get_scheduler_data(); - execution_state(env, &c_p, &scheduler); + if (!esdp) + return ERL_NIF_THR_UNDEFINED; + + if (!ERTS_SCHEDULER_IS_DIRTY(esdp)) + return ERL_NIF_THR_NORMAL_SCHEDULER; - if (!c_p || !scheduler) - erts_exit(ERTS_ABORT_EXIT, "enif_is_on_dirty_scheduler: " - "Invalid env"); + if (ERTS_SCHEDULER_IS_DIRTY_CPU(esdp)) + return ERL_NIF_THR_DIRTY_CPU_SCHEDULER; - return scheduler < 0; + ASSERT(ERTS_SCHEDULER_IS_DIRTY_IO(esdp)); + return ERL_NIF_THR_DIRTY_IO_SCHEDULER; } /* Maps */ diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index da7a754757..494971e118 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -209,6 +209,17 @@ typedef enum { ERL_NIF_BIN2TERM_SAFE = 0x20000000 } ErlNifBinaryToTerm; +/* + * Return values from enif_thread_type(). Negative values + * reserved for specific types of non-scheduler threads. + * Positive values reserved for scheduler thread types. + */ + +#define ERL_NIF_THR_UNDEFINED 0 +#define ERL_NIF_THR_NORMAL_SCHEDULER 1 +#define ERL_NIF_THR_DIRTY_CPU_SCHEDULER 2 +#define ERL_NIF_THR_DIRTY_IO_SCHEDULER 3 + #if (defined(__WIN32__) || defined(_WIN32) || defined(_WIN32_)) # define ERL_NIF_API_FUNC_DECL(RET_TYPE, NAME, ARGS) RET_TYPE (*NAME) ARGS typedef struct { diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index b211ab4b16..9a8f216773 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -173,7 +173,7 @@ ERL_NIF_API_FUNC_DECL(int, enif_get_local_port, (ErlNifEnv* env, ERL_NIF_TERM, E ERL_NIF_API_FUNC_DECL(int, enif_term_to_binary, (ErlNifEnv *env, ERL_NIF_TERM term, ErlNifBinary *bin)); ERL_NIF_API_FUNC_DECL(size_t, enif_binary_to_term, (ErlNifEnv *env, const unsigned char* data, size_t sz, ERL_NIF_TERM *term, unsigned int opts)); ERL_NIF_API_FUNC_DECL(int, enif_port_command, (ErlNifEnv *env, const ErlNifPort* to_port, ErlNifEnv *msg_env, ERL_NIF_TERM msg)); -ERL_NIF_API_FUNC_DECL(int,enif_is_on_dirty_scheduler,(ErlNifEnv*)); +ERL_NIF_API_FUNC_DECL(int,enif_thread_type,(void)); ERL_NIF_API_FUNC_DECL(int,enif_snprintf,(char * buffer, size_t size, const char *format, ...)); /* @@ -330,7 +330,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_snprintf,(char * buffer, size_t size, const char # define enif_term_to_binary ERL_NIF_API_FUNC_MACRO(enif_term_to_binary) # define enif_binary_to_term ERL_NIF_API_FUNC_MACRO(enif_binary_to_term) # define enif_port_command ERL_NIF_API_FUNC_MACRO(enif_port_command) -# define enif_is_on_dirty_scheduler ERL_NIF_API_FUNC_MACRO(enif_is_on_dirty_scheduler) +# define enif_thread_type ERL_NIF_API_FUNC_MACRO(enif_thread_type) # define enif_snprintf ERL_NIF_API_FUNC_MACRO(enif_snprintf) /* diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h index f0075ca2b9..f90844ccc8 100644 --- a/erts/emulator/beam/erl_port.h +++ b/erts/emulator/beam/erl_port.h @@ -361,6 +361,8 @@ Eterm erts_request_io_bytes(Process *c_p); #define ERTS_PORT_REDS_CONNECT (CONTEXT_REDS/200) #define ERTS_PORT_REDS_UNLINK (CONTEXT_REDS/200) #define ERTS_PORT_REDS_LINK (CONTEXT_REDS/200) +#define ERTS_PORT_REDS_MONITOR (CONTEXT_REDS/200) +#define ERTS_PORT_REDS_DEMONITOR (CONTEXT_REDS/200) #define ERTS_PORT_REDS_BADSIG (CONTEXT_REDS/200) #define ERTS_PORT_REDS_CONTROL (CONTEXT_REDS/100) #define ERTS_PORT_REDS_CALL (CONTEXT_REDS/50) @@ -850,16 +852,20 @@ void erts_port_resume_procs(Port *); struct binary; -#define ERTS_P2P_SIG_TYPE_BAD 0 -#define ERTS_P2P_SIG_TYPE_OUTPUT 1 -#define ERTS_P2P_SIG_TYPE_OUTPUTV 2 -#define ERTS_P2P_SIG_TYPE_CONNECT 3 -#define ERTS_P2P_SIG_TYPE_EXIT 4 -#define ERTS_P2P_SIG_TYPE_CONTROL 5 -#define ERTS_P2P_SIG_TYPE_CALL 6 -#define ERTS_P2P_SIG_TYPE_INFO 7 -#define ERTS_P2P_SIG_TYPE_LINK 8 -#define ERTS_P2P_SIG_TYPE_UNLINK 9 +enum { + ERTS_P2P_SIG_TYPE_BAD = 0, + ERTS_P2P_SIG_TYPE_OUTPUT = 1, + ERTS_P2P_SIG_TYPE_OUTPUTV = 2, + ERTS_P2P_SIG_TYPE_CONNECT = 3, + ERTS_P2P_SIG_TYPE_EXIT = 4, + ERTS_P2P_SIG_TYPE_CONTROL = 5, + ERTS_P2P_SIG_TYPE_CALL = 6, + ERTS_P2P_SIG_TYPE_INFO = 7, + ERTS_P2P_SIG_TYPE_LINK = 8, + ERTS_P2P_SIG_TYPE_UNLINK = 9, + ERTS_P2P_SIG_TYPE_MONITOR = 10, + ERTS_P2P_SIG_TYPE_DEMONITOR = 11 +}; #define ERTS_P2P_SIG_TYPE_BITS 4 #define ERTS_P2P_SIG_TYPE_MASK \ @@ -921,6 +927,15 @@ struct ErtsProc2PortSigData_ { struct { Eterm from; } unlink; + struct { + Eterm origin; /* who receives monitor event, pid */ + Eterm name; /* either name for named monitor, or port id */ + } monitor; + struct { + Eterm origin; /* who is at the other end of the monitor, pid */ + Eterm name; /* port id */ + Uint32 ref[ERTS_MAX_REF_NUMBERS]; /* box contents of a ref */ + } demonitor; } u; } ; @@ -1017,6 +1032,29 @@ ErtsPortOpResult erts_port_control(Process *, Port *, unsigned int, Eterm, Eterm ErtsPortOpResult erts_port_call(Process *, Port *, unsigned int, Eterm, Eterm *); ErtsPortOpResult erts_port_info(Process *, Port *, Eterm, Eterm *); +/* Creates monitor between Origin and Target. Ref must be initialized to + * a reference (ref may be rewritten to be used to serve additionally as a + * signal id). Name is atom if user monitors port by name or NIL */ +ErtsPortOpResult erts_port_monitor(Process *origin, Port *target, Eterm name, + Eterm *ref); + +typedef enum { + /* Normal demonitor rules apply with locking and reductions bump */ + ERTS_PORT_DEMONITOR_NORMAL = 1, + /* Relaxed demonitor rules when process is about to die, which means that + * pid lookup won't work, locks won't work, no reductions bump. */ + ERTS_PORT_DEMONITOR_ORIGIN_ON_DEATHBED = 2, +} ErtsDemonitorMode; + +/* Removes monitor between origin and target, identified by ref. + * origin_is_dying can be 0 (false, normal locking rules and reductions bump + * apply) or 1 (true, in case when we avoid origin locking) */ +ErtsPortOpResult erts_port_demonitor(Process *origin, ErtsDemonitorMode mode, + Port *target, Eterm ref, + Eterm *trap_ref); +/* defined in erl_bif_port.c */ +Port *erts_sig_lookup_port(Process *c_p, Eterm id_or_name); + int erts_port_output_async(Port *, Eterm, Eterm); /* diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index c0b1d7246c..66f22979ad 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -3509,7 +3509,7 @@ wake_dirty_schedulers(ErtsRunQueue *rq, int one) #endif #define ERTS_NO_USED_RUNQS_SHIFT 16 -#define ERTS_NO_RUNQS_MASK 0xffff +#define ERTS_NO_RUNQS_MASK 0xffffU #if ERTS_MAX_NO_OF_SCHEDULERS > ERTS_NO_RUNQS_MASK # error "Too large amount of schedulers allowed" @@ -10082,7 +10082,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) } } - if (ERTS_IS_GC_DESIRED(p)) { + if (ERTS_IS_GC_DESIRED(p) && !ERTS_SCHEDULER_IS_DIRTY_IO(esdp)) { if (!(state & ERTS_PSFLG_EXITING) && !(p->flags & (F_DELAY_GC|F_DISABLE_GC))) { int cost = scheduler_gc_proc(p, reds); calls += cost; @@ -12239,7 +12239,6 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) ExitMonitorContext *pcontext = vpcontext; DistEntry *dep; ErtsMonitor *rmon; - Process *rp; switch (mon->type) { case MON_ORIGIN: @@ -12268,9 +12267,10 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) erts_deref_dist_entry(dep); } } else { - ASSERT(is_pid(mon->pid)); - if (is_internal_pid(mon->pid)) { /* local by pid or name */ - rp = erts_pid2proc(NULL, 0, mon->pid, ERTS_PROC_LOCK_LINK); + ASSERT(is_pid(mon->pid) || is_port(mon->pid)); + /* if is local by pid or name */ + if (is_internal_pid(mon->pid)) { + Process *rp = erts_pid2proc(NULL, 0, mon->pid, ERTS_PROC_LOCK_LINK); if (!rp) { goto done; } @@ -12280,7 +12280,17 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) goto done; } erts_destroy_monitor(rmon); - } else { /* remote by pid */ + } else if (is_internal_port(mon->pid)) { + /* Is a local port */ + Port *prt = erts_port_lookup_raw(mon->pid); + if (!prt) { + goto done; + } + erts_port_demonitor(pcontext->p, + ERTS_PORT_DEMONITOR_ORIGIN_ON_DEATHBED, + prt, mon->ref, NULL); + return; /* let erts_port_demonitor do the deletion */ + } else { /* remote by pid */ ASSERT(is_external_pid(mon->pid)); dep = external_pid_dist_entry(mon->pid); ASSERT(dep != NULL); @@ -12318,6 +12328,7 @@ static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) erts_port_release(prt); } else if (is_internal_pid(mon->pid)) {/* local by name or pid */ Eterm watched; + Process *rp; DeclareTmpHeapNoproc(lhp,3); ErtsProcLocks rp_locks = (ERTS_PROC_LOCK_LINK | ERTS_PROC_LOCKS_MSG_SEND); diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c index eeaa9a569c..a70dfb8e73 100644 --- a/erts/emulator/beam/erl_process_dump.c +++ b/erts/emulator/beam/erl_process_dump.c @@ -560,6 +560,11 @@ dump_externally(int to, void *to_arg, Eterm term) } } + /* Do not handle maps */ + if (is_map(term)) { + term = am_undefined; + } + s = p = sbuf; erts_encode_ext(term, &p); erts_print(to, to_arg, "E%X:", p-s); diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h index a5931ffc25..fecfd96ab0 100644 --- a/erts/emulator/beam/erl_ptab.h +++ b/erts/emulator/beam/erl_ptab.h @@ -168,7 +168,7 @@ typedef struct { #define ERTS_PTAB_INVALID_ID(TAG) \ ((Eterm) \ - ((((1 << ERTS_PTAB_ID_DATA_SIZE) - 1) << ERTS_PTAB_ID_DATA_SHIFT) \ + ((((1U << ERTS_PTAB_ID_DATA_SIZE) - 1) << ERTS_PTAB_ID_DATA_SHIFT) \ | (TAG))) #define erts_ptab_is_valid_id(ID) \ diff --git a/erts/emulator/beam/erl_thr_progress.c b/erts/emulator/beam/erl_thr_progress.c index 542541165b..21938e7684 100644 --- a/erts/emulator/beam/erl_thr_progress.c +++ b/erts/emulator/beam/erl_thr_progress.c @@ -95,9 +95,9 @@ #define ERTS_THR_PRGR_FTL_ERR_BLCK_POLL_INTERVAL 100 -#define ERTS_THR_PRGR_LFLG_BLOCK (((erts_aint32_t) 1) << 31) -#define ERTS_THR_PRGR_LFLG_NO_LEADER (((erts_aint32_t) 1) << 30) -#define ERTS_THR_PRGR_LFLG_WAITING_UM (((erts_aint32_t) 1) << 29) +#define ERTS_THR_PRGR_LFLG_BLOCK ((erts_aint32_t) (1U << 31)) +#define ERTS_THR_PRGR_LFLG_NO_LEADER ((erts_aint32_t) (1U << 30)) +#define ERTS_THR_PRGR_LFLG_WAITING_UM ((erts_aint32_t) (1U << 29)) #define ERTS_THR_PRGR_LFLG_ACTIVE_MASK (~(ERTS_THR_PRGR_LFLG_NO_LEADER \ | ERTS_THR_PRGR_LFLG_BLOCK \ | ERTS_THR_PRGR_LFLG_WAITING_UM)) @@ -142,8 +142,8 @@ init_nob(ERTS_THR_PRGR_ATOMIC *atmc, ErtsThrPrgrVal val) #warning "Thread progress state debug is on" #endif -#define ERTS_THR_PROGRESS_STATE_DEBUG_LEADER (((erts_aint32_t) 1) << 0) -#define ERTS_THR_PROGRESS_STATE_DEBUG_ACTIVE (((erts_aint32_t) 1) << 1) +#define ERTS_THR_PROGRESS_STATE_DEBUG_LEADER ((erts_aint32_t) (1U << 0)) +#define ERTS_THR_PROGRESS_STATE_DEBUG_ACTIVE ((erts_aint32_t) (1U << 1)) #define ERTS_THR_PROGRESS_STATE_DEBUG_INIT(ID) \ erts_atomic32_init_nob(&intrnl->thr[(ID)].data.state_debug, \ @@ -179,10 +179,10 @@ do { \ #endif /* ERTS_THR_PROGRESS_STATE_DEBUG */ -#define ERTS_THR_PRGR_BLCKR_INVALID (~((erts_aint32_t) 0)) -#define ERTS_THR_PRGR_BLCKR_UNMANAGED (((erts_aint32_t) 1) << 31) +#define ERTS_THR_PRGR_BLCKR_INVALID ((erts_aint32_t) (~0U)) +#define ERTS_THR_PRGR_BLCKR_UNMANAGED ((erts_aint32_t) (1U << 31)) -#define ERTS_THR_PRGR_BC_FLG_NOT_BLOCKING (((erts_aint32_t) 1) << 31) +#define ERTS_THR_PRGR_BC_FLG_NOT_BLOCKING ((erts_aint32_t) (1U << 31)) #define ERTS_THR_PRGR_BM_BITS 32 #define ERTS_THR_PRGR_BM_SHIFT 5 @@ -1186,7 +1186,7 @@ wakeup_unmanaged_threads(ErtsThrPrgrUnmanagedWakeupData *umwd) int hbase = hix << ERTS_THR_PRGR_BM_SHIFT; int hbit; for (hbit = 0; hbit < ERTS_THR_PRGR_BM_BITS; hbit++) { - if (hmask & (1 << hbit)) { + if (hmask & (1U << hbit)) { erts_aint_t lmask; int lix = hbase + hbit; ASSERT(0 <= lix && lix < umwd->low_sz); @@ -1195,7 +1195,7 @@ wakeup_unmanaged_threads(ErtsThrPrgrUnmanagedWakeupData *umwd) int lbase = lix << ERTS_THR_PRGR_BM_SHIFT; int lbit; for (lbit = 0; lbit < ERTS_THR_PRGR_BM_BITS; lbit++) { - if (lmask & (1 << lbit)) { + if (lmask & (1U << lbit)) { int id = lbase + lbit; wakeup_unmanaged(id); } diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index 3dca58d60b..4cf38bf894 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -2115,32 +2115,36 @@ profile_runnable_proc(Process *p, Eterm status){ Eterm *hp, msg; Eterm where = am_undefined; ErlHeapFragment *bp = NULL; - int use_current = 1; + BeamInstr *current = NULL; #ifndef ERTS_SMP #define LOCAL_HEAP_SIZE (4 + 6 + ERTS_TRACE_PATCH_TS_MAX_SIZE) - DeclareTmpHeapNoproc(local_heap,LOCAL_HEAP_SIZE); UseTmpHeapNoproc(LOCAL_HEAP_SIZE); hp = local_heap; #else + ErtsThrPrgrDelayHandle dhndl; Uint hsz = 4 + 6 + patch_ts_size(erts_system_profile_ts_type)-1; #endif - - if (ERTS_PROC_IS_EXITING(p)) { - use_current = 0; - /* could probably set 'where' to 'exiting' here, - * though it's not documented as such */ - } else { - if (!p->current) { - p->current = find_function_from_pc(p->i); + /* Assumptions: + * We possibly don't have the MAIN_LOCK for the process p here. + * We assume that we can read from p->current and p->i atomically + */ +#ifdef ERTS_SMP + dhndl = erts_thr_progress_unmanaged_delay(); /* suspend purge operations */ +#endif + + if (!ERTS_PROC_IS_EXITING(p)) { + if (p->current) { + current = p->current; + } else { + current = find_function_from_pc(p->i); } - use_current = p->current != NULL; } #ifdef ERTS_SMP - if (!use_current) { + if (!current) { hsz -= 4; } @@ -2148,11 +2152,15 @@ profile_runnable_proc(Process *p, Eterm status){ hp = bp->mem; #endif - if (use_current) { - where = TUPLE3(hp, p->current[0], p->current[1], make_small(p->current[2])); hp += 4; + if (current) { + where = TUPLE3(hp, current[0], current[1], make_small(current[2])); hp += 4; } else { where = make_small(0); } + +#ifdef ERTS_SMP + erts_thr_progress_unmanaged_continue(dhndl); +#endif erts_smp_mtx_lock(&smq_mtx); diff --git a/erts/emulator/beam/erlang_lttng.h b/erts/emulator/beam/erlang_lttng.h index 12f68e477b..4e869671f7 100644 --- a/erts/emulator/beam/erlang_lttng.h +++ b/erts/emulator/beam/erlang_lttng.h @@ -20,7 +20,7 @@ #ifdef USE_LTTNG #undef TRACEPOINT_PROVIDER -#define TRACEPOINT_PROVIDER com_ericsson_otp +#define TRACEPOINT_PROVIDER org_erlang_otp #undef TRACEPOINT_INCLUDE #define TRACEPOINT_INCLUDE "erlang_lttng.h" @@ -33,7 +33,7 @@ /* Schedulers */ TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, scheduler_poll, TP_ARGS( int, id, @@ -62,7 +62,7 @@ typedef struct { /* Port and Driver Scheduling */ TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_start, TP_ARGS( char*, pid, @@ -77,7 +77,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_init, TP_ARGS( char*, driver, @@ -94,7 +94,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_outputv, TP_ARGS( char*, pid, @@ -111,7 +111,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_output, TP_ARGS( char*, pid, @@ -128,7 +128,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_ready_input, TP_ARGS( char*, pid, @@ -143,7 +143,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_ready_output, TP_ARGS( char*, pid, @@ -158,7 +158,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_event, TP_ARGS( char*, pid, @@ -173,7 +173,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_timeout, TP_ARGS( char*, pid, @@ -188,7 +188,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_stop_select, TP_ARGS( char*, driver @@ -199,7 +199,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_flush, TP_ARGS( char*, pid, @@ -214,7 +214,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_stop, TP_ARGS( char*, pid, @@ -229,7 +229,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_process_exit, TP_ARGS( char*, pid, @@ -244,7 +244,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_ready_async, TP_ARGS( char*, pid, @@ -259,7 +259,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_finish, TP_ARGS( char*, driver @@ -270,7 +270,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_call, TP_ARGS( char*, pid, @@ -289,7 +289,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, driver_control, TP_ARGS( char*, pid, @@ -310,7 +310,7 @@ TRACEPOINT_EVENT( /* Async pool */ TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, aio_pool_get, TP_ARGS( char*, port, @@ -323,7 +323,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, aio_pool_put, TP_ARGS( char*, port, @@ -339,7 +339,7 @@ TRACEPOINT_EVENT( /* Memory Allocator */ TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, carrier_create, TP_ARGS( const char*, type, @@ -365,7 +365,7 @@ TRACEPOINT_EVENT( TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, carrier_destroy, TP_ARGS( const char*, type, @@ -390,7 +390,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, carrier_pool_put, TP_ARGS( const char*, name, @@ -405,7 +405,7 @@ TRACEPOINT_EVENT( ) TRACEPOINT_EVENT( - com_ericsson_otp, + org_erlang_otp, carrier_pool_get, TP_ARGS( const char*, name, diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index 3c002d43a7..beed847578 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -2159,12 +2159,23 @@ enc_atom(ErtsAtomCacheMap *acmp, Eterm atom, byte *ep, Uint32 dflags) return ep; } +/* + * We use this atom as sysname in local pid/port/refs + * for the ETS compressed format (DFLAG_INTERNAL_TAGS). + * + * We used atom '' earlier but that turned out to cause problems + * for buggy erl_interface/ic usage of c-nodes with empty node names. + * A long atom reduces risk of nodes actually called this and the length + * does not matter anyway as it's encoded with atom index (ATOM_INTERNAL_REF2). + */ +#define INTERNAL_LOCAL_SYSNAME am_await_microstate_accounting_modifications + static byte* enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags) { Uint on, os; Eterm sysname = ((is_internal_pid(pid) && (dflags & DFLAG_INTERNAL_TAGS)) - ? am_Empty : pid_node_name(pid)); + ? INTERNAL_LOCAL_SYSNAME : pid_node_name(pid)); Uint32 creation = pid_creation(pid); byte* tagp = ep++; @@ -2268,7 +2279,7 @@ dec_atom(ErtsDistExternal *edep, byte* ep, Eterm* objp) static ERTS_INLINE ErlNode* dec_get_node(Eterm sysname, Uint32 creation) { - if (sysname == am_Empty) /* && DFLAG_INTERNAL_TAGS */ + if (sysname == INTERNAL_LOCAL_SYSNAME) /* && DFLAG_INTERNAL_TAGS */ return erts_this_node; if (sysname == erts_this_node->sysname @@ -2555,7 +2566,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, case EXTERNAL_REF_DEF: { Uint32 *ref_num; Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_ref(obj)) - ? am_Empty : ref_node_name(obj)); + ? INTERNAL_LOCAL_SYSNAME : ref_node_name(obj)); Uint32 creation = ref_creation(obj); byte* tagp = ep++; @@ -2584,7 +2595,7 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, case PORT_DEF: case EXTERNAL_PORT_DEF: { Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_port(obj)) - ? am_Empty : port_node_name(obj)); + ? INTERNAL_LOCAL_SYSNAME : port_node_name(obj)); Uint32 creation = port_creation(obj); byte* tagp = ep++; diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 01df5476db..cb8792dffa 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1260,7 +1260,7 @@ typedef struct { /* * Try doing an immediate driver callback call from a process. If * this fail, the operation should be scheduled in the normal case... - * + * Returns: ok to do the call, or error (lock busy, does not exist, etc) */ static ERTS_INLINE ErtsTryImmDrvCallResult try_imm_drv_call(ErtsTryImmDrvCallState *sp) @@ -3074,6 +3074,250 @@ erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp) } static void +port_monitor_failure(Eterm port_id, Eterm origin, Eterm ref_DOWN) +{ + Process *origin_p; + ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK; + ASSERT(is_internal_pid(origin)); + + origin_p = erts_pid2proc(NULL, 0, origin, p_locks); + if (! origin_p) { return; } + + /* Send the DOWN message immediately. Ref is made on the fly because + * caller has never seen it yet. */ + erts_queue_monitor_message(origin_p, &p_locks, ref_DOWN, + am_port, port_id, am_noproc); + erts_smp_proc_unlock(origin_p, p_locks); +} + +/* Origin wants to monitor port Prt. State contains possible error, which has + * happened just before. Name is either NIL or an atom, if user monitors + * a port by name. Ref is premade reference that will be returned to user */ +static void +port_monitor(Port *prt, erts_aint32_t state, Eterm origin, + Eterm name, Eterm ref) +{ + Eterm name_or_nil = is_atom(name) ? name : NIL; + + ASSERT(is_pid(origin)); + ASSERT(is_atom(name) || is_port(name) || name == NIL); + ASSERT(is_internal_ref(ref)); + + if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) { + ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK; + + Process *origin_p = erts_pid2proc(NULL, 0, origin, p_locks); + if (! origin_p) { + goto failure; + } + erts_add_monitor(&ERTS_P_MONITORS(origin_p), MON_ORIGIN, ref, + prt->common.id, name_or_nil); + erts_add_monitor(&ERTS_P_MONITORS(prt), MON_TARGET, ref, + origin, name_or_nil); + + erts_smp_proc_unlock(origin_p, p_locks); + } else { +failure: + port_monitor_failure(prt->common.id, origin, ref); + } +} + +static int +port_sig_monitor(Port *prt, erts_aint32_t state, int op, + ErtsProc2PortSigData *sigdp) +{ + Eterm hp[REF_THING_SIZE]; + Eterm ref = make_internal_ref(&hp); + write_ref_thing(hp, sigdp->ref[0], sigdp->ref[1], sigdp->ref[2]); + + if (op == ERTS_PROC2PORT_SIG_EXEC) { + /* erts_add_monitor call inside port_monitor will copy ref from hp */ + port_monitor(prt, state, + sigdp->u.monitor.origin, + sigdp->u.monitor.name, + ref); + } else { + port_monitor_failure(sigdp->u.monitor.name, + sigdp->u.monitor.origin, + ref); + } + if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) { + port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt); + } + return ERTS_PORT_REDS_MONITOR; +} + +/* Creates monitor between Origin and Target. Ref must be initialized to + * a reference (ref may be rewritten to be used to serve additionally as a + * signal id). Name is atom if user monitors port by name or NIL */ +ErtsPortOpResult +erts_port_monitor(Process *origin, Port *port, Eterm name, Eterm *refp) +{ + ErtsProc2PortSigData *sigdp; + ErtsTryImmDrvCallState try_call_state + = ERTS_INIT_TRY_IMM_DRV_CALL_STATE( + origin, port, ERTS_PORT_SFLGS_INVALID_LOOKUP, + 0, + 0, /* trap_ref is always set so !trap_ref always is false */ + am_monitor); + + ASSERT(origin); + ASSERT(port); + ASSERT(is_atom(name) || is_port(name)); + ASSERT(refp); + + switch (try_imm_drv_call(&try_call_state)) { + case ERTS_TRY_IMM_DRV_CALL_OK: + port_monitor(port, try_call_state.state, origin->common.id, name, *refp); + finalize_imm_drv_call(&try_call_state); + BUMP_REDS(origin, ERTS_PORT_REDS_MONITOR); + return ERTS_PORT_OP_DONE; + case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT: + return ERTS_PORT_OP_BADARG; + default: + break; /* Schedule call instead... */ + } + + sigdp = erts_port_task_alloc_p2p_sig_data(); + sigdp->flags = ERTS_P2P_SIG_TYPE_MONITOR; + sigdp->u.monitor.origin = origin->common.id; + sigdp->u.monitor.name = name; /* either named monitor, or port id */ + + /* Ref contents will be initialized here */ + return erts_schedule_proc2port_signal(origin, port, origin->common.id, + refp, sigdp, 0, NULL, + port_sig_monitor); +} + +static void +port_demonitor_failure(Eterm port_id, Eterm origin, Eterm ref) +{ + Process *origin_p; + ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK; + ErtsMonitor *mon1; + ASSERT(is_internal_pid(origin)); + + origin_p = erts_pid2proc(NULL, 0, origin, rp_locks); + if (! origin_p) { return; } + + /* do not send any DOWN messages, drop monitors on process */ + mon1 = erts_remove_monitor(&ERTS_P_MONITORS(origin_p), ref); + if (mon1 != NULL) { + erts_destroy_monitor(mon1); + } + + erts_smp_proc_unlock(origin_p, rp_locks); +} + +/* Origin wants to demonitor port Prt. State contains possible error, which has + * happened just before. Ref is reference to monitor */ +static void +port_demonitor(Port *port, erts_aint32_t state, Eterm origin, Eterm ref) +{ + ASSERT(port); + ASSERT(is_pid(origin)); + ASSERT(is_internal_ref(ref)); + + if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) { + ErtsProcLocks p_locks = ERTS_PROC_LOCK_LINK; + Process *origin_p = erts_pid2proc(NULL, 0, origin, p_locks); + if (origin_p) { + ErtsMonitor *mon1 = erts_remove_monitor(&ERTS_P_MONITORS(origin_p), + ref); + if (mon1 != NULL) { + erts_destroy_monitor(mon1); + } + } + if (1) { + ErtsMonitor *mon2 = erts_remove_monitor(&ERTS_P_MONITORS(port), + ref); + if (mon2 != NULL) { + erts_destroy_monitor(mon2); + } + } + if (origin_p) { /* when origin is dying, it won't be found */ + erts_smp_proc_unlock(origin_p, p_locks); + } + } else { + port_demonitor_failure(port->common.id, origin, ref); + } +} + +static int +port_sig_demonitor(Port *prt, erts_aint32_t state, int op, + ErtsProc2PortSigData *sigdp) +{ + Eterm hp[REF_THING_SIZE]; + Eterm ref = make_internal_ref(&hp); + write_ref_thing(hp, sigdp->u.demonitor.ref[0], + sigdp->u.demonitor.ref[1], + sigdp->u.demonitor.ref[2]); + if (op == ERTS_PROC2PORT_SIG_EXEC) { + port_demonitor(prt, state, sigdp->u.demonitor.origin, ref); + } else { + port_demonitor_failure(sigdp->u.demonitor.name, + sigdp->u.demonitor.origin, + ref); + } + if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) { + port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt); + } + return ERTS_PORT_REDS_DEMONITOR; +} + +/* Removes monitor between origin and target, identified by ref. + * Mode defines normal or relaxed demonitor rules (process is at death) */ +ErtsPortOpResult erts_port_demonitor(Process *origin, ErtsDemonitorMode mode, + Port *target, Eterm ref, + Eterm *trap_ref) +{ + Process *c_p = mode == ERTS_PORT_DEMONITOR_NORMAL ? origin : NULL; + ErtsProc2PortSigData *sigdp; + ErtsTryImmDrvCallState try_call_state + = ERTS_INIT_TRY_IMM_DRV_CALL_STATE( + c_p, + target, ERTS_PORT_SFLGS_INVALID_LOOKUP, + 0, + !trap_ref, + am_demonitor); + + ASSERT(origin); + ASSERT(target); + ASSERT(is_internal_ref(ref)); + + switch (try_imm_drv_call(&try_call_state)) { + case ERTS_TRY_IMM_DRV_CALL_OK: + port_demonitor(target, try_call_state.state, origin->common.id, ref); + finalize_imm_drv_call(&try_call_state); + if (mode == ERTS_PORT_DEMONITOR_NORMAL) { + BUMP_REDS(origin, ERTS_PORT_REDS_DEMONITOR); + } + return ERTS_PORT_OP_DONE; + case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT: + return ERTS_PORT_OP_BADARG; + default: + break; /* Schedule call instead... */ + } + + sigdp = erts_port_task_alloc_p2p_sig_data(); + sigdp->flags = ERTS_P2P_SIG_TYPE_DEMONITOR; + sigdp->u.demonitor.origin = origin->common.id; + sigdp->u.demonitor.name = target->common.id; + { + RefThing *reft = ref_thing_ptr(ref); + /* Start from 1 skip ref arity */ + sys_memcpy(sigdp->u.demonitor.ref, + internal_thing_ref_numbers(reft), + sizeof(sigdp->u.demonitor.ref)); + } + + /* Ref contents will be initialized here */ + return erts_schedule_proc2port_signal(c_p, target, origin->common.id, + trap_ref, sigdp, 0, NULL, + port_sig_demonitor); +} + +static void init_ack_send_reply(Port *port, Eterm resp) { @@ -3942,23 +4186,30 @@ erts_terminate_port(Port *pp) terminate_port(pp); } +static void port_fire_one_monitor(ErtsMonitor *mon, void *ctx0); static void sweep_one_monitor(ErtsMonitor *mon, void *vpsc) { - ErtsMonitor *rmon; - Process *rp; + switch (mon->type) { + case MON_ORIGIN: { + ErtsMonitor *rmon; + Process *rp; - ASSERT(mon->type == MON_ORIGIN); - ASSERT(is_internal_pid(mon->pid)); - rp = erts_pid2proc(NULL, 0, mon->pid, ERTS_PROC_LOCK_LINK); - if (!rp) { - goto done; - } - rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref); - erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - if (rmon == NULL) { - goto done; + ASSERT(is_internal_pid(mon->pid)); + rp = erts_pid2proc(NULL, 0, mon->pid, ERTS_PROC_LOCK_LINK); + if (!rp) { + goto done; + } + rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref); + erts_smp_proc_unlock(rp, ERTS_PROC_LOCK_LINK); + if (rmon == NULL) { + goto done; + } + erts_destroy_monitor(rmon); + } break; + case MON_TARGET: { + port_fire_one_monitor(mon, vpsc); /* forward call */ + } break; } - erts_destroy_monitor(rmon); done: erts_destroy_monitor(mon); } @@ -4039,6 +4290,43 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc) erts_destroy_link(lnk); } +static void +port_fire_one_monitor(ErtsMonitor *mon, void *ctx0) +{ + Process *origin; + ErtsProcLocks origin_locks; + + if (mon->type != MON_TARGET || ! is_pid(mon->pid)) { + return; + } + /* + * Proceed here if someone monitors us, we (port) are the target and + * origin is some process + */ + origin_locks = ERTS_PROC_LOCKS_MSG_SEND | ERTS_PROC_LOCK_LINK; + + origin = erts_pid2proc(NULL, 0, mon->pid, origin_locks); + if (origin) { + DeclareTmpHeapNoproc(lhp,3); + SweepContext *ctx = (SweepContext *)ctx0; + ErtsMonitor *rmon; + Eterm watched = (is_atom(mon->name) + ? TUPLE2(lhp, mon->name, erts_this_dist_entry->sysname) + : ctx->port->common.id); + + erts_queue_monitor_message(origin, &origin_locks, mon->ref, am_port, + watched, ctx->reason); + UnUseTmpHeapNoproc(3); + + rmon = erts_remove_monitor(&ERTS_P_MONITORS(origin), mon->ref); + erts_smp_proc_unlock(origin, origin_locks); + + if (rmon) { + erts_destroy_monitor(rmon); + } + } +} + /* 'from' is sending 'this_port' an exit signal, (this_port must be internal). * If reason is normal we don't do anything, *unless* from is our connected * process in which case we close the port. Any other reason kills the port. @@ -4050,39 +4338,40 @@ static void sweep_one_link(ErtsLink *lnk, void *vpsc) */ int -erts_deliver_port_exit(Port *p, Eterm from, Eterm reason, int send_closed, +erts_deliver_port_exit(Port *prt, Eterm from, Eterm reason, int send_closed, int drop_normal) { ErtsLink *lnk; - Eterm rreason; + Eterm modified_reason; erts_aint32_t state, set_state_flags; ERTS_SMP_CHK_NO_PROC_LOCKS; - ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(p)); + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(prt)); - rreason = (reason == am_kill) ? am_killed : reason; + modified_reason = (reason == am_kill) ? am_killed : reason; #ifdef USE_VM_PROBES if (DTRACE_ENABLED(port_exit)) { DTRACE_CHARBUF(from_str, DTRACE_TERM_BUF_SIZE); DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); - DTRACE_CHARBUF(rreason_str, 64); + DTRACE_CHARBUF(reason_str, 64); erts_snprintf(from_str, sizeof(DTRACE_CHARBUF_NAME(from_str)), "%T", from); - dtrace_port_str(p, port_str); - erts_snprintf(rreason_str, sizeof(DTRACE_CHARBUF_NAME(rreason_str)), "%T", rreason); - DTRACE4(port_exit, from_str, port_str, p->name, rreason_str); + dtrace_port_str(prt, port_str); + erts_snprintf(reason_str, sizeof(DTRACE_CHARBUF_NAME(reason_str)), "%T", + modified_reason); + DTRACE4(port_exit, from_str, port_str, prt->name, reason_str); } #endif - state = erts_atomic32_read_nob(&p->state); + state = erts_atomic32_read_nob(&prt->state); if (state & (ERTS_PORT_SFLGS_DEAD | ERTS_PORT_SFLG_EXITING | ERTS_PORT_SFLG_CLOSING)) return 0; - if (reason == am_normal && from != ERTS_PORT_GET_CONNECTED(p) - && from != p->common.id && drop_normal) { + if (reason == am_normal && from != ERTS_PORT_GET_CONNECTED(prt) + && from != prt->common.id && drop_normal) { return 0; } @@ -4090,53 +4379,54 @@ erts_deliver_port_exit(Port *p, Eterm from, Eterm reason, int send_closed, if (send_closed) set_state_flags |= ERTS_PORT_SFLG_SEND_CLOSED; - erts_port_task_sched_enter_exiting_state(&p->sched); + erts_port_task_sched_enter_exiting_state(&prt->sched); - state = erts_atomic32_read_bor_mb(&p->state, set_state_flags); + state = erts_atomic32_read_bor_mb(&prt->state, set_state_flags); state |= set_state_flags; - if (IS_TRACED_FL(p, F_TRACE_PORTS)) - trace_port(p, am_closed, reason); + if (IS_TRACED_FL(prt, F_TRACE_PORTS)) + trace_port(prt, am_closed, reason); - erts_trace_check_exiting(p->common.id); + erts_trace_check_exiting(prt->common.id); - set_busy_port(ERTS_Port2ErlDrvPort(p), 0); + set_busy_port(ERTS_Port2ErlDrvPort(prt), 0); - if (p->common.u.alive.reg != NULL) - (void) erts_unregister_name(NULL, 0, p, p->common.u.alive.reg->name); + if (prt->common.u.alive.reg != NULL) + (void) erts_unregister_name(NULL, 0, prt, prt->common.u.alive.reg->name); { - SweepContext sc = {p, rreason}; - lnk = ERTS_P_LINKS(p); - ERTS_P_LINKS(p) = NULL; + SweepContext sc = {prt, modified_reason}; + lnk = ERTS_P_LINKS(prt); + ERTS_P_LINKS(prt) = NULL; erts_sweep_links(lnk, &sweep_one_link, &sc); } - DRV_MONITOR_LOCK_PDL(p); + DRV_MONITOR_LOCK_PDL(prt); { - ErtsMonitor *moni = ERTS_P_MONITORS(p); - ERTS_P_MONITORS(p) = NULL; - erts_sweep_monitors(moni, &sweep_one_monitor, NULL); + SweepContext ctx = {prt, modified_reason}; + ErtsMonitor *moni = ERTS_P_MONITORS(prt); + ERTS_P_MONITORS(prt) = NULL; + erts_sweep_monitors(moni, &sweep_one_monitor, &ctx); } - DRV_MONITOR_UNLOCK_PDL(p); + DRV_MONITOR_UNLOCK_PDL(prt); - if ((state & ERTS_PORT_SFLG_DISTRIBUTION) && p->dist_entry) { - erts_do_net_exits(p->dist_entry, rreason); - erts_deref_dist_entry(p->dist_entry); - p->dist_entry = NULL; - erts_atomic32_read_band_relb(&p->state, + if ((state & ERTS_PORT_SFLG_DISTRIBUTION) && prt->dist_entry) { + erts_do_net_exits(prt->dist_entry, modified_reason); + erts_deref_dist_entry(prt->dist_entry); + prt->dist_entry = NULL; + erts_atomic32_read_band_relb(&prt->state, ~ERTS_PORT_SFLG_DISTRIBUTION); } - if ((reason != am_kill) && !is_port_ioq_empty(p)) { + if ((reason != am_kill) && !is_port_ioq_empty(prt)) { /* must turn exiting flag off */ - erts_atomic32_read_bset_relb(&p->state, + erts_atomic32_read_bset_relb(&prt->state, (ERTS_PORT_SFLG_EXITING | ERTS_PORT_SFLG_CLOSING), ERTS_PORT_SFLG_CLOSING); - flush_port(p); + flush_port(prt); } else { - terminate_port(p); + terminate_port(prt); } return 1; diff --git a/erts/emulator/beam/lttng-wrapper.h b/erts/emulator/beam/lttng-wrapper.h index 294872c365..0bc75c1552 100644 --- a/erts/emulator/beam/lttng-wrapper.h +++ b/erts/emulator/beam/lttng-wrapper.h @@ -77,23 +77,23 @@ (RQ)->scheduler->no #define LTTNG_ENABLED(Name) \ - tracepoint_enabled(com_ericsson_otp, Name) + tracepoint_enabled(org_erlang_otp, Name) /* include a special LTTNG_DO for do_tracepoint ? */ #define LTTNG1(Name, Arg1) \ - tracepoint(com_ericsson_otp, Name, (Arg1)) + tracepoint(org_erlang_otp, Name, (Arg1)) #define LTTNG2(Name, Arg1, Arg2) \ - tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2)) + tracepoint(org_erlang_otp, Name, (Arg1), (Arg2)) #define LTTNG3(Name, Arg1, Arg2, Arg3) \ - tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2), (Arg3)) + tracepoint(org_erlang_otp, Name, (Arg1), (Arg2), (Arg3)) #define LTTNG4(Name, Arg1, Arg2, Arg3, Arg4) \ - tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4)) + tracepoint(org_erlang_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4)) #define LTTNG5(Name, Arg1, Arg2, Arg3, Arg4, Arg5) \ - tracepoint(com_ericsson_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4), (Arg5)) + tracepoint(org_erlang_otp, Name, (Arg1), (Arg2), (Arg3), (Arg4), (Arg5)) #else /* USE_LTTNG */ diff --git a/erts/emulator/beam/register.c b/erts/emulator/beam/register.c index 77f79fcea4..ac7096745e 100644 --- a/erts/emulator/beam/register.c +++ b/erts/emulator/beam/register.c @@ -323,7 +323,8 @@ erts_whereis_name(Process *c_p, Process** proc, ErtsProcLocks need_locks, int flags, - Port** port) + Port** port, + int lock_port) { RegProc* rp = NULL; HashValue hval; @@ -406,31 +407,33 @@ erts_whereis_name(Process *c_p, *port = NULL; else { #ifdef ERTS_SMP - if (pending_port == rp->pt) - pending_port = NULL; - else { - if (pending_port) { - /* Ahh! Registered port changed while reg lock - was unlocked... */ - erts_port_release(pending_port); - pending_port = NULL; - } + if (lock_port) { + if (pending_port == rp->pt) + pending_port = NULL; + else { + if (pending_port) { + /* Ahh! Registered port changed while reg lock + was unlocked... */ + erts_port_release(pending_port); + pending_port = NULL; + } - if (erts_smp_port_trylock(rp->pt) == EBUSY) { - Eterm id = rp->pt->common.id; /* id read only... */ - /* Unlock all locks, acquire port lock, and restart... */ - if (current_c_p_locks) { - erts_smp_proc_unlock(c_p, current_c_p_locks); - current_c_p_locks = 0; - } - reg_read_unlock(); - pending_port = erts_id2port(id); - goto restart; - } - } + if (erts_smp_port_trylock(rp->pt) == EBUSY) { + Eterm id = rp->pt->common.id; /* id read only... */ + /* Unlock all locks, acquire port lock, and restart... */ + if (current_c_p_locks) { + erts_smp_proc_unlock(c_p, current_c_p_locks); + current_c_p_locks = 0; + } + reg_read_unlock(); + pending_port = erts_id2port(id); + goto restart; + } + } + ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(rp->pt)); + } #endif *port = rp->pt; - ERTS_SMP_LC_ASSERT(erts_lc_is_port_locked(*port)); } } @@ -452,7 +455,7 @@ erts_whereis_process(Process *c_p, int flags) { Process *proc; - erts_whereis_name(c_p, c_p_locks, name, &proc, need_locks, flags, NULL); + erts_whereis_name(c_p, c_p_locks, name, &proc, need_locks, flags, NULL, 0); return proc; } diff --git a/erts/emulator/beam/register.h b/erts/emulator/beam/register.h index 88ab7b7bf1..d839f55d6b 100644 --- a/erts/emulator/beam/register.h +++ b/erts/emulator/beam/register.h @@ -49,7 +49,7 @@ int erts_register_name(Process *, Eterm, Eterm); Eterm erts_whereis_name_to_id(Process *, Eterm); void erts_whereis_name(Process *, ErtsProcLocks, Eterm, Process**, ErtsProcLocks, int, - Port**); + Port**, int); Process *erts_whereis_process(Process *, ErtsProcLocks, Eterm, diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 9a205d50d3..dfe82cab44 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -154,8 +154,9 @@ typedef ERTS_SYS_FD_TYPE ErtsSysFdType; # define ERTS_WRITE_UNLIKELY(X) X #endif +/* clang may have too low __GNUC__ versions but can handle it */ #ifdef __GNUC__ -# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5) +# if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 5) || defined(__clang__) # define ERTS_DECLARE_DUMMY(X) X __attribute__ ((unused)) # else # define ERTS_DECLARE_DUMMY(X) X diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 06dfb2dd10..93ea9f5dcb 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -627,6 +627,7 @@ static int is_nonzero(const char *s, size_t n) */ /* general address encode/decode tag */ +#define INET_AF_UNSPEC 0 #define INET_AF_INET 1 #define INET_AF_INET6 2 #define INET_AF_ANY 3 /* INADDR_ANY or IN6ADDR_ANY_INIT */ @@ -655,7 +656,7 @@ static int is_nonzero(const char *s, size_t n) /* INET_REQ_GETSTATUS enumeration */ #define INET_F_OPEN 0x0001 -#define INET_F_BOUND 0x0002 +/* INET_F_BOUND removed - renumber when there comes a bigger rewrite */ #define INET_F_ACTIVE 0x0004 #define INET_F_LISTEN 0x0008 #define INET_F_CON 0x0010 @@ -853,19 +854,15 @@ static int is_nonzero(const char *s, size_t n) #define INET_STATE_CLOSED (0) #define INET_STATE_OPEN (INET_F_OPEN) -#define INET_STATE_BOUND (INET_STATE_OPEN | INET_F_BOUND) -#define INET_STATE_CONNECTED (INET_STATE_BOUND | INET_F_ACTIVE) -#define INET_STATE_LISTENING (INET_STATE_BOUND | INET_F_LISTEN) -#define INET_STATE_CONNECTING (INET_STATE_BOUND | INET_F_CON) +#define INET_STATE_CONNECTED (INET_STATE_OPEN | INET_F_ACTIVE) +#define INET_STATE_LISTENING (INET_STATE_OPEN | INET_F_LISTEN) +#define INET_STATE_CONNECTING (INET_STATE_OPEN | INET_F_CON) #define INET_STATE_ACCEPTING (INET_STATE_LISTENING | INET_F_ACC) #define INET_STATE_MULTI_ACCEPTING (INET_STATE_ACCEPTING | INET_F_MULTI_CLIENT) #define IS_OPEN(d) \ (((d)->state & INET_F_OPEN) == INET_F_OPEN) -#define IS_BOUND(d) \ - (((d)->state & INET_F_BOUND) == INET_F_BOUND) - #define IS_CONNECTED(d) \ (((d)->state & INET_STATE_CONNECTED) == INET_STATE_CONNECTED) @@ -1287,6 +1284,7 @@ static int async_ref = 0; /* async reference id generator */ static ErlDrvTermData am_ok; static ErlDrvTermData am_undefined; +static ErlDrvTermData am_unspec; static ErlDrvTermData am_tcp; static ErlDrvTermData am_error; static ErlDrvTermData am_einval; @@ -1553,10 +1551,18 @@ static int load_address(ErlDrvTermData* spec, int i, char* buf) break; } #endif - default: { /* INET_AF_UNDEFINED */ - i = LOAD_ATOM(spec, i, am_undefined); + case INET_AF_UNSPEC: { + i = LOAD_ATOM(spec, i, am_unspec); + i = LOAD_BUF2BINARY(spec, i, buf, 0); + spec[i++] = ERL_DRV_TUPLE; + spec[i++] = 2; spec[i++] = ERL_DRV_INT; spec[i++] = 0; + break; + } + default: { /* INET_AF_UNDEFINED */ + i = LOAD_ATOM(spec, i, am_undefined); + i = LOAD_BUF2BINARY(spec, i, buf, 0); spec[i++] = ERL_DRV_TUPLE; spec[i++] = 2; spec[i++] = ERL_DRV_INT; @@ -3823,6 +3829,7 @@ static int inet_init() INIT_ATOM(ok); INIT_ATOM(undefined); + INIT_ATOM(unspec); INIT_ATOM(tcp); #ifdef HAVE_UDP INIT_ATOM(udp); @@ -4149,6 +4156,10 @@ static int inet_get_address(char* dst, inet_address* src, unsigned int* len) return 0; } #endif + else if (family == AF_UNSPEC) { + dst[0] = INET_AF_UNSPEC; + *len = 1; + } else { dst[0] = INET_AF_UNDEFINED; *len = 1; @@ -4419,10 +4430,12 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, char** rbuf, ErlDrvSizeT rsize) { inet_address name; - unsigned int sz = sizeof(name); + unsigned int sz; if (bound) { /* check that it is a socket and that the socket is bound */ + sz = sizeof(name); + sys_memzero((char *) &name, sz); if (IS_SOCKET_ERROR(sock_name(s, (struct sockaddr*) &name, &sz))) return ctl_error(sock_errno(), rbuf, rsize); if (name.sa.sa_family != domain) @@ -4437,10 +4450,7 @@ static ErlDrvSSizeT inet_ctl_fdopen(inet_descriptor* desc, int domain, int type, driver_select(desc->port, desc->event, ERL_DRV_READ, 1); #endif - if (bound) - desc->state = INET_STATE_BOUND; - else - desc->state = INET_STATE_OPEN; + desc->state = INET_STATE_OPEN; if (type == SOCK_STREAM) { /* check if connected */ sz = sizeof(name); @@ -8448,7 +8458,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (len != 4) return ctl_error(EINVAL, rbuf, rsize); if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize); #ifdef HAVE_SCTP if (IS_SCTP(desc) && p_sctp_getpaddrs) { @@ -8524,7 +8533,6 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (len != 4) return ctl_error(EINVAL, rbuf, rsize); if (! IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (! IS_BOUND(desc)) return ctl_xerror(EXBADSEQ, rbuf, rsize); #ifdef HAVE_SCTP if (IS_SCTP(desc) && p_sctp_getladdrs) { @@ -8546,6 +8554,7 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, int i; sz = sizeof(addr); + sys_memzero((char *) &addr, sz); i = sock_name(desc->s, (struct sockaddr *) &addr, &sz); return reply_inet_addrs(i >= 0 ? 1 : i, &addr, rbuf, rsize, sz); } @@ -8559,15 +8568,13 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, DEBUGF(("inet_ctl(%ld): NAME\r\n", (long)desc->port)); - if (!IS_BOUND(desc)) - return ctl_error(EINVAL, rbuf, rsize); /* address is not valid */ - if ((ptr = desc->name_ptr) != NULL) { sz = desc->name_addr_len; } else { ptr = &name; sz = sizeof(name); + sys_memzero((char *) &name, sz); if (IS_SOCKET_ERROR (sock_name(desc->s, (struct sockaddr*)ptr, &sz))) return ctl_error(sock_errno(), rbuf, rsize); @@ -8612,11 +8619,12 @@ static ErlDrvSSizeT inet_ctl(inet_descriptor* desc, int cmd, char* buf, if (IS_SOCKET_ERROR(sock_bind(desc->s,(struct sockaddr*) &local, len))) return ctl_error(sock_errno(), rbuf, rsize); - desc->state = INET_STATE_BOUND; + desc->state = INET_STATE_OPEN; port = inet_address_port(&local); if (port == 0) { SOCKLEN_T adrlen = sizeof(local); + sys_memzero((char *) &local, adrlen); sock_name(desc->s, &local.sa, &adrlen); port = inet_address_port(&local); } @@ -9136,8 +9144,6 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_xerror(EXBADPORT, rbuf, rsize); if (!IS_OPEN(INETP(desc))) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (!IS_BOUND(INETP(desc))) - return ctl_xerror(EXBADSEQ, rbuf, rsize); if (len != 2) return ctl_error(EINVAL, rbuf, rsize); backlog = get_int16(buf); @@ -9160,8 +9166,6 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_xerror(EXBADPORT, rbuf, rsize); if (IS_CONNECTED(INETP(desc))) return ctl_error(EISCONN, rbuf, rsize); - if (!IS_BOUND(INETP(desc))) - return ctl_xerror(EXBADSEQ, rbuf, rsize); if (IS_CONNECTING(INETP(desc))) return ctl_error(EINVAL, rbuf, rsize); if (len < 6) @@ -9262,6 +9266,7 @@ static ErlDrvSSizeT tcp_inet_ctl(ErlDrvData e, unsigned int cmd, return ctl_reply(INET_REP_OK, tbuf, 2, rbuf, rsize); } else { n = sizeof(desc->inet.remote); + sys_memzero((char *) &remote, n); s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &n); if (s == INVALID_SOCKET) { if (sock_errno() == ERRNO_BLOCK) { @@ -10147,6 +10152,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) inet_async_op *this_op = desc->inet.opt; len = sizeof(desc->inet.remote); + sys_memzero((char *) &remote, len); s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len); if (s == INVALID_SOCKET && sock_errno() == ERRNO_BLOCK) { /* Just try again, no real error, just a ghost trigger from poll, @@ -10213,6 +10219,7 @@ static int tcp_inet_input(tcp_descriptor* desc, HANDLE event) while (desc->inet.state == INET_STATE_MULTI_ACCEPTING) { len = sizeof(desc->inet.remote); + sys_memzero((char *) &remote, len); s = sock_accept(desc->inet.s, (struct sockaddr*) &remote, &len); if (s == INVALID_SOCKET && sock_errno() == ERRNO_BLOCK) { /* Just try again, no real error, keep the last return code */ @@ -10653,7 +10660,7 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) (struct sockaddr*) &desc->inet.remote, &sz); if (IS_SOCKET_ERROR(code)) { - desc->inet.state = INET_STATE_BOUND; /* restore state */ + desc->inet.state = INET_STATE_OPEN; /* restore state */ ret = async_error(INETP(desc), sock_errno()); goto done; } @@ -10666,7 +10673,7 @@ static int tcp_inet_output(tcp_descriptor* desc, HANDLE event) (void *)&error, &sz); if ((code < 0) || error) { - desc->inet.state = INET_STATE_BOUND; /* restore state */ + desc->inet.state = INET_STATE_OPEN; /* restore state */ ret = async_error(INETP(desc), error); goto done; } @@ -11075,8 +11082,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, if (!IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (!IS_BOUND(desc)) - return ctl_xerror(EXBADSEQ, rbuf, rsize); #ifdef HAVE_SCTP if (IS_SCTP(desc)) { inet_address remote; @@ -11163,8 +11168,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return ctl_xerror(EXBADPORT, rbuf, rsize); if (!IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (!IS_BOUND(desc)) - return ctl_xerror(EXBADSEQ, rbuf, rsize); if (len != 2) return ctl_error(EINVAL, rbuf, rsize); @@ -11211,7 +11214,7 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return ctl_error(sock_errno(), rbuf, rsize); } - desc->state = INET_STATE_BOUND; + desc->state = INET_STATE_OPEN; return ctl_reply(INET_REP_OK, NULL, 0, rbuf, rsize); } @@ -11228,8 +11231,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, return ctl_xerror(EXBADPORT, rbuf, rsize); if (!IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (!IS_BOUND(desc)) - return ctl_xerror(EXBADSEQ, rbuf, rsize); if (! p_sctp_peeloff) return ctl_error(ENOTSUP, rbuf, rsize); @@ -11270,8 +11271,6 @@ static ErlDrvSSizeT packet_inet_ctl(ErlDrvData e, unsigned int cmd, char* buf, /* INPUT: Timeout(4), Length(4) */ if (!IS_OPEN(desc)) return ctl_xerror(EXBADPORT, rbuf, rsize); - if (!IS_BOUND(desc)) - return ctl_error(EINVAL, rbuf, rsize); if (desc->active || (len != 8)) return ctl_error(EINVAL, rbuf, rsize); timeout = get_int32(buf); @@ -11336,10 +11335,6 @@ static void packet_inet_command(ErlDrvData e, char* buf, ErlDrvSizeT len) inet_reply_error(desc, EINVAL); return; } - if (!IS_BOUND(desc)) { - inet_reply_error(desc, EINVAL); - return; - } #ifdef HAVE_SCTP if (IS_SCTP(desc)) @@ -11470,6 +11465,8 @@ static int packet_inet_input(udp_descriptor* udesc, HANDLE event) while(packet_count--) { unsigned int len = sizeof(other); + sys_memzero((char *) &other, sizeof(other)); + /* udesc->i_buf is only kept between SCTP fragments */ if (udesc->i_buf == NULL) { udesc->i_bufsz = desc->bufsz + len; @@ -11665,7 +11662,7 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event) (struct sockaddr*) &desc->remote, &sz); if (IS_SOCKET_ERROR(code)) { - desc->state = INET_STATE_BOUND; /* restore state */ + desc->state = INET_STATE_OPEN; /* restore state */ ret = async_error(desc, sock_errno()); goto done; } @@ -11678,7 +11675,7 @@ static int packet_inet_output(udp_descriptor* udesc, HANDLE event) (void *)&error, &sz); if ((code < 0) || error) { - desc->state = INET_STATE_BOUND; /* restore state */ + desc->state = INET_STATE_OPEN; /* restore state */ ret = async_error(desc, error); goto done; } diff --git a/erts/emulator/sys/unix/erl_unix_sys.h b/erts/emulator/sys/unix/erl_unix_sys.h index 241540b894..3a0d23cd36 100644 --- a/erts/emulator/sys/unix/erl_unix_sys.h +++ b/erts/emulator/sys/unix/erl_unix_sys.h @@ -163,7 +163,7 @@ typedef long long ErtsSysHrTime; typedef ErtsMonotonicTime ErtsSystemTime; typedef ErtsSysHrTime ErtsSysPerfCounter; -#define ERTS_MONOTONIC_TIME_MIN (((ErtsMonotonicTime) 1) << 63) +#define ERTS_MONOTONIC_TIME_MIN ((ErtsMonotonicTime) (1ULL << 63)) #define ERTS_MONOTONIC_TIME_MAX (~ERTS_MONOTONIC_TIME_MIN) /* diff --git a/erts/emulator/sys/win32/erl_win_sys.h b/erts/emulator/sys/win32/erl_win_sys.h index 7bdfac168b..04fbf23109 100644 --- a/erts/emulator/sys/win32/erl_win_sys.h +++ b/erts/emulator/sys/win32/erl_win_sys.h @@ -187,7 +187,7 @@ typedef ErtsMonotonicTime ErtsSysPerfCounter; ErtsSystemTime erts_os_system_time(void); -#define ERTS_MONOTONIC_TIME_MIN (((ErtsMonotonicTime) 1) << 63) +#define ERTS_MONOTONIC_TIME_MIN ((ErtsMonotonicTime) (1ULL << 63)) #define ERTS_MONOTONIC_TIME_MAX (~ERTS_MONOTONIC_TIME_MIN) #define ERTS_HAVE_OS_MONOTONIC_TIME_SUPPORT 1 diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl index 22a1c0b765..95042ac802 100644 --- a/erts/emulator/test/bs_construct_SUITE.erl +++ b/erts/emulator/test/bs_construct_SUITE.erl @@ -23,6 +23,7 @@ -module(bs_construct_SUITE). -export([all/0, suite/0, + init_per_suite/1, end_per_suite/1, test1/1, test2/1, test3/1, test4/1, test5/1, testf/1, not_used/1, in_guard/1, mem_leak/1, coerce_to_float/1, bjorn/1, @@ -43,6 +44,12 @@ all() -> copy_writable_binary, kostis, dynamic, bs_add, otp_7422, zero_width, bad_append, bs_add_overflow]. +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + application:stop(os_mon). + big(1) -> 57285702734876389752897683. @@ -882,10 +889,14 @@ append_unit_16(Bin) -> %% Produce a large result of bs_add that, if cast to signed int, would overflow %% into a negative number that fits a smallnum. -bs_add_overflow(Config) -> +bs_add_overflow(_Config) -> + Memsize = memsize(), + io:format("Memsize = ~w Bytes~n", [Memsize]), case erlang:system_info(wordsize) of 8 -> {skip, "64-bit architecture"}; + _ when Memsize < (2 bsl 30) -> + {skip, "Less then 2 GB of memory"}; 4 -> Large = <<0:((1 bsl 30)-1)>>, {'EXIT',{system_limit,_}} = @@ -894,5 +905,10 @@ bs_add_overflow(Config) -> Large/bits>>), ok end. - + id(I) -> I. + +memsize() -> + application:ensure_all_started(os_mon), + {Tot,_Used,_} = memsup:get_memory_data(), + Tot. diff --git a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c index e38bececde..d92933a096 100644 --- a/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c +++ b/erts/emulator/test/dirty_nif_SUITE_data/dirty_nif_SUITE.c @@ -48,7 +48,8 @@ static ERL_NIF_TERM dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ char s[10]; ErlNifBinary b; if (have_dirty_schedulers()) { - assert(enif_is_on_dirty_scheduler(env)); + assert(ERL_NIF_THR_DIRTY_CPU_SCHEDULER == enif_thread_type() + || ERL_NIF_THR_DIRTY_IO_SCHEDULER == enif_thread_type()); } assert(argc == 3); enif_get_int(env, argv[0], &n); @@ -65,7 +66,7 @@ static ERL_NIF_TERM call_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM int n; char s[10]; ErlNifBinary b; - assert(!enif_is_on_dirty_scheduler(env)); + assert(ERL_NIF_THR_NORMAL_SCHEDULER == enif_thread_type()); if (argc != 3) return enif_make_badarg(env); if (have_dirty_schedulers()) { @@ -151,7 +152,8 @@ dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) ErlNifPid pid; ErlNifEnv* msg_env = NULL; - assert(enif_is_on_dirty_scheduler(env)); + assert(ERL_NIF_THR_DIRTY_CPU_SCHEDULER == enif_thread_type() + || ERL_NIF_THR_DIRTY_IO_SCHEDULER == enif_thread_type()); /* If we get a pid argument, it indicates a process involved in the test wants a message from us. Prior to the sleep we send a 'ready' @@ -221,7 +223,8 @@ static ERL_NIF_TERM dirty_heap_access_nif(ErlNifEnv* env, int argc, const ERL_NI { ERL_NIF_TERM res = enif_make_list(env, 0); int i; - assert(enif_is_on_dirty_scheduler(env)); + assert(ERL_NIF_THR_DIRTY_CPU_SCHEDULER == enif_thread_type() + || ERL_NIF_THR_DIRTY_IO_SCHEDULER == enif_thread_type()); for (i = 0; i < 1000; i++) res = enif_make_list_cell(env, enif_make_copy(env, argv[0]), res); diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl index 26780f6017..c6939a695d 100644 --- a/erts/emulator/test/distribution_SUITE.erl +++ b/erts/emulator/test/distribution_SUITE.erl @@ -187,8 +187,13 @@ bulk_sendsend2(Terms, BinSize, BusyBufSize) -> {ok, NodeSend} = start_node(bulk_sender, "+zdbbl " ++ integer_to_list(BusyBufSize)), _Send = spawn(NodeSend, erlang, apply, [fun sendersender/4, [self(), Recv, Bin, Terms]]), {Elapsed, {_TermsN, SizeN}, MonitorCount} = - receive {sendersender, BigRes} -> - BigRes + receive + %% On some platforms (windows), the time taken is 0 so we + %% simulate that some little time has passed. + {sendersender, {0.0,T,MC}} -> + {0.0015, T, MC}; + {sendersender, BigRes} -> + BigRes end, stop_node(NodeRecv), stop_node(NodeSend), @@ -1042,10 +1047,13 @@ atom_roundtrip_r15b(Config) when is_list(Config) -> ct:timetrap({minutes, 6}), AtomData = atom_data(), verify_atom_data(AtomData), - {ok, Node} = start_node(Config, [], "r15b"), - do_atom_roundtrip(Node, AtomData), - stop_node(Node), - ok; + case start_node(Config, [], "r15b") of + {ok, Node} -> + do_atom_roundtrip(Node, AtomData), + stop_node(Node); + {error, timeout} -> + {skip,"Unable to start OTP R15B release"} + end; false -> {skip,"No OTP R15B available"} end. diff --git a/erts/emulator/test/lttng_SUITE.erl b/erts/emulator/test/lttng_SUITE.erl index 1360751aee..6b7ad836f5 100644 --- a/erts/emulator/test/lttng_SUITE.erl +++ b/erts/emulator/test/lttng_SUITE.erl @@ -80,34 +80,34 @@ end_per_testcase(Case, _Config) -> ok. %% Not tested yet -%% com_ericsson_otp:driver_process_exit -%% com_ericsson_otp:driver_event +%% org_erlang_otp:driver_process_exit +%% org_erlang_otp:driver_event %% tracepoints %% -%% com_ericsson_otp:carrier_pool_get -%% com_ericsson_otp:carrier_pool_put -%% com_ericsson_otp:carrier_destroy -%% com_ericsson_otp:carrier_create -%% com_ericsson_otp:aio_pool_put -%% com_ericsson_otp:aio_pool_get -%% com_ericsson_otp:driver_control -%% com_ericsson_otp:driver_call -%% com_ericsson_otp:driver_finish -%% com_ericsson_otp:driver_ready_async -%% com_ericsson_otp:driver_process_exit -%% com_ericsson_otp:driver_stop -%% com_ericsson_otp:driver_flush -%% com_ericsson_otp:driver_stop_select -%% com_ericsson_otp:driver_timeout -%% com_ericsson_otp:driver_event -%% com_ericsson_otp:driver_ready_output -%% com_ericsson_otp:driver_ready_input -%% com_ericsson_otp:driver_output -%% com_ericsson_otp:driver_outputv -%% com_ericsson_otp:driver_init -%% com_ericsson_otp:driver_start -%% com_ericsson_otp:scheduler_poll +%% org_erlang_otp:carrier_pool_get +%% org_erlang_otp:carrier_pool_put +%% org_erlang_otp:carrier_destroy +%% org_erlang_otp:carrier_create +%% org_erlang_otp:aio_pool_put +%% org_erlang_otp:aio_pool_get +%% org_erlang_otp:driver_control +%% org_erlang_otp:driver_call +%% org_erlang_otp:driver_finish +%% org_erlang_otp:driver_ready_async +%% org_erlang_otp:driver_process_exit +%% org_erlang_otp:driver_stop +%% org_erlang_otp:driver_flush +%% org_erlang_otp:driver_stop_select +%% org_erlang_otp:driver_timeout +%% org_erlang_otp:driver_event +%% org_erlang_otp:driver_ready_output +%% org_erlang_otp:driver_ready_input +%% org_erlang_otp:driver_output +%% org_erlang_otp:driver_outputv +%% org_erlang_otp:driver_init +%% org_erlang_otp:driver_start +%% org_erlang_otp:scheduler_poll %% %% Testcases @@ -117,48 +117,48 @@ t_lttng_list(_Config) -> {ok, _} = cmd("lttng list -u"), ok. -%% com_ericsson_otp:carrier_pool_get -%% com_ericsson_otp:carrier_pool_put +%% org_erlang_otp:carrier_pool_get +%% org_erlang_otp:carrier_pool_put t_carrier_pool(Config) -> case have_carriers(ets_alloc) of false -> {skip, "No Memory Carriers configured on system."}; true -> - ok = lttng_start_event("com_ericsson_otp:carrier_pool*", Config), + ok = lttng_start_event("org_erlang_otp:carrier_pool*", Config), ok = ets_load(), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:carrier_pool_get", Res), - ok = check_tracepoint("com_ericsson_otp:carrier_pool_put", Res), + ok = check_tracepoint("org_erlang_otp:carrier_pool_get", Res), + ok = check_tracepoint("org_erlang_otp:carrier_pool_put", Res), ok end. -%% com_ericsson_otp:carrier_destroy -%% com_ericsson_otp:carrier_create +%% org_erlang_otp:carrier_destroy +%% org_erlang_otp:carrier_create t_memory_carrier(Config) -> case have_carriers(ets_alloc) of false -> {skip, "No Memory Carriers configured on system."}; true -> - ok = lttng_start_event("com_ericsson_otp:carrier_*", Config), + ok = lttng_start_event("org_erlang_otp:carrier_*", Config), ok = ets_load(), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:carrier_destroy", Res), - ok = check_tracepoint("com_ericsson_otp:carrier_create", Res), + ok = check_tracepoint("org_erlang_otp:carrier_destroy", Res), + ok = check_tracepoint("org_erlang_otp:carrier_create", Res), ok end. -%% com_ericsson_otp:aio_pool_put -%% com_ericsson_otp:aio_pool_get +%% org_erlang_otp:aio_pool_put +%% org_erlang_otp:aio_pool_get t_async_io_pool(Config) -> case have_async_threads() of false -> {skip, "No Async Threads configured on system."}; true -> - ok = lttng_start_event("com_ericsson_otp:aio_pool_*", Config), + ok = lttng_start_event("org_erlang_otp:aio_pool_*", Config), Path1 = proplists:get_value(priv_dir, Config), {ok, [[Path2]]} = init:get_argument(home), @@ -168,16 +168,16 @@ t_async_io_pool(Config) -> {ok, _} = file:list_dir(Path2), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:aio_pool_put", Res), - ok = check_tracepoint("com_ericsson_otp:aio_pool_get", Res), + ok = check_tracepoint("org_erlang_otp:aio_pool_put", Res), + ok = check_tracepoint("org_erlang_otp:aio_pool_get", Res), ok end. -%% com_ericsson_otp:driver_start -%% com_ericsson_otp:driver_stop +%% org_erlang_otp:driver_start +%% org_erlang_otp:driver_stop t_driver_start_stop(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_*", Config), + ok = lttng_start_event("org_erlang_otp:driver_*", Config), timer:sleep(500), Path = proplists:get_value(priv_dir, Config), Name = filename:join(Path, "sometext.txt"), @@ -186,35 +186,35 @@ t_driver_start_stop(Config) -> {ok, Bin} = file:read_file(Name), timer:sleep(500), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_start", Res), - ok = check_tracepoint("com_ericsson_otp:driver_stop", Res), - ok = check_tracepoint("com_ericsson_otp:driver_control", Res), - ok = check_tracepoint("com_ericsson_otp:driver_outputv", Res), - ok = check_tracepoint("com_ericsson_otp:driver_ready_async", Res), + ok = check_tracepoint("org_erlang_otp:driver_start", Res), + ok = check_tracepoint("org_erlang_otp:driver_stop", Res), + ok = check_tracepoint("org_erlang_otp:driver_control", Res), + ok = check_tracepoint("org_erlang_otp:driver_outputv", Res), + ok = check_tracepoint("org_erlang_otp:driver_ready_async", Res), ok. -%% com_ericsson_otp:driver_control -%% com_ericsson_otp:driver_outputv -%% com_ericsson_otp:driver_ready_async +%% org_erlang_otp:driver_control +%% org_erlang_otp:driver_outputv +%% org_erlang_otp:driver_ready_async t_driver_control_ready_async(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_control", Config), - ok = lttng_start_event("com_ericsson_otp:driver_outputv", Config), - ok = lttng_start_event("com_ericsson_otp:driver_ready_async", Config), + ok = lttng_start_event("org_erlang_otp:driver_control", Config), + ok = lttng_start_event("org_erlang_otp:driver_outputv", Config), + ok = lttng_start_event("org_erlang_otp:driver_ready_async", Config), Path = proplists:get_value(priv_dir, Config), Name = filename:join(Path, "sometext.txt"), Bin = txt(), ok = file:write_file(Name, Bin), {ok, Bin} = file:read_file(Name), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_control", Res), - ok = check_tracepoint("com_ericsson_otp:driver_outputv", Res), - ok = check_tracepoint("com_ericsson_otp:driver_ready_async", Res), + ok = check_tracepoint("org_erlang_otp:driver_control", Res), + ok = check_tracepoint("org_erlang_otp:driver_outputv", Res), + ok = check_tracepoint("org_erlang_otp:driver_ready_async", Res), ok. -%% com_ericsson_otp:driver_ready_input -%% com_ericsson_otp:driver_ready_output +%% org_erlang_otp:driver_ready_input +%% org_erlang_otp:driver_ready_output t_driver_ready_input_output(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_ready_*", Config), + ok = lttng_start_event("org_erlang_otp:driver_ready_*", Config), timer:sleep(500), Me = self(), Pid = spawn_link(fun() -> tcp_server(Me, active) end), @@ -230,15 +230,15 @@ t_driver_ready_input_output(Config) -> timer:sleep(500), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_ready_input", Res), - ok = check_tracepoint("com_ericsson_otp:driver_ready_output", Res), + ok = check_tracepoint("org_erlang_otp:driver_ready_input", Res), + ok = check_tracepoint("org_erlang_otp:driver_ready_output", Res), ok. -%% com_ericsson_otp:driver_stop_select -%% com_ericsson_otp:driver_timeout +%% org_erlang_otp:driver_stop_select +%% org_erlang_otp:driver_timeout t_driver_timeout(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_*", Config), + ok = lttng_start_event("org_erlang_otp:driver_*", Config), Me = self(), Pid = spawn_link(fun() -> tcp_server(Me, timeout) end), receive {Pid, accept} -> ok end, @@ -247,16 +247,16 @@ t_driver_timeout(Config) -> receive {Pid, done} -> ok end, ok = gen_tcp:close(Sock), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_timeout", Res), - ok = check_tracepoint("com_ericsson_otp:driver_stop_select", Res), + ok = check_tracepoint("org_erlang_otp:driver_timeout", Res), + ok = check_tracepoint("org_erlang_otp:driver_stop_select", Res), ok. -%% com_ericsson_otp:driver_call -%% com_ericsson_otp:driver_output -%% com_ericsson_otp:driver_init -%% com_ericsson_otp:driver_finish +%% org_erlang_otp:driver_call +%% org_erlang_otp:driver_output +%% org_erlang_otp:driver_init +%% org_erlang_otp:driver_finish t_driver_caller(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_*", Config), + ok = lttng_start_event("org_erlang_otp:driver_*", Config), Drv = 'caller_drv', os:putenv("CALLER_DRV_USE_OUTPUTV", "false"), @@ -282,25 +282,25 @@ t_driver_caller(Config) -> erl_ddll:unload_driver(Drv), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_call", Res), - ok = check_tracepoint("com_ericsson_otp:driver_output", Res), - ok = check_tracepoint("com_ericsson_otp:driver_init", Res), - ok = check_tracepoint("com_ericsson_otp:driver_finish", Res), + ok = check_tracepoint("org_erlang_otp:driver_call", Res), + ok = check_tracepoint("org_erlang_otp:driver_output", Res), + ok = check_tracepoint("org_erlang_otp:driver_init", Res), + ok = check_tracepoint("org_erlang_otp:driver_finish", Res), ok. -%% com_ericsson_otp:scheduler_poll +%% org_erlang_otp:scheduler_poll t_scheduler_poll(Config) -> - ok = lttng_start_event("com_ericsson_otp:scheduler_poll", Config), + ok = lttng_start_event("org_erlang_otp:scheduler_poll", Config), ok = memory_load(), Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:scheduler_poll", Res), + ok = check_tracepoint("org_erlang_otp:scheduler_poll", Res), ok. -%% com_ericsson_otp:driver_flush +%% org_erlang_otp:driver_flush t_driver_flush(Config) -> - ok = lttng_start_event("com_ericsson_otp:driver_flush", Config), + ok = lttng_start_event("org_erlang_otp:driver_flush", Config), Me = self(), Pid = spawn_link(fun() -> tcp_server(Me, passive_no_read) end), @@ -324,7 +324,7 @@ t_driver_flush(Config) -> receive {Pid, done} -> ok end, Res = lttng_stop_and_view(Config), - ok = check_tracepoint("com_ericsson_otp:driver_flush", Res), + ok = check_tracepoint("org_erlang_otp:driver_flush", Res), ok. %% @@ -416,29 +416,29 @@ tcp_server(Pid, Type) -> txt() -> <<"%% tracepoints\n" "%%\n" - "%% com_ericsson_otp:carrier_pool_get\n" - "%% com_ericsson_otp:carrier_pool_put\n" - "%% com_ericsson_otp:carrier_destroy\n" - "%% com_ericsson_otp:carrier_create\n" - "%% com_ericsson_otp:aio_pool_put\n" - "%% com_ericsson_otp:aio_pool_get\n" - "%% com_ericsson_otp:driver_control\n" - "%% com_ericsson_otp:driver_call\n" - "%% com_ericsson_otp:driver_finish\n" - "%% com_ericsson_otp:driver_ready_async\n" - "%% com_ericsson_otp:driver_process_exit\n" - "%% com_ericsson_otp:driver_stop\n" - "%% com_ericsson_otp:driver_flush\n" - "%% com_ericsson_otp:driver_stop_select\n" - "%% com_ericsson_otp:driver_timeout\n" - "%% com_ericsson_otp:driver_event\n" - "%% com_ericsson_otp:driver_ready_output\n" - "%% com_ericsson_otp:driver_ready_input\n" - "%% com_ericsson_otp:driver_output\n" - "%% com_ericsson_otp:driver_outputv\n" - "%% com_ericsson_otp:driver_init\n" - "%% com_ericsson_otp:driver_start\n" - "%% com_ericsson_otp:scheduler_poll">>. + "%% org_erlang_otp:carrier_pool_get\n" + "%% org_erlang_otp:carrier_pool_put\n" + "%% org_erlang_otp:carrier_destroy\n" + "%% org_erlang_otp:carrier_create\n" + "%% org_erlang_otp:aio_pool_put\n" + "%% org_erlang_otp:aio_pool_get\n" + "%% org_erlang_otp:driver_control\n" + "%% org_erlang_otp:driver_call\n" + "%% org_erlang_otp:driver_finish\n" + "%% org_erlang_otp:driver_ready_async\n" + "%% org_erlang_otp:driver_process_exit\n" + "%% org_erlang_otp:driver_stop\n" + "%% org_erlang_otp:driver_flush\n" + "%% org_erlang_otp:driver_stop_select\n" + "%% org_erlang_otp:driver_timeout\n" + "%% org_erlang_otp:driver_event\n" + "%% org_erlang_otp:driver_ready_output\n" + "%% org_erlang_otp:driver_ready_input\n" + "%% org_erlang_otp:driver_output\n" + "%% org_erlang_otp:driver_outputv\n" + "%% org_erlang_otp:driver_init\n" + "%% org_erlang_otp:driver_start\n" + "%% org_erlang_otp:scheduler_poll">>. load_driver(Dir, Driver) -> case erl_ddll:load_driver(Dir, Driver) of diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index 8955e62df5..90d2bd8c5d 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -21,6 +21,7 @@ -module(monitor_SUITE). -include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). -export([all/0, suite/0, groups/0, case_1/1, case_1a/1, case_2/1, case_2a/1, mon_e_1/1, demon_e_1/1, demon_1/1, @@ -706,7 +707,7 @@ named_down(Config) when is_list(Config) -> spawn_opt(fun () -> WFun = fun (F, hej) -> F(F, hopp); -(F, hopp) -> F(F, hej) + (F, hopp) -> F(F, hej) end, NoSchedulers = erlang:system_info(schedulers_online), lists:foreach(fun (_) -> @@ -726,13 +727,14 @@ named_down(Config) when is_list(Config) -> NamedProc = spawn_link(fun () -> receive after infinity -> ok end end), - true = register(Name, NamedProc), + ?assertEqual(true, register(Name, NamedProc)), unlink(NamedProc), exit(NamedProc, bang), Mon = erlang:monitor(process, Name), - receive {'DOWN',Mon, _, _, _} -> ok end, - true = register(Name, self()), - true = unregister(Name), + receive {'DOWN',Mon, _, _, bang} -> ok + after 3000 -> ?assert(false) end, + ?assertEqual(true, register(Name, self())), + ?assertEqual(true, unregister(Name)), process_flag(priority,Prio), ok. diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index 79abcbde5f..ee07699884 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -74,27 +74,68 @@ %% --export([all/0, suite/0, groups/0, - init_per_testcase/2, end_per_testcase/2, - init_per_suite/1, end_per_suite/1, - stream_small/1, stream_big/1, - basic_ping/1, slow_writes/1, bad_packet/1, bad_port_messages/1, - mul_basic/1, mul_slow_writes/1, - dying_port/1, port_program_with_path/1, - open_input_file_port/1, open_output_file_port/1, - count_fds/1, - iter_max_ports/1, eof/1, input_only/1, output_only/1, - name1/1, - t_binary/1, parallell/1, t_exit/1, - env/1, huge_env/1, bad_env/1, cd/1, exit_status/1, - bad_args/1, - tps_16_bytes/1, tps_1K/1, line/1, stderr_to_stdout/1, - otp_3906/1, otp_4389/1, win_massive/1, win_massive_client/1, - mix_up_ports/1, otp_5112/1, otp_5119/1, otp_6224/1, - exit_status_multi_scheduling_block/1, ports/1, - spawn_driver/1, spawn_executable/1, close_deaf_port/1, - port_setget_data/1, - unregister_name/1, parallelism_option/1]). +-export([all/0, suite/0, groups/0, init_per_testcase/2, end_per_testcase/2, + init_per_suite/1, end_per_suite/1]). +-export([ + bad_args/1, + bad_env/1, + bad_packet/1, + bad_port_messages/1, + basic_ping/1, + cd/1, + close_deaf_port/1, + count_fds/1, + dying_port/1, + env/1, + eof/1, + exit_status/1, + exit_status_multi_scheduling_block/1, + huge_env/1, + input_only/1, + iter_max_ports/1, + line/1, + mix_up_ports/1, + mon_port_invalid_type/1, + mon_port_bad_named/1, + mon_port_bad_remote_on_local/1, + mon_port_local/1, + mon_port_name_demonitor/1, + mon_port_named/1, + mon_port_origin_dies/1, + mon_port_pid_demonitor/1, + mon_port_remote_on_remote/1, + mon_port_driver_die/1, + mon_port_driver_die_demonitor/1, + mul_basic/1, + mul_slow_writes/1, + name1/1, + open_input_file_port/1, + open_output_file_port/1, + otp_3906/1, + otp_4389/1, + otp_5112/1, + otp_5119/1, + otp_6224/1, + output_only/1, + parallelism_option/1, + parallell/1, + port_program_with_path/1, + port_setget_data/1, + ports/1, + slow_writes/1, + spawn_driver/1, + spawn_executable/1, + stderr_to_stdout/1, + stream_big/1, + stream_small/1, + t_binary/1, + t_exit/1, + tps_16_bytes/1, + tps_1K/1, + unregister_name/1, + win_massive/1, + win_massive_client/1 +]). -export([do_iter_max_ports/2]). @@ -105,12 +146,13 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). +-include_lib("eunit/include/eunit.hrl"). suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {seconds, 10}}]. -all() -> +all() -> [otp_6224, {group, stream}, basic_ping, slow_writes, bad_packet, bad_port_messages, {group, options}, {group, multiple_packets}, parallell, dying_port, @@ -123,14 +165,32 @@ all() -> exit_status_multi_scheduling_block, ports, spawn_driver, spawn_executable, close_deaf_port, unregister_name, port_setget_data, - parallelism_option]. - -groups() -> + parallelism_option, + mon_port_invalid_type, + mon_port_local, + mon_port_remote_on_remote, + mon_port_bad_remote_on_local, + mon_port_origin_dies, + mon_port_named, + mon_port_bad_named, + mon_port_pid_demonitor, + mon_port_name_demonitor, + mon_port_driver_die, + mon_port_driver_die_demonitor + ]. + +groups() -> [{stream, [], [stream_small, stream_big]}, {options, [], [t_binary, eof, input_only, output_only]}, {multiple_packets, [], [mul_basic, mul_slow_writes]}, {tps, [], [tps_16_bytes, tps_1K]}]. +init_per_testcase(Case, Config) when Case =:= mon_port_driver_die; + Case =:= mon_port_driver_die_demonitor -> + case erlang:system_info(schedulers_online) of + 1 -> {skip, "Need 2 schedulers to run testcase"}; + _ -> Config + end; init_per_testcase(Case, Config) -> [{testcase, Case} |Config]. @@ -160,7 +220,7 @@ do_win_massive() -> ct:timetrap({minutes, 6}), SuiteDir = filename:dirname(code:which(?MODULE)), Ports = " +Q 8192", - {ok, Node} = + {ok, Node} = test_server:start_node(win_massive, slave, [{args, " -pa " ++ SuiteDir ++ Ports}]), @@ -169,7 +229,7 @@ do_win_massive() -> ok. win_massive_client(N) -> - {ok,P}=gen_tcp:listen(?WIN_MASSIVE_PORT,[{reuseaddr,true}]), + {ok,P}=gen_tcp:listen(?WIN_MASSIVE_PORT,[{reuseaddr,true}]), L = win_massive_loop(P,N), Len = length(L), lists:foreach(fun(E) -> @@ -278,7 +338,7 @@ bad_port_messages(Config) when is_list(Config) -> bad_message(PortTest, {self(),{connect,no_pid}}), ok. -bad_message(PortTest, Message) -> +bad_message(PortTest, Message) -> P = open_port({spawn,PortTest}, []), P ! Message, receive @@ -773,7 +833,7 @@ line(Config) when is_list(Config) -> S1 = lists:flatten(io_lib:format("-l~w", [length(L1)])), io:format("S1 = ~w, L1 = ~w~n", [S1,L1]), port_expect(Config,[{L1, - [{eol, Packet1}, {noeol, Packet2}, eof]}], 0, + [{eol, Packet1}, {noeol, Packet2}, eof]}], 0, S1, [{line,Siz},eof]), %% Test that lonely <CR> Don't get treated as newlines port_expect(Config,[{lists:append([Packet1, [13], Packet2, @@ -844,9 +904,9 @@ env(Config) when is_list(Config) -> {"glurf","a glorfy string"}]), %% A lot of non existing variables (mingled with existing) - NotExistingList = [{lists:flatten(io_lib:format("V~p_not_existing",[X])),false} + NotExistingList = [{lists:flatten(io_lib:format("V~p_not_existing",[X])),false} || X <- lists:seq(1,150)], - ExistingList = [{lists:flatten(io_lib:format("V~p_existing",[X])),"a_value"} + ExistingList = [{lists:flatten(io_lib:format("V~p_existing",[X])),"a_value"} || X <- lists:seq(1,150)], env_slave(Temp, lists:sort(ExistingList ++ NotExistingList)), ok. @@ -1320,22 +1380,22 @@ spawn_driver(Config) when is_list(Config) -> ok = load_driver(Path, "echo_drv"), Port = erlang:open_port({spawn_driver, "echo_drv"}, []), Port ! {self(), {command, "Hello port!"}}, - receive - {Port, {data, "Hello port!"}} = Msg1 -> + receive + {Port, {data, "Hello port!"}} = Msg1 -> io:format("~p~n", [Msg1]), - ok; + ok; Other -> ct:fail({unexpected, Other}) end, Port ! {self(), close}, receive {Port, closed} -> ok end, - Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, + Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, []), - receive - {Port2, {data, "Hello port?"}} = Msg2 -> + receive + {Port2, {data, "Hello port?"}} = Msg2 -> io:format("~p~n", [Msg2]), - ok; + ok; Other2 -> ct:fail({unexpected2, Other2}) end, @@ -1354,23 +1414,23 @@ parallelism_option(Config) when is_list(Config) -> [{parallelism, true}]), {parallelism, true} = erlang:port_info(Port, parallelism), Port ! {self(), {command, "Hello port!"}}, - receive - {Port, {data, "Hello port!"}} = Msg1 -> + receive + {Port, {data, "Hello port!"}} = Msg1 -> io:format("~p~n", [Msg1]), - ok; + ok; Other -> ct:fail({unexpected, Other}) end, Port ! {self(), close}, receive {Port, closed} -> ok end, - Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, + Port2 = erlang:open_port({spawn_driver, "echo_drv -Hello port?"}, [{parallelism, false}]), {parallelism, false} = erlang:port_info(Port2, parallelism), - receive - {Port2, {data, "Hello port?"}} = Msg2 -> + receive + {Port2, {data, "Hello port?"}} = Msg2 -> io:format("~p~n", [Msg2]), - ok; + ok; Other2 -> ct:fail({unexpected2, Other2}) end, @@ -1389,20 +1449,20 @@ spawn_executable(Config) when is_list(Config) -> ["echo_args"] = run_echo_args(DataDir,[binary, "echo_args"]), ["echo_arguments"] = run_echo_args(DataDir,["echo_arguments"]), ["echo_arguments"] = run_echo_args(DataDir,[binary, "echo_arguments"]), - [ExactFile1,"hello world","dlrow olleh"] = + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[ExactFile1,"hello world","dlrow olleh"]), [ExactFile1] = run_echo_args(DataDir,[default]), [ExactFile1] = run_echo_args(DataDir,[binary, default]), - [ExactFile1,"hello world","dlrow olleh"] = + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[switch_order,ExactFile1,"hello world", "dlrow olleh"]), - [ExactFile1,"hello world","dlrow olleh"] = + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[binary,switch_order,ExactFile1,"hello world", "dlrow olleh"]), [ExactFile1,"hello world","dlrow olleh"] = run_echo_args(DataDir,[default,"hello world","dlrow olleh"]), - [ExactFile1,"hello world","dlrow olleh"] = + [ExactFile1,"hello world","dlrow olleh"] = run_echo_args_2("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\""), [ExactFile1,"hello world","dlrow olleh"] = run_echo_args_2(unicode:characters_to_binary("\""++ExactFile1++"\" "++"\"hello world\" \"dlrow olleh\"")), @@ -1418,7 +1478,7 @@ spawn_executable(Config) when is_list(Config) -> [ExactFile2] = run_echo_args(SpaceDir,[]), ["echo_args"] = run_echo_args(SpaceDir,["echo_args"]), ["echo_arguments"] = run_echo_args(SpaceDir,["echo_arguments"]), - [ExactFile2,"hello world","dlrow olleh"] = + [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[ExactFile2,"hello world","dlrow olleh"]), [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[binary, ExactFile2,"hello world","dlrow olleh"]), @@ -1429,16 +1489,16 @@ spawn_executable(Config) when is_list(Config) -> run_echo_args(SpaceDir,[binary, ExactFile2,"hello \"world\"","\"dlrow\" olleh"]), [ExactFile2] = run_echo_args(SpaceDir,[default]), - [ExactFile2,"hello world","dlrow olleh"] = + [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[switch_order,ExactFile2,"hello world", "dlrow olleh"]), - [ExactFile2,"hello world","dlrow olleh"] = + [ExactFile2,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,[default,"hello world","dlrow olleh"]), - [ExactFile2,"hello world","dlrow olleh"] = + [ExactFile2,"hello world","dlrow olleh"] = run_echo_args_2("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\""), [ExactFile2,"hello world","dlrow olleh"] = run_echo_args_2(unicode:characters_to_binary("\""++ExactFile2++"\" "++"\"hello world\" \"dlrow olleh\"")), - ExeExt = + ExeExt = case string:to_lower(lists:last(string:tokens(ExactFile2,"."))) of "exe" -> ".exe"; @@ -1452,17 +1512,17 @@ spawn_executable(Config) when is_list(Config) -> [ExactFile3] = run_echo_args(SpaceDir,Executable2,[]), ["echo_args"] = run_echo_args(SpaceDir,Executable2,["echo_args"]), ["echo_arguments"] = run_echo_args(SpaceDir,Executable2,["echo_arguments"]), - [ExactFile3,"hello world","dlrow olleh"] = + [ExactFile3,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,Executable2,[ExactFile3,"hello world","dlrow olleh"]), [ExactFile3] = run_echo_args(SpaceDir,Executable2,[default]), - [ExactFile3,"hello world","dlrow olleh"] = + [ExactFile3,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,Executable2, [switch_order,ExactFile3,"hello world", "dlrow olleh"]), - [ExactFile3,"hello world","dlrow olleh"] = + [ExactFile3,"hello world","dlrow olleh"] = run_echo_args(SpaceDir,Executable2, [default,"hello world","dlrow olleh"]), - [ExactFile3,"hello world","dlrow olleh"] = + [ExactFile3,"hello world","dlrow olleh"] = run_echo_args_2("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\""), [ExactFile3,"hello world","dlrow olleh"] = run_echo_args_2(unicode:characters_to_binary("\""++ExactFile3++"\" "++"\"hello world\" \"dlrow olleh\"")), @@ -1510,11 +1570,11 @@ test_bat_file(Dir) -> <<"\r\n">>], file:write_file(Full,list_to_binary(D)), EF = filename:basename(FN), - [DN,"hello","world"] = + [DN,"hello","world"] = run_echo_args(Dir,FN, [default,"hello","world"]), %% The arg0 argumant should be ignored when running batch files - [DN,"hello","world"] = + [DN,"hello","world"] = run_echo_args(Dir,FN, ["knaskurt","hello","world"]), EF = filename:basename(DN), @@ -1533,10 +1593,10 @@ test_sh_file(Dir) -> <<"done\n">>], file:write_file(Full,list_to_binary(D)), chmodplusx(Full), - [Full,"hello","world"] = + [Full,"hello","world"] = run_echo_args(Dir,FN, [default,"hello","world"]), - [Full,"hello","world of spaces"] = + [Full,"hello","world of spaces"] = run_echo_args(Dir,FN, [default,"hello","world of spaces"]), file:write_file(filename:join([Dir,"testfile1"]),<<"testdata1">>), @@ -1544,7 +1604,7 @@ test_sh_file(Dir) -> Pattern = filename:join([Dir,"testfile*"]), L = filelib:wildcard(Pattern), 2 = length(L), - [Full,"hello",Pattern] = + [Full,"hello",Pattern] = run_echo_args(Dir,FN, [default,"hello",Pattern]), ok. @@ -1620,10 +1680,10 @@ mix_up_ports(Config) when is_list(Config) -> ok = load_driver(Path, "echo_drv"), Port = erlang:open_port({spawn, "echo_drv"}, []), Port ! {self(), {command, "Hello port!"}}, - receive - {Port, {data, "Hello port!"}} = Msg1 -> + receive + {Port, {data, "Hello port!"}} = Msg1 -> io:format("~p~n", [Msg1]), - ok; + ok; Other -> ct:fail({unexpected, Other}) end, @@ -1631,7 +1691,7 @@ mix_up_ports(Config) when is_list(Config) -> receive {Port, closed} -> ok end, loop(start, done, fun(P) -> - Q = + Q = (catch erlang:open_port({spawn, "echo_drv"}, [])), %% io:format("~p ", [Q]), if is_port(Q) -> @@ -1642,7 +1702,7 @@ mix_up_ports(Config) when is_list(Config) -> end end), Port ! {self(), {command, "Hello again port!"}}, - receive + receive Msg2 -> ct:fail({unexpected, Msg2}) after 1000 -> @@ -1802,7 +1862,7 @@ exit_status_msb_test(Config, SleepSecs) when is_list(Config) -> %% We want to start port programs from as many schedulers as possible %% and we want these port programs to terminate while multi-scheduling %% is blocked. - %% + %% NoSchedsOnln = erlang:system_info(schedulers_online), Parent = self(), io:format("SleepSecs = ~p~n", [SleepSecs]), @@ -2214,7 +2274,7 @@ ports_snapshots(0, _, _) -> ok; ports_snapshots(Iter, TrafficPid, OtherPorts) -> - TrafficPid ! start, + TrafficPid ! start, receive after 1 -> ok end, Snapshot = erlang:ports(), @@ -2243,7 +2303,7 @@ ports_traffic_stopped(MaxPorts, {PortList, PortCnt}) -> end. ports_traffic_started(MaxPorts, {PortList, PortCnt}, EventList) -> - receive + receive {Pid, stop} -> %%io:format("Traffic stopped in ~p\n",[self()]), Pid ! {self(), EventList, PortList}, @@ -2256,7 +2316,7 @@ ports_traffic_started(MaxPorts, {PortList, PortCnt}, EventList) -> ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList) -> N = uniform(MaxPorts), case N > PortCnt of - true -> % Open port + true -> % Open port P = open_port({spawn, "exit_drv"}, []), %%io:format("Created port ~p\n",[P]), ports_traffic_started(MaxPorts, {[P|PortList], PortCnt+1}, @@ -2270,7 +2330,7 @@ ports_traffic_do(MaxPorts, {PortList, PortCnt}, EventList) -> [{close,P}|EventList]) end. -ports_verify(Ports, PortsAfter, EventList) -> +ports_verify(Ports, PortsAfter, EventList) -> %%io:format("Candidate=~p\nEvents=~p\n", [PortsAfter, EventList]), case lists:sort(Ports) =:= lists:sort(PortsAfter) of true -> @@ -2280,10 +2340,10 @@ ports_verify(Ports, PortsAfter, EventList) -> %% Note that we track the event list "backwards", undoing open/close: case EventList of [{open,P} | Tail] -> - ports_verify(Ports, lists:delete(P,PortsAfter), Tail); + ports_verify(Ports, lists:delete(P,PortsAfter), Tail); [{close,P} | Tail] -> - ports_verify(Ports, [P | PortsAfter], Tail); + ports_verify(Ports, [P | PortsAfter], Tail); [] -> ct:fail("Inconsistent snapshot from erlang:ports()") @@ -2391,3 +2451,227 @@ wait_until(Fun) -> receive after 100 -> ok end, wait_until(Fun) end. + +%% Attempt to monitor pid as port, and port as pid +mon_port_invalid_type(_Config) -> + Port = hd(erlang:ports()), + ?assertError(badarg, erlang:monitor(port, self())), + ?assertError(badarg, erlang:monitor(process, Port)), + ok. + +%% With local port +mon_port_local(Config) -> + Port1 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte + Ref1 = erlang:monitor(port, Port1), + ?assertMatch({proc_monitors, true, port_monitored_by, true}, + port_is_monitored(self(), Port1)), + Port1 ! {self(), {command, <<"1">>}}, % port test will close self immediately + receive ExitP1 -> ?assertMatch({'DOWN', Ref1, port, Port1, _}, ExitP1) + after 1000 -> ?assert(false) end, + ?assertMatch({proc_monitors, false, port_monitored_by, false}, + port_is_monitored(self(), Port1)), + + %% Trying to re-monitor a port which exists but is not healthy will + %% succeed but then will immediately send DOWN + Ref2 = erlang:monitor(port, Port1), + receive ExitP2 -> ?assertMatch({'DOWN', Ref2, port, Port1, _}, ExitP2) + after 1000 -> ?assert(false) end, + ok. + +%% With remote port on remote node (should fail) +mon_port_remote_on_remote(_Config) -> + Port3 = binary_to_term(<<131, 102, % Ext term format: PORT_EXT + 100, 0, 13, "fgsfds@fgsfds", % Node :: ATOM_EXT + 1:32/big, % Id + 0>>), % Creation + ?assertError(badarg, erlang:monitor(port, Port3)), + ok. + +%% Remote port belongs to this node and does not exist +%% Port4 produces #Port<0.167772160> which should not exist in a test run +mon_port_bad_remote_on_local(_Config) -> + Port4 = binary_to_term(<<131, 102, % Ext term format: PORT_EXT + 100, 0, 13, "nonode@nohost", % Node + 167772160:32/big, % Id + 0>>), % Creation + ?assertError(badarg, erlang:monitor(port, Port4)), + ok. + +%% Monitor owner (origin) dies before port is closed +mon_port_origin_dies(Config) -> + Port5 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte + Self5 = self(), + Proc5 = spawn(fun() -> + Self5 ! test5_started, + erlang:monitor(port, Port5), + receive stop -> ok end + end), + erlang:monitor(process, Proc5), % we want to sync with its death + receive test5_started -> ok + after 1000 -> ?assert(false) end, + ?assertMatch({proc_monitors, true, port_monitored_by, true}, + port_is_monitored(Proc5, Port5)), + Proc5 ! stop, + % receive from monitor (removing race condition) + receive ExitP5 -> ?assertMatch({'DOWN', _, process, Proc5, _}, ExitP5) + after 1000 -> ?assert(false) end, + ?assertMatch({proc_monitors, false, port_monitored_by, false}, + port_is_monitored(Proc5, Port5)), + Port5 ! {self(), {command, <<"1">>}}, % make port quit + ok. + +%% Monitor a named port +mon_port_named(Config) -> + Name6 = test_port6, + Port6 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte + erlang:register(Name6, Port6), + erlang:monitor(port, Name6), + ?assertMatch({proc_monitors, true, port_monitored_by, true}, + port_is_monitored(self(), Name6)), + Port6 ! {self(), {command, <<"1">>}}, % port test will close self immediately + receive ExitP6 -> ?assertMatch({'DOWN', _, port, {Name6, _}, _}, ExitP6) + after 1000 -> ?assert(false) end, + ?assertMatch({proc_monitors, false, port_monitored_by, false}, + port_is_monitored(self(), Name6)), + ok. + +%% Named does not exist: Should succeed but immediately send 'DOWN' +mon_port_bad_named(_Config) -> + Name7 = test_port7, + erlang:monitor(port, Name7), + receive {'DOWN', _, port, {Name7, _}, noproc} -> ok + after 1000 -> ?assert(false) end, + ok. + +%% Monitor a pid and demonitor by ref +mon_port_pid_demonitor(Config) -> + Port8 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte + Ref8 = erlang:monitor(port, Port8), + ?assertMatch({proc_monitors, true, port_monitored_by, true}, + port_is_monitored(self(), Port8)), + erlang:demonitor(Ref8), + ?assertMatch({proc_monitors, false, port_monitored_by, false}, + port_is_monitored(self(), Port8)), + Port8 ! {self(), {command, <<"1">>}}, % port test will close self immediately + ok. + +%% Monitor by name and demonitor by ref +mon_port_name_demonitor(Config) -> + Name9 = test_port9, + Port9 = create_port(Config, ["-h1", "-q"]), % will close after we send 1 byte + erlang:register(Name9, Port9), + Ref9 = erlang:monitor(port, Name9), + ?assertMatch({proc_monitors, true, port_monitored_by, true}, + port_is_monitored(self(), Name9)), + erlang:demonitor(Ref9), + ?assertMatch({proc_monitors, false, port_monitored_by, false}, + port_is_monitored(self(), Name9)), + Port9 ! {self(), {command, <<"1">>}}, % port test will close self immediately + ok. + +%% 1. Spawn a port which will sleep 3 seconds +%% 2. Port driver and dies horribly (via C driver_failure call). This should +%% mark port as exiting or something. +%% 3. While the command happens, a monitor is requested on the port +mon_port_driver_die(Config) -> + erlang:process_flag(scheduler, 1), + + Path = proplists:get_value(data_dir, Config), + ok = load_driver(Path, "sleep_failure_drv"), + Port = open_port({spawn, "sleep_failure_drv"}, []), + + Self = self(), + erlang:spawn_opt(fun() -> + timer:sleep(250), + Ref = erlang:monitor(port, Port), + %% Now check that msg actually arrives + receive + {'DOWN', Ref, _Port2, _, _} = M -> Self ! M + after 3000 -> Self ! no_down_message + end + end,[{scheduler, 2}]), + Port ! {self(), {command, "Fail, please!"}}, + receive + A when is_atom(A) -> ?assertEqual(A, 'A_should_be_printed'); + {'DOWN', _R, port, Port, noproc} -> ok; + {'DOWN', _R, _P, _, _} = M -> ct:fail({got_wrong_down,M}) + after 5000 -> ?assert(false) + end, + ok. + + +%% 1. Spawn a port which will sleep 3 seconds +%% 2. Monitor port +%% 3. Port driver and dies horribly (via C driver_failure call). This should +%% mark port as exiting or something. +%% 4. While the command happens, a demonitor is requested on the port +mon_port_driver_die_demonitor(Config) -> + erlang:process_flag(scheduler, 1), + + Path = proplists:get_value(data_dir, Config), + ok = load_driver(Path, "sleep_failure_drv"), + Port = open_port({spawn, "sleep_failure_drv"}, []), + + Self = self(), + erlang:spawn_opt( + fun() -> + Ref = erlang:monitor(port, Port), + Self ! Ref, + timer:sleep(250), + erlang:demonitor(Ref), + %% Now check that msg still arrives, + %% the demon should have arrived after + %% the port exited + receive + {'DOWN', Ref, _Port2, _, _} = M -> Self ! M + after 3000 -> Self ! no_down_message + end + end,[{scheduler, 2}]), + Ref = receive R -> R end, + Port ! {self(), {command, "Fail, please!"}}, + receive + {'DOWN', Ref, port, Port, normal} -> ok; + {'DOWN', _R, _P, _, _} = M -> ct:fail({got_wrong_down,M}) + after 5000 -> ?assert(false) + end, + ok. + +%% @doc Makes a controllable port for testing. Underlying mechanism of this +%% port is not important, only important is our ability to close/kill it or +%% have it monitored. +create_port(Config, Args) -> + DataDir = ?config(data_dir, Config), + %% Borrow port test utility from port SUITE + Program = filename:join([DataDir, "port_test"]), + erlang:open_port({spawn_executable, Program}, [{args, Args}]). + +%% @doc Checks if process Pid exists, and if so, if its monitoring (or not) +%% the Port (or if port doesn't exist, we assume answer is no). +port_is_monitored(Pid, Port) when is_pid(Pid), is_port(Port) -> + %% Variant for when port is a port id (port()) + A = case erlang:process_info(Pid, monitors) of + undefined -> false; + {monitors, ProcMTargets} -> lists:member({port, Port}, ProcMTargets) + end, + B = case erlang:port_info(Port, monitored_by) of + undefined -> false; + {monitored_by, PortMonitors} -> lists:member(Pid, PortMonitors) + end, + {proc_monitors, A, port_monitored_by, B}; +port_is_monitored(Pid, PortName) when is_pid(Pid), is_atom(PortName) -> + %% Variant for when port is an atom + A = case erlang:process_info(Pid, monitors) of + undefined -> false; + {monitors, ProcMTargets} -> + lists:member({port, {PortName, node()}}, ProcMTargets) + end, + B = case erlang:whereis(PortName) of + undefined -> false; % name is not registered or is dead + PortId -> + case erlang:port_info(PortId, monitored_by) of + undefined -> false; % is dead + {monitored_by, PortMonitors} -> + lists:member(Pid, PortMonitors) + end + end, + {proc_monitors, A, port_monitored_by, B}. diff --git a/erts/emulator/test/port_SUITE_data/Makefile.src b/erts/emulator/test/port_SUITE_data/Makefile.src index ff822ae720..fb7685c4b6 100644 --- a/erts/emulator/test/port_SUITE_data/Makefile.src +++ b/erts/emulator/test/port_SUITE_data/Makefile.src @@ -4,7 +4,7 @@ CFLAGS = @CFLAGS@ -I@erl_include@ @DEFS@ CROSSLDFLAGS = @CROSSLDFLAGS@ PROGS = port_test@exe@ echo_args@exe@ dead_port@exe@ -DRIVERS = echo_drv@dll@ exit_drv@dll@ failure_drv@dll@ +DRIVERS = echo_drv@dll@ exit_drv@dll@ failure_drv@dll@ sleep_failure_drv@dll@ all: $(PROGS) $(DRIVERS) port_test.@EMULATOR@ diff --git a/erts/emulator/test/port_SUITE_data/sleep_failure_drv.c b/erts/emulator/test/port_SUITE_data/sleep_failure_drv.c new file mode 100644 index 0000000000..1f52646572 --- /dev/null +++ b/erts/emulator/test/port_SUITE_data/sleep_failure_drv.c @@ -0,0 +1,76 @@ +#include <stdio.h> +#include "erl_driver.h" +#ifdef __WIN32__ +# include <windows.h> +#else +# include <unistd.h> +#endif + +typedef struct _erl_drv_data FailureDrvData; + +static FailureDrvData *failure_drv_start(ErlDrvPort, char *); +static void failure_drv_stop(FailureDrvData *); +static void failure_drv_output(ErlDrvData, char *, ErlDrvSizeT); +static void failure_drv_finish(void); + +static ErlDrvEntry failure_drv_entry = { + NULL, /* init */ + failure_drv_start, + failure_drv_stop, + failure_drv_output, + NULL, /* ready_input */ + NULL, /* ready_output */ + "sleep_failure_drv", + NULL, /* finish */ + NULL, /* handle */ + NULL, /* control */ + NULL, /* timeout */ + NULL, /* outputv */ + NULL, /* ready_async */ + NULL, + NULL, + NULL, + ERL_DRV_EXTENDED_MARKER, + ERL_DRV_EXTENDED_MAJOR_VERSION, + ERL_DRV_EXTENDED_MINOR_VERSION, + 0, + NULL, + NULL, + NULL, +}; + + + +/* ------------------------------------------------------------------------- +** Entry functions +**/ + +DRIVER_INIT(failure_drv) +{ + return &failure_drv_entry; +} + +static FailureDrvData *failure_drv_start(ErlDrvPort port, char *command) { + void *void_ptr; + + return void_ptr = port; +} + +static void failure_drv_stop(FailureDrvData *data_p) { +} + +static void failure_drv_output(ErlDrvData drv_data, char *buf, ErlDrvSizeT len) { + FailureDrvData *data_p = (FailureDrvData *) drv_data; + void *void_ptr; + ErlDrvPort port = void_ptr = data_p; + +#ifdef __WIN32__ + Sleep(3000); +#else + sleep(3); +#endif + driver_failure(port, 0); +} + +static void failure_drv_finish() { +} diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index eaa4026a8a..dae8990f56 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -147,11 +147,7 @@ spawn_with_binaries(Config) when is_list(Config) -> TwoMeg = lists:duplicate(1024, L), Fun = fun() -> spawn(?MODULE, binary_owner, [list_to_binary(TwoMeg)]), receive after 1 -> ok end end, - Iter = case test_server:purify_is_running() of - true -> 10; - false -> 150 - end, - test_server:do_times(Iter, Fun), + test_server:do_times(150, Fun), ok. binary_owner(Bin) when is_binary(Bin) -> @@ -2585,7 +2581,10 @@ enable_internal_state() -> _ -> erts_debug:set_internal_state(available_internal_state, true) end. -sys_mem_cond_run(ReqSizeMB, TestFun) when is_integer(ReqSizeMB) -> +sys_mem_cond_run(OrigReqSizeMB, TestFun) when is_integer(OrigReqSizeMB) -> + %% Debug normally needs more memory, so double the requirement + Debug = erlang:system_info(debug_compiled), + ReqSizeMB = if Debug -> OrigReqSizeMB * 2; true -> OrigReqSizeMB end, case total_memory() of TotMem when is_integer(TotMem), TotMem >= ReqSizeMB -> TestFun(); diff --git a/erts/emulator/test/trace_call_time_SUITE.erl b/erts/emulator/test/trace_call_time_SUITE.erl index 40c8bc4340..6582ad134b 100644 --- a/erts/emulator/test/trace_call_time_SUITE.erl +++ b/erts/emulator/test/trace_call_time_SUITE.erl @@ -351,7 +351,7 @@ combo(Config) when is_list(Config) -> %% Tests tracing of bifs bif(Config) when is_list(Config) -> P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), - M = 1000000, + M = 5000000, %% 2 = erlang:trace_pattern({erlang, binary_to_term, '_'}, true, [call_time]), 2 = erlang:trace_pattern({erlang, term_to_binary, '_'}, true, [call_time]), @@ -381,7 +381,7 @@ bif(Config) when is_list(Config) -> nif(Config) when is_list(Config) -> load_nif(Config), P = erlang:trace_pattern({'_','_','_'}, false, [call_time]), - M = 1000000, + M = 5000000, %% 1 = erlang:trace_pattern({?MODULE, nif_dec, '_'}, true, [call_time]), 1 = erlang:trace_pattern({?MODULE, with_nif, '_'}, true, [call_time]), diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c index e931ae4641..d67b997d6d 100644 --- a/erts/etc/common/heart.c +++ b/erts/etc/common/heart.c @@ -119,6 +119,8 @@ #define HEART_COMMAND_ENV "HEART_COMMAND" #define ERL_CRASH_DUMP_SECONDS_ENV "ERL_CRASH_DUMP_SECONDS" #define HEART_KILL_SIGNAL "HEART_KILL_SIGNAL" +#define HEART_NO_KILL "HEART_NO_KILL" + #define MSG_HDR_SIZE (2) #define MSG_HDR_PLUS_OP_SIZE (3) @@ -524,6 +526,12 @@ static void kill_old_erlang(void){ HANDLE erlh; DWORD exit_code; + char* envvar = NULL; + + envvar = get_env(HEART_NO_KILL); + if (envvar && strcmp(envvar, "TRUE") == 0) + return; + if(heart_beat_kill_pid != 0){ if((erlh = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE | @@ -555,10 +563,14 @@ kill_old_erlang(void){ pid_t pid; int i, res; int sig = SIGKILL; - char *sigenv = NULL; + char *envvar = NULL; + + envvar = get_env(HEART_NO_KILL); + if (envvar && strcmp(envvar, "TRUE") == 0) + return; - sigenv = get_env(HEART_KILL_SIGNAL); - if (sigenv && strcmp(sigenv, "SIGABRT") == 0) { + envvar = get_env(HEART_KILL_SIGNAL); + if (envvar && strcmp(envvar, "SIGABRT") == 0) { print_error("kill signal SIGABRT requested"); sig = SIGABRT; } diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 8f9945c4b4..15fb718c47 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -1807,52 +1807,53 @@ define etp-process-info # Args: Process* # printf " Pid: " - etp-1 ($arg0)->common.id + set $etp_proc = ((Process*)$arg0) + etp-1 $etp_proc->common.id printf "\n State: " - etp-proc-state $arg0 + etp-proc-state $etp_proc if $proxy_process != 0 - printf " Pointer: (Process *) %p\n", $arg0 + printf " Pointer: (Process *) %p\n", $etp_proc printf " *** PROXY process struct *** refer to: \n" - etp-pid2proc-1 $arg0->common.id + etp-pid2proc-1 $etp_proc->common.id etp-process-info $proc else - if (*(((Uint32 *) &(((Process *) $arg0)->state))) & 0x4) == 0 - if ($arg0->common.u.alive.reg) + if (*(((Uint32 *) &($etp_proc->state))) & 0x4) == 0 + if ($etp_proc->common.u.alive.reg) printf " Registered name: " - etp-1 $arg0->common.u.alive.reg->name + etp-1 $etp_proc->common.u.alive.reg->name printf "\n" end end - if ($arg0->current) + if ($etp_proc->current) printf " Current function: " - etp-1 $arg0->current[0] + etp-1 $etp_proc->current[0] printf ":" - etp-1 $arg0->current[1] - printf "/%d\n", $arg0->current[2] + etp-1 $etp_proc->current[1] + printf "/%d\n", $etp_proc->current[2] end - if ($arg0->cp) + if ($etp_proc->cp) printf " CP: " - etp-cp-1 $arg0->cp + etp-cp-1 $etp_proc->cp printf "\n" end - if ($arg0->i) + if ($etp_proc->i) printf " I: " - etp-cp-1 $arg0->i + etp-cp-1 $etp_proc->i printf "\n" end - printf " Heap size: %ld\n", $arg0->heap_sz - if ($arg0->old_heap) - printf " Old-heap size: %ld\n", $arg0->old_hend - $arg0->old_heap + printf " Heap size: %ld\n", $etp_proc->heap_sz + if ($etp_proc->old_heap) + printf " Old-heap size: %ld\n", $etp_proc->old_hend - $etp_proc->old_heap end - printf " Mbuf size: %ld\n", $arg0->mbuf_sz + printf " Mbuf size: %ld\n", $etp_proc->mbuf_sz if (etp_smp_compiled) - printf " Msgq len: %ld (inner=%ld, outer=%ld)\n", ($arg0->msg.len + $arg0->msg_inq.len), $arg0->msg.len, $arg0->msg_inq.len + printf " Msgq len: %ld (inner=%ld, outer=%ld)\n", ($etp_proc->msg.len + $etp_proc->msg_inq.len), $etp_proc->msg.len, $etp_proc->msg_inq.len else - printf " Msgq len: %d\n", $arg0->msg.len + printf " Msgq len: %d\n", $etp_proc->msg.len end printf " Parent: " - etp-1 $arg0->parent - printf "\n Pointer: (Process *) %p\n", $arg0 + etp-1 $etp_proc->parent + printf "\n Pointer: (Process *) %p\n", $etp_proc end end @@ -1918,57 +1919,58 @@ end define etp-process-memory-info # Args: Process* # - if ((*(((Uint32 *) &(((Process *) $arg0)->state)))) & 0x400000) + set $etp_pmem_proc = ((Process *) $arg0) + if ((*(((Uint32 *) &($etp_pmem_proc->state)))) & 0x400000) set $proxy_process = 1 else set $proxy_process = 0 end printf " " - etp-1 $arg0->common.id - printf ": (Process *) %p ", $arg0 + etp-1 $etp_pmem_proc->common.id + printf ": (Process *) %p ", $etp_pmem_proc if $proxy_process != 0 - printf "(Process *) %p ", $arg0 + printf "(Process *) %p ", $etp_pmem_proc printf " *** PROXY process struct *** refer to next: \n" - etp-pid2proc-1 $arg0->common.id + etp-pid2proc-1 $etp_pmem_proc->common.id printf " -" etp-process-memory-info $proc else - printf " [Heap: %5ld", $arg0->heap_sz - if ($arg0->old_heap) - printf " | %5ld", $arg0->old_hend - $arg0->old_heap + printf " [Heap: %5ld", $etp_pmem_proc->heap_sz + if ($etp_pmem_proc->old_heap) + printf " | %5ld", $etp_pmem_proc->old_hend - $etp_pmem_proc->old_heap else printf " | none " end - printf "] [Mbuf: %5ld", $arg0->mbuf_sz + printf "] [Mbuf: %5ld", $etp_pmem_proc->mbuf_sz if (etp_smp_compiled) - printf " | %3ld (%3ld | %3ld)", ($arg0->msg.len + $arg0->msg_inq.len), $arg0->msg.len, $arg0->msg_inq.len + printf " | %3ld (%3ld | %3ld)", ($etp_pmem_proc->msg.len + $etp_pmem_proc->msg_inq.len), $etp_pmem_proc->msg.len, $etp_pmem_proc->msg_inq.len else - printf " | %3ld", $arg0->msg.len + printf " | %3ld", $etp_pmem_proc->msg.len end printf "] " - if ($arg0->i) + if ($etp_pmem_proc->i) printf " I: " - etp-cp-1 $arg0->i + etp-cp-1 $etp_pmem_proc->i printf " " end - if ($arg0->current) - etp-1 $arg0->current[0] + if ($etp_pmem_proc->current) + etp-1 $etp_pmem_proc->current[0] printf ":" - etp-1 $arg0->current[1] - printf "/%d ", $arg0->current[2] + etp-1 $etp_pmem_proc->current[1] + printf "/%d ", $etp_pmem_proc->current[2] end - if (*(((Uint32 *) &(((Process *) $arg0)->state))) & 0x4) == 0 - if ($arg0->common.u.alive.reg) - etp-1 $arg0->common.u.alive.reg->name + if (*(((Uint32 *) &(((Process *) $etp_pmem_proc)->state))) & 0x4) == 0 + if ($etp_pmem_proc->common.u.alive.reg) + etp-1 $etp_pmem_proc->common.u.alive.reg->name printf " " end end - if ($arg0->cp) + if ($etp_pmem_proc->cp) printf " CP: " - etp-cp-1 $arg0->cp + etp-cp-1 $etp_pmem_proc->cp printf " " end printf "\n" @@ -2166,23 +2168,24 @@ define etp-port-info # Args: Port* # printf " Port: " - etp-1 $arg0->common.id - printf "\n Name: %s\n", $arg0->name + set $etp_pinfo_port = ((Port*)$arg0) + etp-1 $etp_pinfo_port->common.id + printf "\n Name: %s\n", $etp_pinfo_port->name printf " State:" - etp-port-state $arg0 + etp-port-state $etp_pinfo_port printf " Scheduler flags:" - etp-port-sched-flags $arg0 - if (*(((Uint32 *) &(((Port *) $arg0)->state))) & 0x5C00) == 0 - if ($arg0->common.u.alive.reg) + etp-port-sched-flags $etp_pinfo_port + if (*(((Uint32 *) &($etp_pinfo_port->state))) & 0x5C00) == 0 + if ($etp_pinfo_port->common.u.alive.reg) printf " Registered name: " - etp-1 $arg0->common.u.alive.reg->name + etp-1 $etp_pinfo_port->common.u.alive.reg->name printf "\n" end end printf " Connected: " - set $connected = *(((Eterm *) &(((Port *) $arg0)->connected))) + set $connected = *(((Eterm *) &(((Port *) $etp_pinfo_port)->connected))) etp-1 $connected - printf "\n Pointer: (Port *) %p\n", $arg0 + printf "\n Pointer: (Port *) %p\n", $etp_pinfo_port end document etp-port-info @@ -3694,6 +3697,26 @@ document etp-address-to-beam-opcode %--------------------------------------------------------------------------- end +define etp-compile-debug + shell (cd $ERL_TOP && make emulator FLAVOR=smp TYPE=debug) +end + +document etp-compile-debug +%--------------------------------------------------------------------------- +% Re-compile the debug erlang emulator +%--------------------------------------------------------------------------- +end + +define etp-compile + shell (cd $ERL_TOP && make emulator) +end + +document etp-compile +%--------------------------------------------------------------------------- +% Re-compile the erlang emulator +%--------------------------------------------------------------------------- +end + ############################################################################ # Toolbox parameter handling diff --git a/erts/include/internal/ethr_mutex.h b/erts/include/internal/ethr_mutex.h index a510a2c97f..8ef3b1e40b 100644 --- a/erts/include/internal/ethr_mutex.h +++ b/erts/include/internal/ethr_mutex.h @@ -108,13 +108,13 @@ void LeaveCriticalSection(CRITICAL_SECTION *); # error Need a qlock implementation #endif -#define ETHR_RWMTX_W_FLG__ (((ethr_sint32_t) 1) << 31) -#define ETHR_RWMTX_W_WAIT_FLG__ (((ethr_sint32_t) 1) << 30) -#define ETHR_RWMTX_R_WAIT_FLG__ (((ethr_sint32_t) 1) << 29) +#define ETHR_RWMTX_W_FLG__ ((ethr_sint32_t) (1U << 31)) +#define ETHR_RWMTX_W_WAIT_FLG__ ((ethr_sint32_t) (1U << 30)) +#define ETHR_RWMTX_R_WAIT_FLG__ ((ethr_sint32_t) (1U << 29)) /* frequent read kind */ -#define ETHR_RWMTX_R_FLG__ (((ethr_sint32_t) 1) << 28) -#define ETHR_RWMTX_R_ABRT_UNLCK_FLG__ (((ethr_sint32_t) 1) << 27) +#define ETHR_RWMTX_R_FLG__ ((ethr_sint32_t) (1U << 28)) +#define ETHR_RWMTX_R_ABRT_UNLCK_FLG__ ((ethr_sint32_t) (1U << 27)) #define ETHR_RWMTX_R_PEND_UNLCK_MASK__ (ETHR_RWMTX_R_ABRT_UNLCK_FLG__ - 1) /* normal kind */ diff --git a/erts/preloaded/ebin/prim_inet.beam b/erts/preloaded/ebin/prim_inet.beam Binary files differindex edb86d35a6..fdf8bbf4e2 100644 --- a/erts/preloaded/ebin/prim_inet.beam +++ b/erts/preloaded/ebin/prim_inet.beam diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 94f3078173..edf79b8f75 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1206,16 +1206,18 @@ module_loaded(_Module) -> erlang:nif_error(undefined). -type registered_name() :: atom(). - -type registered_process_identifier() :: registered_name() | {registered_name(), node()}. - -type monitor_process_identifier() :: pid() | registered_process_identifier(). +-type monitor_port_identifier() :: port() | registered_name(). %% monitor/2 --spec monitor(process, monitor_process_identifier()) -> MonitorRef when - MonitorRef :: reference(); - (time_offset, clock_service) -> MonitorRef when - MonitorRef :: reference(). +-spec monitor + (process, monitor_process_identifier()) -> MonitorRef + when MonitorRef :: reference(); + (port, monitor_port_identifier()) -> MonitorRef + when MonitorRef :: reference(); + (time_offset, clock_service) -> MonitorRef + when MonitorRef :: reference(). monitor(_Type, _Item) -> erlang:nif_error(undefined). @@ -2160,7 +2162,7 @@ process_flag(_Flag, _Value) -> {max_heap_size, MaxHeapSize :: max_heap_size()} | {monitored_by, Pids :: [pid()]} | {monitors, - Monitors :: [{process, Pid :: pid() | + Monitors :: [{process | port, Pid :: pid() | port() | {RegName :: atom(), Node :: node()}}]} | {message_queue_data, MQD :: message_queue_data()} | {priority, Level :: priority_level()} | @@ -3087,6 +3089,9 @@ port_info(Port) -> (Port, monitors) -> {monitors, Monitors} | 'undefined' when Port :: port() | atom(), Monitors :: [{process, pid()}]; + (Port, monitored_by) -> {monitored_by, MonitoredBy} | 'undefined' when + Port :: port() | atom(), + MonitoredBy :: [pid()]; (Port, name) -> {name, Name} | 'undefined' when Port :: port() | atom(), Name :: string(); diff --git a/erts/preloaded/src/prim_inet.erl b/erts/preloaded/src/prim_inet.erl index 71dbfbd0a7..560810d222 100644 --- a/erts/preloaded/src/prim_inet.erl +++ b/erts/preloaded/src/prim_inet.erl @@ -1746,8 +1746,8 @@ enc_value_2(addr, {inet6,{IP,Port}}) -> [?INET_AF_INET6,?int16(Port)|ip6_to_bytes(IP)]; enc_value_2(addr, {local,Addr}) -> %% A binary is passed as is, but anything else will be - %% regarded as a filename and therefore UTF-8 encoded - %% if the system filename encoding flag so dictates. + %% regarded as a filename and therefore encoded according to + %% the current system filename encoding mode. Bin = if is_binary(Addr) -> |