diff options
Diffstat (limited to 'erts')
144 files changed, 15358 insertions, 8696 deletions
diff --git a/erts/configure.in b/erts/configure.in index b2435f1ac9..820247b4b8 100644 --- a/erts/configure.in +++ b/erts/configure.in @@ -2721,6 +2721,13 @@ LIBS=$saved_libs dnl restore CPPFLAGS CPPFLAGS=$saved_cppflags +case $ARCH in + x86|amd64) + AC_DEFINE(ERTS_STRUCTURE_ALIGNED_ALLOC, 1, [Define if structure alignment is enough for allocators. If not defined, 64-bit alignment will be forced.]);; + *) + ;; +esac + LM_SYS_IPV6 LM_SYS_MULTICAST ERL_TIME_CORRECTION diff --git a/erts/doc/src/alt_dist.xml b/erts/doc/src/alt_dist.xml index d3731a5391..92d40d8558 100644 --- a/erts/doc/src/alt_dist.xml +++ b/erts/doc/src/alt_dist.xml @@ -782,10 +782,9 @@ <taglist> <tag><c>DFLAG_DIST_HDR_ATOM_CACHE</c></tag> <item>Do not use atom cache over this connection.</item> - <tag><c>DFLAGS_STRICT_ORDER_DELIVERY</c></tag> - <item>Do not use any features that require strict - order delivery.</item> </taglist> + <p>Use function <c>dist_util:strict_order_flags/0</c> to get all flags + for features that require strict order delivery.</p> <p> This flag field is optional. </p> @@ -819,9 +818,9 @@ <p> The data delivery order can be relaxed by disabling features that require strict ordering. This is done by - passing the <c>?DFLAGS_STRICT_ORDER_DELIVERY</c> - <seealso marker="erl_dist_protocol#dflags">distribution - flags</seealso> in the + passing the + <seealso marker="erl_dist_protocol#dflags">distribution flags</seealso> + returned by <c>dist_util:strict_order_flags/0</c> in the <seealso marker="alt_dist#hs_data_reject_flags"><c>reject_flags</c></seealso> field of the <seealso marker="#hs_data_record"><c>#hs_data{}</c></seealso> record used when setting up the connection. When relaxed diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml index 99f0421080..5089d4e0ce 100644 --- a/erts/doc/src/erl.xml +++ b/erts/doc/src/erl.xml @@ -572,7 +572,7 @@ <tag><marker id="async_thread_pool_size"/><c><![CDATA[+A size]]></c></tag> <item> <p>Sets the number of threads in async thread pool. Valid range - is 0-1024. Defaults to 10 if thread support is available.</p> + is 0-1024. Defaults to 1.</p> </item> <tag><c><![CDATA[+B [c | d | i]]]></c></tag> <item> @@ -1155,6 +1155,26 @@ without prior notice.</p> </note> </item> + <tag><marker id="+sbwtdcpu"/> + <c>+sbwtdcpu none|very_short|short|medium|long|very_long</c></tag> + <item> + <p>As <seealso marker="+sbwt"><c>+sbwt</c></seealso> but affects + dirty CPU schedulers. Defaults to <c>short</c>.</p> + <note> + <p>This flag can be removed or changed at any time + without prior notice.</p> + </note> + </item> + <tag><marker id="+sbwtdio"/> + <c>+sbwtdio none|very_short|short|medium|long|very_long</c></tag> + <item> + <p>As <seealso marker="+sbwt"><c>+sbwt</c></seealso> but affects + dirty IO schedulers. Defaults to <c>short</c>.</p> + <note> + <p>This flag can be removed or changed at any time + without prior notice.</p> + </note> + </item> <tag><marker id="+scl"/><c>+scl true|false</c></tag> <item> <p>Enables or disables scheduler compaction of load. By default @@ -1420,6 +1440,26 @@ notice.</p> </note> </item> + <tag><marker id="+swtdcpu"/> + <c>+swtdcpu very_low|low|medium|high|very_high</c></tag> + <item> + <p>As <seealso marker="+swt"><c>+swt</c></seealso> but + affects dirty CPU schedulers. Defaults to <c>medium</c>.</p> + <note> + <p>This flag can be removed or changed at any time + without prior notice.</p> + </note> + </item> + <tag><marker id="+swtdio"/> + <c>+swtdio very_low|low|medium|high|very_high</c></tag> + <item> + <p>As <seealso marker="+swt"><c>+swt</c></seealso> but affects + dirty IO schedulers. Defaults to <c>medium</c>.</p> + <note> + <p>This flag can be removed or changed at any time + without prior notice.</p> + </note> + </item> </taglist> </item> <tag><marker id="+t"/><c><![CDATA[+t size]]></c></tag> diff --git a/erts/doc/src/erl_dist_protocol.xml b/erts/doc/src/erl_dist_protocol.xml index a78b13aaa4..98a9a76b60 100644 --- a/erts/doc/src/erl_dist_protocol.xml +++ b/erts/doc/src/erl_dist_protocol.xml @@ -849,10 +849,9 @@ DiB == gen_digest(ChA, ICA)? </item> </taglist> <p> - There are also a collection of <c>DFLAG</c>s bitwise or:ed - together in the <c>DFLAGS_STRICT_ORDER_DELIVERY</c> macro. - These flags corresponds to features that require strict - ordering of data over distribution channels. + There is also function <c>dist_util:strict_order_flags/0</c> + returning all flags (bitwise or:ed together) corresponding to features + that require strict ordering of data over distribution channels. </p> </section> </section> diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml index f2f380ca4e..cabc07d020 100644 --- a/erts/doc/src/erl_nif.xml +++ b/erts/doc/src/erl_nif.xml @@ -540,7 +540,7 @@ int writeiovec(ErlNifEnv *env, ERL_NIF_TERM term, ERL_NIF_TERM *tail, have to wait for a very long time. Blocking multi-scheduling, that is, calling <seealso marker="erlang#system_flag_multi_scheduling"> <c>erlang:system_flag(multi_scheduling, block)</c></seealso>, can - also take a very long time to complete. This becaue all ongoing + also take a very long time to complete. This is because all ongoing dirty operations on all dirty schedulers must complete before the block operation can complete.</p> <p>Many operations communicating with a process executing a @@ -590,7 +590,7 @@ int writeiovec(ErlNifEnv *env, ERL_NIF_TERM term, ERL_NIF_TERM *tail, <c>unload</c> is called to release the library. All are described individually below.</p> <p>The fourth argument <c>NULL</c> is ignored. It - was earlier used for the deprectated <c>reload</c> callback + was earlier used for the deprecated <c>reload</c> callback which is no longer supported since OTP 20.</p> <p>If compiling a NIF for static inclusion through <c>--enable-static-nifs</c>, you must define <c>STATIC_ERLANG_NIF</c> @@ -809,7 +809,7 @@ typedef void ErlNifResourceDtor(ErlNifEnv* env, void* obj);</code> <tag><marker id="ErlNifResourceDown"/><c>ErlNifResourceDown</c></tag> <item> <code type="none"> -typedef void ErlNifResourceDown(ErlNifEnv* env, void* obj, const ErlNifPid* pid, const ErlNifMonitor* mon);</code> +typedef void ErlNifResourceDown(ErlNifEnv* env, void* obj, ErlNifPid* pid, ErlNifMonitor* mon);</code> <p>The function prototype of a resource down function, called on the behalf of <seealso marker="#enif_monitor_process"> enif_monitor_process</seealso>. <c>obj</c> is the resource, <c>pid</c> @@ -875,7 +875,7 @@ typedef enum { <item> <p>An enumeration of the properties that can be requested from <seealso marker="#enif_make_unique_integer"> - <c>enif_unique_integer</c></seealso>. + <c>enif_make_unique_integer</c></seealso>. For default properties, use value <c>0</c>.</p> <taglist> <tag><c>ERL_NIF_UNIQUE_POSITIVE</c></tag> @@ -1104,6 +1104,16 @@ typedef struct { </func> <func> + <name><ret>char*</ret> + <nametext>enif_cond_name(ErlNifCond* cnd)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_cond_name"> + <c>erl_drv_cond_name</c></seealso>.</p> + </desc> + </func> + + <func> <name><ret>void</ret> <nametext>enif_cond_signal(ErlNifCond *cnd)</nametext></name> <fsummary></fsummary> @@ -1236,6 +1246,18 @@ typedef struct { </func> <func> + <name><ret>int</ret><nametext>enif_fprintf(FILE *stream, const char *format, ...)</nametext></name> + <fsummary>Format strings and Erlang terms.</fsummary> + <desc> + <p>Similar to <c>fprintf</c> but this format string also accepts + <c>"%T"</c>, which formats Erlang terms.</p> + <p>This function was originally intenden for debugging purpose. It is not + recommended to print very large terms with <c>%T</c>. The function may + change <c>errno</c>, even if successful.</p> + </desc> + </func> + + <func> <name><ret>void</ret><nametext>enif_free(void* ptr)</nametext></name> <fsummary>Free dynamic memory.</fsummary> <desc> @@ -2163,6 +2185,20 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); </func> <func> + <name><ret>int</ret> + <nametext>enif_make_map_from_arrays(ErlNifEnv* env, ERL_NIF_TERM keys[], + ERL_NIF_TERM values[], size_t cnt, ERL_NIF_TERM *map_out)</nametext></name> + <fsummary>Make map term from the given keys and values.</fsummary> + <desc> + <p>Makes a map term from the given keys and values.</p> + <p>If successful, this function sets <c>*map_out</c> to the new map and + returns <c>true</c>. Returns <c>false</c> there are any duplicate + keys.</p> + <p>All keys and values must belong to <c>env</c>.</p> + </desc> + </func> + + <func> <name><ret>unsigned char *</ret><nametext>enif_make_new_binary(ErlNifEnv* env, size_t size, ERL_NIF_TERM* termp)</nametext></name> <fsummary>Allocate and create a new binary term.</fsummary> @@ -2236,7 +2272,7 @@ enif_inspect_iovec(env, max_elements, term, &tail, &iovec); <seealso marker="#enif_get_resource"><c>enif_get_resource</c></seealso>.</p> </item> <item> - <p>A resoure term can be serialized with <c>term_to_binary</c> and later + <p>A resource term can be serialized with <c>term_to_binary</c> and later be fully recreated if the resource object is still alive when <c>binary_to_term</c> is called. A <em>stale</em> resource term will be returned from <c>binary_to_term</c> if the resource object has @@ -2616,6 +2652,16 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> + <name><ret>char*</ret> + <nametext>enif_mutex_name(ErlNifMutex* mtx)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_mutex_name"> + <c>erl_drv_mutex_name</c></seealso>.</p> + </desc> + </func> + + <func> <name><ret>int</ret> <nametext>enif_mutex_trylock(ErlNifMutex *mtx)</nametext></name> <fsummary></fsummary> @@ -2860,6 +2906,16 @@ enif_map_iterator_destroy(env, &iter);</code> </func> <func> + <name><ret>char*</ret> + <nametext>enif_rwlock_name(ErlNifRWLock* rwlck)</nametext></name> + <fsummary></fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_rwlock_name"> + <c>erl_drv_rwlock_name</c></seealso>.</p> + </desc> + </func> + + <func> <name><ret>void</ret> <nametext>enif_rwlock_rlock(ErlNifRWLock *rwlck)</nametext></name> <fsummary></fsummary> @@ -2980,7 +3036,7 @@ enif_map_iterator_destroy(env, &iter);</code> <p>Argument <c>mode</c> describes the type of events to wait for. It can be <c>ERL_NIF_SELECT_READ</c>, <c>ERL_NIF_SELECT_WRITE</c> or a bitwise OR combination to wait for both. It can also be <c>ERL_NIF_SELECT_STOP</c> - which is described further below. When a read or write event is triggerred, + which is described further below. When a read or write event is triggered, a notification message like this is sent to the process identified by <c>pid</c>:</p> <code type="none">{select, Obj, Ref, ready_input | ready_output}</code> @@ -3031,7 +3087,7 @@ enif_map_iterator_destroy(env, &iter);</code> <item>The stop callback was scheduled to run on some other thread or later by this thread.</item> </taglist> - <p>Returns a negative value if the call failed where the follwing bits can be set:</p> + <p>Returns a negative value if the call failed where the following bits can be set:</p> <taglist> <tag><c>ERL_NIF_SELECT_INVALID_EVENT</c></tag> <item>Argument <c>event</c> is not a valid OS event object.</item> @@ -3039,9 +3095,9 @@ enif_map_iterator_destroy(env, &iter);</code> <item>The system call failed to add the event object to the poll set.</item> </taglist> <note> - <p>Use bitwise AND to test for specific bits in the return vaue. + <p>Use bitwise AND to test for specific bits in the return value. New significant bits may be added in future releases to give more detailed - information for both failed and successful calls. Do NOT use equallity tests + information for both failed and successful calls. Do NOT use equality tests like <c>==</c>, as that may cause your application to stop working.</p> <p>Example:</p> <code type="none"> @@ -3106,7 +3162,7 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { <c>enif_free_env</c></seealso> of cleared for reuse with <seealso marker="#enif_clear_env"><c>enif_clear_env</c></seealso>.</p> <p>If <c>msg_env</c> is set to <c>NULL</c>, the <c>msg</c> term is - copied and the original term and its environemt is still valid after + copied and the original term and its environment is still valid after the call.</p> <p>This function is only thread-safe when the emulator with SMP support is used. It can only be used in a non-SMP emulator from a NIF-calling @@ -3136,6 +3192,9 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { <desc> <p>Similar to <c>snprintf</c> but this format string also accepts <c>"%T"</c>, which formats Erlang terms.</p> + <p>This function was originally intenden for debugging purpose. It is not + recommended to print very large terms with <c>%T</c>. The function may + change <c>errno</c>, even if successful.</p> </desc> </func> @@ -3199,6 +3258,16 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { </func> <func> + <name><ret>char*</ret> + <nametext>enif_thread_name(ErlNifTid tid)</nametext></name> + <fsummary>Thread name</fsummary> + <desc> + <p>Same as <seealso marker="erl_driver#erl_drv_thread_name"> + <c>erl_drv_thread_name</c></seealso>.</p> + </desc> + </func> + + <func> <name><ret>ErlNifThreadOpts *</ret> <nametext>enif_thread_opts_create(char *name)</nametext></name> <fsummary></fsummary> @@ -3316,6 +3385,30 @@ if (retval & ERL_NIF_SELECT_STOP_CALLED) { <func> <name><ret>int</ret> + <nametext>enif_vfprintf(FILE *stream, const char *format, va_list ap) + </nametext></name> + <fsummary>Format strings and Erlang terms.</fsummary> + <desc> + <p>Equivalent to <seealso marker="#enif_fprintf"><c>enif_fprintf</c></seealso> + except that its called with a <c>va_list</c> instead of a variable number of + arguments.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> + <nametext>enif_vsnprintf(char *str, size_t size, const char *format, va_list ap) + </nametext></name> + <fsummary>Format strings and Erlang terms.</fsummary> + <desc> + <p>Equivalent to <seealso marker="#enif_snprintf"><c>enif_snprintf</c></seealso> + except that its called with a <c>va_list</c> instead of a variable number of + arguments.</p> + </desc> + </func> + + <func> + <name><ret>int</ret> <nametext>enif_whereis_pid(ErlNifEnv *env, ERL_NIF_TERM name, ErlNifPid *pid)</nametext></name> <fsummary>Looks up a process by its registered name.</fsummary> diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml index 56eaa47af4..c086928bb3 100644 --- a/erts/doc/src/erlang.xml +++ b/erts/doc/src/erlang.xml @@ -5040,7 +5040,6 @@ RealSystem = system + MissedSystem</code> <item><c>initial_call</c></item> <item><c>status</c></item> <item><c>message_queue_len</c></item> - <item><c>messages</c></item> <item><c>links</c></item> <item><c>dictionary</c></item> <item><c>trap_exit</c></item> @@ -5700,11 +5699,17 @@ true</pre> <type name="dst"/> <desc> <p>Sends a message and returns <c><anno>Msg</anno></c>. This - is the same as <c><anno>Dest</anno> ! <anno>Msg</anno></c>.</p> + is the same as using the <seealso marker="doc/reference_manual:expressions#send"> + send operator</seealso>: + <c><anno>Dest</anno> ! <anno>Msg</anno></c>.</p> <p><c><anno>Dest</anno></c> can be a remote or local process identifier, a (local) port, a locally registered name, or a tuple <c>{<anno>RegName</anno>, <anno>Node</anno>}</c> - for a registered name at another node.</p> + for a registered name at another node.</p> + <p>The function fails with a <c>badarg</c> run-time error if + <c><anno>Dest</anno></c> is an atom name, but this name is not + registered. This is the only case when <c>send</c> fails for an + unreachable destination <c><anno>Dest</anno></c> (of correct type).</p> </desc> </func> diff --git a/erts/doc/src/erts_alloc.xml b/erts/doc/src/erts_alloc.xml index 049ffe560a..53e136d76c 100644 --- a/erts/doc/src/erts_alloc.xml +++ b/erts/doc/src/erts_alloc.xml @@ -68,8 +68,7 @@ fixed size data types.</item> <tag><c>exec_alloc</c></tag> <item>Allocator used by the <seealso marker="hipe:HiPE_app"><c>HiPE</c></seealso> - application for native executable code on specific architectures - (x86_64).</item> + application for native executable code.</item> <tag><c>std_alloc</c></tag> <item>Allocator used for most memory blocks not allocated through any of the other allocators described above.</item> @@ -723,19 +722,6 @@ </section> <section> - <title>Special Flag for exec_alloc</title> - <taglist> - <tag><marker id="MXscs"/><c><![CDATA[+MXscs <size in MB>]]></c></tag> - <item> - <p><c>exec_alloc</c> super carrier size (in MB). The amount of - <em>virtual</em> address space reserved for native executable code - used by the <seealso marker="hipe:HiPE_app"><c>HiPE</c></seealso> application - on specific architectures (x86_64). Defaults to <c>512</c>.</p> - </item> - </taglist> - </section> - - <section> <title>Instrumentation Flags</title> <taglist> <tag><marker id="Mim"/><c>+Mim true|false</c></tag> diff --git a/erts/doc/src/match_spec.xml b/erts/doc/src/match_spec.xml index 2a14f1e47b..644b989800 100644 --- a/erts/doc/src/match_spec.xml +++ b/erts/doc/src/match_spec.xml @@ -405,7 +405,8 @@ <c><![CDATA[tracer]]></c>.</p> <p>If a tracer is specified in both lists, the tracer in the enable list takes precedence. If no tracer is specified, the same - tracer as the process executing the match specification is used.</p> + tracer as the process executing the match specification is used (not the meta tracer). + If that process doesn't have tracer either, then trace flags are ignored.</p> <p>When using a <seealso marker="erl_tracer">tracer module</seealso>, the module must be loaded before the match specification is executed. If it is not loaded, the match fails.</p> diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml index 7ef42c2318..fcc7ec5b66 100644 --- a/erts/doc/src/notes.xml +++ b/erts/doc/src/notes.xml @@ -31,6 +31,133 @@ </header> <p>This document describes the changes made to the ERTS application.</p> +<section><title>Erts 9.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed a <c>configure</c> test for <c>libz</c> internals + that unintentionally caused various native code in OTP to + link against <c>libz</c>. Under certain circumstances + this caused the build of OTP to fail.</p> + <p> + Own Id: OTP-14840 Aux Id: ERL-529 </p> + </item> + <item> + <p> + File names containing unicode codepoints larger than 255 + were not correctly encoded in stack traces.</p> + <p> + Own Id: OTP-14847 Aux Id: ERL-544 </p> + </item> + <item> + <p> + Fix HiPE bug for binary constructs like + <c><<X/utf8>></c> which could in rare cases + cause faulty results or VM crash.</p> + <p> + This fix affects both the <c>hipe</c> compiler and + <c>erts</c> runtime in an <em>incompatible</em> way. Old + hipe compiled files need to be recompiled to load and run + properly as native.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-14850 Aux Id: PR-1664 </p> + </item> + <item> + <p> + Fix <c>term_to_binary/2</c> spec for + <c>minor_version</c>.</p> + <p> + Own Id: OTP-14876 Aux Id: ERL-548 </p> + </item> + <item> + <p> + Fix bug in erlang:binary_to_integer/2 where invalid + characters were not detected for bases larger then 10. + e.g. <c>binary_to_integer(<<":">>, 16)</c> + would return 3 and not badarg as it should.</p> + <p> + Own Id: OTP-14879</p> + </item> + <item> + <p>Fixed bug in <c>float_to_list/2</c> and + <c>float_to_binary/2</c> with option <c>decimals</c> that + caused a faulty rounding up of the last decimal digit for + about 6% of floats with a fraction part.</p> + <p>For example, <c>float_to_list(0.145, + [{decimals,1}])</c> returned <c>"0.2"</c> instead of + <c>"0.1"</c>.</p> + <p> + Own Id: OTP-14890</p> + </item> + <item> + <p> + Fix bug causing slow hipe execution in modules loaded + early during boot or loaded by <c>code:atomic_load</c> or + <c>code:finish_loading</c>.</p> + <p> + Own Id: OTP-14891</p> + </item> + <item> + <p>Fixed a buffer overflow in an internal string + formatting function that could be hit if specifying a + long floating-point format specifier to + <c>erts_sprintf</c> or similar.</p> + <p> + Own Id: OTP-14920</p> + </item> + <item> + <p><c>erlang:iolist_to_iovec/1</c> and + <c>enif_inspect_iovec</c> will no longer fail when + provided with binaries that have been matched-out on a + non-byte boundary.</p> + <p> + Own Id: OTP-14921</p> + </item> + <item> + <p><c>iolist_to_binary/1</c> and + <c>erlang:iolist_to_iovec/1</c> will now badarg if + supplied with a bitstring (without a list).</p> + <p> + Own Id: OTP-14926</p> + </item> + <item> + <p> + Reject loading modules with names containing directory + separators ('/' or '\' on Windows).</p> + <p> + Own Id: OTP-14933 Aux Id: ERL-564, PR-1716 </p> + </item> + <item> + <p> + Fix potential dead-lock when the tracer process dies + while a the traced process is running on a dirty + scheduler.</p> + <p> + Own Id: OTP-14938</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + More crash dump info such as: process binary virtual heap + stats, full info for process causing out-of-mem during + GC, more port related info, and dirty scheduler info.</p> + <p> + Own Id: OTP-14820</p> + </item> + </list> + </section> + +</section> + <section><title>Erts 9.2.1</title> <section><title>Improvements and New Features</title> diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in index 9c79bf3da0..92cf40c1ae 100644 --- a/erts/emulator/Makefile.in +++ b/erts/emulator/Makefile.in @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2017. All Rights Reserved. +# Copyright Ericsson AB 1996-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -645,7 +645,7 @@ PRELOAD_BEAM = $(ERL_TOP)/erts/preloaded/ebin/otp_ring0.beam \ $(ERL_TOP)/erts/preloaded/ebin/erts_internal.beam \ $(ERL_TOP)/erts/preloaded/ebin/erl_tracer.beam \ $(ERL_TOP)/erts/preloaded/ebin/erts_literal_area_collector.beam \ - $(ERL_TOP)/erts/preloaded/ebin/erts_dirty_process_code_checker.beam + $(ERL_TOP)/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam ifeq ($(TARGET),win32) # On windows the preloaded objects are in a resource object. @@ -844,7 +844,7 @@ RUN_OBJS += \ $(OBJDIR)/utils.o $(OBJDIR)/bif.o \ $(OBJDIR)/io.o $(OBJDIR)/erl_printf_term.o\ $(OBJDIR)/erl_debug.o $(OBJDIR)/erl_md5.o \ - $(OBJDIR)/erl_message.o \ + $(OBJDIR)/erl_message.o $(OBJDIR)/erl_proc_sig_queue.o \ $(OBJDIR)/erl_process_dict.o $(OBJDIR)/erl_process_lock.o \ $(OBJDIR)/erl_port_task.o $(OBJDIR)/erl_arith.o \ $(OBJDIR)/time.o $(OBJDIR)/erl_time_sup.o \ @@ -858,11 +858,11 @@ RUN_OBJS += \ $(OBJDIR)/register.o $(OBJDIR)/break.o \ $(OBJDIR)/erl_async.o $(OBJDIR)/erl_lock_check.o \ $(OBJDIR)/erl_gc.o $(OBJDIR)/erl_lock_count.o \ - $(OBJDIR)/erl_posix_str.o \ + $(OBJDIR)/erl_posix_str.o \ $(OBJDIR)/erl_bits.o $(OBJDIR)/erl_math.o \ $(OBJDIR)/erl_fun.o $(OBJDIR)/erl_bif_port.o \ $(OBJDIR)/erl_term.o $(OBJDIR)/erl_node_tables.o \ - $(OBJDIR)/erl_monitors.o $(OBJDIR)/erl_process_dump.o \ + $(OBJDIR)/erl_monitor_link.o $(OBJDIR)/erl_process_dump.o \ $(OBJDIR)/erl_hl_timer.o $(OBJDIR)/erl_cpu_topology.o \ $(OBJDIR)/erl_drv_thread.o $(OBJDIR)/erl_bif_chksum.o \ $(OBJDIR)/erl_bif_re.o $(OBJDIR)/erl_unicode.o \ diff --git a/erts/emulator/beam/atom.c b/erts/emulator/beam/atom.c index bbe1cb3e11..e5b7616a0d 100644 --- a/erts/emulator/beam/atom.c +++ b/erts/emulator/beam/atom.c @@ -452,7 +452,7 @@ init_atom_table(void) /* Ordinary atoms */ for (i = 0; erl_atom_names[i] != 0; i++) { int ix; - a.len = strlen(erl_atom_names[i]); + a.len = sys_strlen(erl_atom_names[i]); a.latin1_chars = a.len; a.name = (byte*)erl_atom_names[i]; a.slot.index = i; diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names index 42a368cdd8..cceca66850 100644 --- a/erts/emulator/beam/atom.names +++ b/erts/emulator/beam/atom.names @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2017. All Rights Reserved. +# Copyright Ericsson AB 1996-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -48,7 +48,7 @@ atom Empty='' # # Used in the Beam emulator loop. (Smaller literals usually means tighter code.) # -atom fun infinity timeout normal call return +atom infinity timeout normal call return atom throw error exit atom undefined @@ -69,11 +69,9 @@ atom DOWN='DOWN' atom UP='UP' atom EXIT='EXIT' atom abort -atom aborted atom abs_path atom absoluteURI atom ac -atom accessor atom active atom active_tasks atom active_tasks_all @@ -88,7 +86,6 @@ atom allocated_areas atom allocator atom allocator_sizes atom alloc_util_allocators -atom allow_gc atom allow_passive_connect atom already_loaded atom amd64 @@ -109,6 +106,7 @@ atom atom atom atom_used atom attributes atom auto_connect +atom await_exit atom await_microstate_accounting_modifications atom await_port_send_result atom await_proc_exit @@ -122,7 +120,6 @@ atom bag atom band atom big atom bif_return_trap -atom bif_timer_server atom binary atom binary_copy_trap atom binary_find_trap @@ -178,7 +175,6 @@ atom convert_time_unit atom connect atom connected atom connection_closed -atom cons atom const atom context_switches atom control @@ -198,7 +194,6 @@ atom debug_flags atom decimals atom default atom delay_trap -atom depth atom dictionary atom dirty_bif_exception atom dirty_bif_result @@ -220,7 +215,6 @@ atom dist_data atom Div='/' atom div atom dmonitor_node -atom dmonitor_p atom DollarDollar='$$' atom DollarUnderscore='$_' atom dollar_endonly @@ -245,15 +239,14 @@ atom Eqeq='==' atom erl_tracer atom erlang atom erl_signal_server -atom ERROR='ERROR' atom error_handler atom error_logger atom erts_code_purger atom erts_debug +atom erts_dflags atom erts_internal atom ets atom ETS_TRANSFER='ETS-TRANSFER' -atom event atom exact_reductions atom exception_from atom exception_trace @@ -280,27 +273,20 @@ atom force atom format_cpu_topology atom free atom fullsweep_after -atom fullsweep_if_old_binaries -atom fun -atom function atom functions atom function_clause atom garbage_collecting atom garbage_collection atom garbage_collection_info -atom gc_end atom gc_major_end atom gc_major_start atom gc_max_heap_size atom gc_minor_end atom gc_minor_start -atom gc_start atom Ge='>=' atom generational -atom get_data atom get_seq_token atom get_tcw -atom getenv atom gather_gc_info_result atom gather_io_bytes atom gather_microstate_accounting_result @@ -342,7 +328,6 @@ atom initial_call atom input atom internal atom internal_error -atom internal_status atom instruction_counts atom invalid atom is_constant @@ -388,14 +373,12 @@ atom match_spec_result atom max atom maximum atom max_heap_size -atom max_tables max_processes atom mbuf_size atom md5 atom memory atom memory_internal atom memory_types atom message -atom message_binary atom message_queue_data atom message_queue_len atom messages @@ -442,21 +425,18 @@ atom new_processes atom new_ports atom new_uniq atom newline -atom next atom no atom nomatch atom none atom no_auto_capture atom noconnect atom noconnection -atom nocookie atom node atom node_type atom nodedown atom nodedown_reason atom nodeup atom noeol -atom nofile atom noproc atom normal atom nosuspend @@ -478,7 +458,6 @@ atom notempty_atstart atom notify atom notsup atom nouse_stdio -atom objects atom off_heap atom offset atom ok @@ -548,7 +527,6 @@ atom re_run_trap atom read_concurrency atom ready_input atom ready_output -atom ready_async atom reason atom receive atom recent_size @@ -595,7 +573,6 @@ atom sequential_trace_token atom serial atom set atom set_cpu_topology -atom set_data atom set_on_first_link atom set_on_first_spawn atom set_on_link @@ -603,8 +580,6 @@ atom set_on_spawn atom set_seq_token atom set_tcw atom set_tcw_fake -atom separate -atom shared atom sighup atom sigterm atom sigusr1 @@ -620,7 +595,6 @@ atom sigtstp atom sigquit atom silent atom size -atom sl_alloc atom spawn_executable atom spawn_driver atom spawned @@ -628,7 +602,6 @@ atom ssl_tls atom stack_size atom start atom status -atom static atom stderr_to_stdout atom stop atom stream @@ -638,14 +611,11 @@ atom sunrm atom suspend atom suspended atom suspending -atom sys_misc atom system -atom system_error atom system_flag_scheduler_wall_time atom system_limit atom system_version atom system_architecture -atom SYSTEM='SYSTEM' atom table atom term_to_binary_trap atom this @@ -663,7 +633,7 @@ atom total_heap_size atom total_run_queue_lengths atom total_run_queue_lengths_all atom tpkt -atom trace trace_ts traced +atom trace traced atom trace_control_word atom trace_status atom tracer @@ -672,7 +642,6 @@ atom trim atom trim_all atom try_clause atom true -atom tuple atom type atom ucompile atom ucp @@ -689,14 +658,11 @@ atom unblock_normal atom uniq atom unless_suspending atom unloaded -atom unloading atom unloaded_only atom unload_cancelled atom value -atom values atom version atom visible -atom wait atom waiting atom wall_clock atom warning @@ -707,3 +673,4 @@ atom xor atom x86 atom yes atom yield +atom nifs diff --git a/erts/emulator/beam/beam_bif_load.c b/erts/emulator/beam/beam_bif_load.c index 87367b44ab..5c76aafae7 100644 --- a/erts/emulator/beam/beam_bif_load.c +++ b/erts/emulator/beam/beam_bif_load.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2017. All Rights Reserved. + * Copyright Ericsson AB 1999-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,6 +37,7 @@ #include "erl_bits.h" #include "erl_thr_progress.h" #include "erl_nfunc_sched.h" +#include "erl_proc_sig_queue.h" #ifdef HIPE # include "hipe_bif0.h" # define IF_HIPE(X) (X) @@ -65,7 +66,6 @@ static struct { Process *erts_code_purger = NULL; -Process *erts_dirty_process_code_checker; erts_atomic_t erts_copy_literal_area__; #define ERTS_SET_COPY_LITERAL_AREA(LA) \ erts_atomic_set_nob(&erts_copy_literal_area__, \ @@ -607,7 +607,9 @@ BIF_RETTYPE erts_internal_check_dirty_process_code_2(BIF_ALIST_2) int reds = 0; Eterm res; - if (BIF_P != erts_dirty_process_code_checker) + if (BIF_P != erts_dirty_process_signal_handler + && BIF_P != erts_dirty_process_signal_handler_high + && BIF_P != erts_dirty_process_signal_handler_max) BIF_ERROR(BIF_P, EXC_NOTSUP); if (is_not_internal_pid(BIF_ARG_1)) @@ -901,15 +903,59 @@ static void hfrag_literal_copy(Eterm **hpp, ErlOffHeap *ohp, Eterm *start, Eterm *end, char *lit_start, Uint lit_size); +static ERTS_INLINE void +msg_copy_literal_area(ErtsMessage *msgp, int *redsp, + char *literals, Uint lit_bsize) +{ + ErlHeapFragment *hfrag, *hf; + Uint lit_sz = 0; + + *redsp += 1; + + if (!ERTS_SIG_IS_INTERNAL_MSG(msgp) || !msgp->data.attached) + return; + + if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) + hfrag = &msgp->hfrag; + else + hfrag = msgp->data.heap_frag; + + for (hf = hfrag; hf; hf = hf->next) { + lit_sz += hfrag_literal_size(&hf->mem[0], + &hf->mem[hf->used_size], + literals, lit_bsize); + *redsp += 1; + } + + *redsp += lit_sz / 16; /* Better value needed... */ + if (lit_sz > 0) { + ErlHeapFragment *bp = new_message_buffer(lit_sz); + Eterm *hp = bp->mem; + + for (hf = hfrag; hf; hf = hf->next) { + hfrag_literal_copy(&hp, &bp->off_heap, + &hf->mem[0], + &hf->mem[hf->used_size], + literals, lit_bsize); + hfrag = hf; + } + + /* link new hfrag last */ + ASSERT(hfrag->next == NULL); + hfrag->next = bp; + bp->next = NULL; + } +} + Eterm erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed) { ErtsLiteralArea *la; - ErtsMessage *msgp; struct erl_off_heap_header* oh; char *literals; Uint lit_bsize; ErlHeapFragment *hfrag; + ErtsMessage *mfp; la = ERTS_COPY_LITERAL_AREA(); if (!la) @@ -927,46 +973,13 @@ erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed */ erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ); - ERTS_MSGQ_MV_INQ2PRIVQ(c_p); + erts_proc_sig_fetch(c_p); erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ); - for (msgp = c_p->msg.first; msgp; msgp = msgp->next) { - ErlHeapFragment *hf; - Uint lit_sz = 0; - - *redsp += 1; - - if (msgp->data.attached == ERTS_MSG_COMBINED_HFRAG) - hfrag = &msgp->hfrag; - else if (is_value(ERL_MESSAGE_TERM(msgp)) && msgp->data.heap_frag) - hfrag = msgp->data.heap_frag; - else - continue; /* Content on heap or in external term format... */ - - for (hf = hfrag; hf; hf = hf->next) { - lit_sz += hfrag_literal_size(&hf->mem[0], &hf->mem[hf->used_size], - literals, lit_bsize); - *redsp += 1; - } - - *redsp += lit_sz / 16; /* Better value needed... */ - if (lit_sz > 0) { - ErlHeapFragment *bp = new_message_buffer(lit_sz); - Eterm *hp = bp->mem; - - for (hf = hfrag; hf; hf = hf->next) { - hfrag_literal_copy(&hp, &bp->off_heap, - &hf->mem[0], &hf->mem[hf->used_size], - literals, lit_bsize); - hfrag = hf; - } - - /* link new hfrag last */ - ASSERT(hfrag->next == NULL); - hfrag->next = bp; - bp->next = NULL; - } - } + ERTS_FOREACH_SIG_PRIVQS(c_p, msgp, msg_copy_literal_area(msgp, + redsp, + literals, + lit_bsize)); if (gc_allowed) { /* @@ -1041,8 +1054,8 @@ erts_proc_copy_literal_area(Process *c_p, int *redsp, int fcalls, int gc_allowed * process off heap structure. * - Check for literals */ - for (msgp = c_p->msg_frag; msgp; msgp = msgp->next) { - hfrag = erts_message_to_heap_frag(msgp); + for (mfp = c_p->msg_frag; mfp; mfp = mfp->next) { + hfrag = erts_message_to_heap_frag(mfp); for (; hfrag; hfrag = hfrag->next) { Eterm *hp, *hp_end; diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c index 8f02d509a9..5eb68b817e 100644 --- a/erts/emulator/beam/beam_debug.c +++ b/erts/emulator/beam/beam_debug.c @@ -228,11 +228,11 @@ erts_debug_instructions_0(BIF_ALIST_0) Eterm res = NIL; for (i = 0; i < num_instructions; i++) { - needed += 2*strlen(opc[i].name); + needed += 2*sys_strlen(opc[i].name); } hp = HAlloc(BIF_P, needed); for (i = num_instructions-1; i >= 0; i--) { - Eterm s = erts_bld_string_n(&hp, 0, opc[i].name, strlen(opc[i].name)); + Eterm s = erts_bld_string_n(&hp, 0, opc[i].name, sys_strlen(opc[i].name)); res = erts_bld_cons(&hp, 0, s, res); } return res; @@ -431,7 +431,7 @@ print_op(fmtfn_t to, void *to_arg, int op, int size, BeamInstr* addr) * the packing program backwards and in reverse. */ - prog = start_prog + strlen(start_prog); + prog = start_prog + sys_strlen(start_prog); while (start_prog < prog) { prog--; switch (*prog) { diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c index ef9abcde08..fb87be3f17 100644 --- a/erts/emulator/beam/beam_emu.c +++ b/erts/emulator/beam/beam_emu.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2017. All Rights Reserved. + * Copyright Ericsson AB 1996-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,6 +44,7 @@ #include "hipe_bif1.h" #endif #include "dtrace-wrapper.h" +#include "erl_proc_sig_queue.h" /* #define HARDDEBUG 1 */ @@ -749,7 +750,7 @@ void process_main(Eterm * x_reg_array, FloatDef* f_reg_array) dtrace_proc_str(c_p, process_buf); if (ERTS_PROC_IS_EXITING(c_p)) { - strcpy(fun_buf, "<exiting>"); + sys_strcpy(fun_buf, "<exiting>"); } else { ErtsCodeMFA *cmfa = find_function_from_pc(c_p->i); if (cmfa) { @@ -1229,7 +1230,7 @@ void erts_dirty_process_main(ErtsSchedulerData *esdp) dtrace_proc_str(c_p, process_buf); if (ERTS_PROC_IS_EXITING(c_p)) { - strcpy(fun_buf, "<exiting>"); + sys_strcpy(fun_buf, "<exiting>"); } else { ErtsCodeMFA *cmfa = find_function_from_pc(c_p->i); if (cmfa) { @@ -1454,7 +1455,7 @@ handle_error(Process* c_p, BeamInstr* pc, Eterm* reg, ErtsCodeMFA *bif_mfa) reg[3] = c_p->ftrace; if ((new_pc = next_catch(c_p, reg))) { c_p->cp = 0; /* To avoid keeping stale references. */ - c_p->msg.saved_last = 0; /* No longer safe to use this position */ + ERTS_RECV_MARK_CLEAR(c_p); /* No longer safe to use this position */ return new_pc; } if (c_p->catches > 0) erts_exit(ERTS_ERROR_EXIT, "Catch not found"); @@ -2419,8 +2420,8 @@ erts_hibernate(Process* c_p, Eterm* reg) * shrink the heap. */ erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS); - ERTS_MSGQ_MV_INQ2PRIVQ(c_p); - if (!c_p->msg.len) { + erts_proc_sig_fetch(c_p); + if (!c_p->sig_qs.len) { erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS); c_p->fvalue = NIL; PROCESS_MAIN_CHK_LOCKS(c_p); @@ -2428,8 +2429,8 @@ erts_hibernate(Process* c_p, Eterm* reg) ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p); PROCESS_MAIN_CHK_LOCKS(c_p); erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS); - ERTS_MSGQ_MV_INQ2PRIVQ(c_p); - if (!c_p->msg.len) + erts_proc_sig_fetch(c_p); + if (!c_p->sig_qs.len) erts_atomic32_read_band_relb(&c_p->state, ~ERTS_PSFLG_ACTIVE); ASSERT(!ERTS_PROC_IS_EXITING(c_p)); } diff --git a/erts/emulator/beam/beam_load.c b/erts/emulator/beam/beam_load.c index 3ac98cec8d..e61199a8fd 100644 --- a/erts/emulator/beam/beam_load.c +++ b/erts/emulator/beam/beam_load.c @@ -459,7 +459,7 @@ typedef struct LoaderState { #ifdef DEBUG # define GARBAGE 0xCC -# define DEBUG_INIT_GENOP(Dst) memset(Dst, GARBAGE, sizeof(GenOp)) +# define DEBUG_INIT_GENOP(Dst) sys_memset(Dst, GARBAGE, sizeof(GenOp)) #else # define DEBUG_INIT_GENOP(Dst) #endif @@ -554,6 +554,7 @@ static Eterm get_module_info(Process* p, ErtsCodeIndex code_ix, static Eterm exported_from_module(Process* p, ErtsCodeIndex code_ix, Eterm mod); static Eterm functions_in_module(Process* p, BeamCodeHeader*); +static Eterm nifs_in_module(Process* p, Eterm module); static Eterm attributes_for_module(Process* p, BeamCodeHeader*); static Eterm compilation_info_for_module(Process* p, BeamCodeHeader*); static Eterm md5_of_module(Process* p, BeamCodeHeader*); @@ -1422,7 +1423,7 @@ load_atom_table(LoaderState* stp, ErtsAtomEncoding enc) Atom* ap; ap = atom_tab(atom_val(stp->atom[1])); - memcpy(sbuf, ap->name, ap->len); + sys_memcpy(sbuf, ap->name, ap->len); sbuf[ap->len] = '\0'; LoadError1(stp, "module name in object code is %s", sbuf); } @@ -2089,7 +2090,7 @@ load_code(LoaderState* stp) erts_alloc(ERTS_ALC_T_LOADER_TMP, (arity+last_op->a[arg].val) *sizeof(GenOpArg)); - memcpy(last_op->a, last_op->def_args, + sys_memcpy(last_op->a, last_op->def_args, arity*sizeof(GenOpArg)); arity += last_op->a[arg].val; break; @@ -4928,7 +4929,9 @@ freeze_code(LoaderState* stp) line_items[i] = codev + stp->ci - 1; line_tab->fname_ptr = (Eterm*) &line_items[i + 1]; - memcpy(line_tab->fname_ptr, stp->fname, stp->num_fnames*sizeof(Eterm)); + if (stp->num_fnames) + sys_memcpy(line_tab->fname_ptr, stp->fname, + stp->num_fnames*sizeof(Eterm)); line_tab->loc_size = stp->loc_size; if (stp->loc_size == 2) { @@ -5511,8 +5514,8 @@ transform_engine(LoaderState* st) case TOP_store_rest_args: { GENOP_ARITY(instr, instr->arity+num_rest_args); - memcpy(instr->a, instr->def_args, ap*sizeof(GenOpArg)); - memcpy(instr->a+ap, rest_args, num_rest_args*sizeof(GenOpArg)); + sys_memcpy(instr->a, instr->def_args, ap*sizeof(GenOpArg)); + sys_memcpy(instr->a+ap, rest_args, num_rest_args*sizeof(GenOpArg)); ap += num_rest_args; } break; @@ -5965,6 +5968,8 @@ get_module_info(Process* p, ErtsCodeIndex code_ix, BeamCodeHeader* code_hdr, return exported_from_module(p, code_ix, module); } else if (what == am_functions) { return functions_in_module(p, code_hdr); + } else if (what == am_nifs) { + return nifs_in_module(p, module); } else if (what == am_attributes) { return attributes_for_module(p, code_hdr); } else if (what == am_compile) { @@ -6018,6 +6023,46 @@ functions_in_module(Process* p, /* Process whose heap to use. */ } /* + * Builds a list of all NIFs in the given module: + * [{Name, Arity},...] + */ +Eterm +nifs_in_module(Process* p, Eterm module) +{ + Eterm nif_list, *hp; + Module *mod; + + mod = erts_get_module(module, erts_active_code_ix()); + nif_list = NIL; + + if (mod->curr.nif != NULL) { + int func_count, func_ix; + ErlNifFunc *funcs; + + func_count = erts_nif_get_funcs(mod->curr.nif, &funcs); + hp = HAlloc(p, func_count * 5); + + for (func_ix = func_count - 1; func_ix >= 0; func_ix--) { + Eterm name, arity, pair; + ErlNifFunc *func; + + func = &funcs[func_ix]; + + name = am_atom_put(func->name, sys_strlen(func->name)); + arity = make_small(func->arity); + + pair = TUPLE2(hp, name, arity); + hp += 3; + + nif_list = CONS(hp, pair, nif_list); + hp += 2; + } + } + + return nif_list; +} + +/* * Returns 'true' if mod has any native compiled functions, otherwise 'false' */ @@ -6460,7 +6505,7 @@ stub_copy_info(LoaderState* stp, Sint decoded_size; Uint size = stp->chunks[chunk].size; if (size != 0) { - memcpy(info, stp->chunks[chunk].start, size); + sys_memcpy(info, stp->chunks[chunk].start, size); *ptr_word = info; decoded_size = erts_decode_ext_size(info, size); if (decoded_size < 0) { @@ -7030,8 +7075,8 @@ void dbg_set_traced_mfa(const char* m, const char* f, Uint a) { unsigned i = dbg_trace_ix++; ASSERT(i < MFA_MAX); - dbg_trace_m[i] = am_atom_put(m, strlen(m)); - dbg_trace_f[i] = am_atom_put(f, strlen(f)); + dbg_trace_m[i] = am_atom_put(m, sys_strlen(m)); + dbg_trace_f[i] = am_atom_put(f, sys_strlen(f)); dbg_trace_a[i] = a; } diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c index bfd572335f..232597c5b6 100644 --- a/erts/emulator/beam/bif.c +++ b/erts/emulator/beam/bif.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2017. All Rights Reserved. + * Copyright Ericsson AB 1996-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,11 +46,12 @@ #include "erl_bif_unique.h" #include "erl_map.h" #include "erl_msacc.h" +#include "erl_proc_sig_queue.h" Export *erts_await_result; +static Export await_exit_trap; static Export* flush_monitor_messages_trap = NULL; static Export* set_cpu_topology_trap = NULL; -static Export* await_proc_exit_trap = NULL; static Export* await_port_send_result_trap = NULL; Export* erts_format_cpu_topology_trap = NULL; static Export dsend_continue_trap_export; @@ -92,83 +93,51 @@ BIF_RETTYPE spawn_3(BIF_ALIST_3) /* Utility to add a new link between processes p and another internal * process (rpid). Process p must be the currently executing process. */ -static int insert_internal_link(Process* p, Eterm rpid) -{ - Process *rp; - ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK; - - ASSERT(is_internal_pid(rpid)); - - if (IS_TRACED(p) - && (ERTS_TRACE_FLAGS(p) & (F_TRACE_SOL|F_TRACE_SOL1))) { - rp_locks = ERTS_PROC_LOCKS_ALL; - } - - erts_proc_lock(p, ERTS_PROC_LOCK_LINK); - - /* get a pointer to the process struct of the linked process */ - rp = erts_pid2proc_opt(p, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, - rpid, rp_locks, - ERTS_P2P_FLG_ALLOW_OTHER_X); - - if (!rp) { - erts_proc_unlock(p, ERTS_PROC_LOCK_LINK); - return 0; - } - - if (p != rp) { - erts_add_link(&ERTS_P_LINKS(p), LINK_PID, rp->common.id); - erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, p->common.id); - - ASSERT(IS_TRACER_VALID(ERTS_TRACER(p))); - - if (IS_TRACED(p)) { - if (ERTS_TRACE_FLAGS(p) & (F_TRACE_SOL|F_TRACE_SOL1)) { - ERTS_TRACE_FLAGS(rp) |= (ERTS_TRACE_FLAGS(p) & TRACEE_FLAGS); - erts_tracer_replace(&rp->common, ERTS_TRACER(p)); - if (ERTS_TRACE_FLAGS(p) & F_TRACE_SOL1) { /* maybe override */ - ERTS_TRACE_FLAGS(rp) &= ~(F_TRACE_SOL1 | F_TRACE_SOL); - ERTS_TRACE_FLAGS(p) &= ~(F_TRACE_SOL1 | F_TRACE_SOL); - } - } - } - } - if (IS_TRACED_FL(rp, F_TRACE_PROCS)) - trace_proc(p, p == rp ? rp_locks : ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, - rp, am_getting_linked, p->common.id); - - if (p == rp) - erts_proc_unlock(p, rp_locks & ~ERTS_PROC_LOCK_MAIN); - else { - erts_proc_unlock(p, ERTS_PROC_LOCK_LINK); - erts_proc_unlock(rp, rp_locks); - } - - return 1; -} - /* create a link to the process */ BIF_RETTYPE link_1(BIF_ALIST_1) { - DistEntry *dep; - if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS)) { trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN, BIF_P, am_link, BIF_ARG_1); } /* check that the pid or port which is our argument is OK */ if (is_internal_pid(BIF_ARG_1)) { - if (insert_internal_link(BIF_P, BIF_ARG_1)) { - BIF_RET(am_true); - } - else { - goto res_no_proc; - } + int created; + ErtsLinkData *ldp; + ErtsLink *lnk; + + if (BIF_P->common.id == BIF_ARG_1) + BIF_RET(am_true); + + if (!erts_proc_lookup(BIF_ARG_1)) + goto res_no_proc; + + lnk = erts_link_tree_lookup_create(&ERTS_P_LINKS(BIF_P), + &created, + ERTS_LNK_TYPE_PROC, + BIF_P->common.id, + BIF_ARG_1); + if (!created) + BIF_RET(am_true); + + ldp = erts_link_to_data(lnk); + + + if (erts_proc_sig_send_link(BIF_P, BIF_ARG_1, &ldp->b)) + BIF_RET(am_true); + + erts_link_tree_delete(&ERTS_P_LINKS(BIF_P), lnk); + erts_link_release_both(ldp); + goto res_no_proc; } if (is_internal_port(BIF_ARG_1)) { - int send_link_signal = 0; + int created; + ErtsLinkData *ldp; + ErtsLink *lnk; + Eterm ref; + Eterm *refp; Port *prt = erts_port_lookup(BIF_ARG_1, (erts_port_synchronous_ops ? ERTS_PORT_SFLGS_INVALID_DRIVER_LOOKUP @@ -177,31 +146,31 @@ BIF_RETTYPE link_1(BIF_ALIST_1) goto res_no_proc; } - erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK); - - if (erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, BIF_ARG_1) >= 0) - send_link_signal = 1; - /* else: already linked */ + lnk = erts_link_tree_lookup_create(&ERTS_P_LINKS(BIF_P), + &created, + ERTS_LNK_TYPE_PORT, + BIF_P->common.id, + BIF_ARG_1); + if (!created) + BIF_RET(am_true); - erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK); + ldp = erts_link_to_data(lnk); + refp = erts_port_synchronous_ops ? &ref : NULL; - if (send_link_signal) { - Eterm ref; - Eterm *refp = erts_port_synchronous_ops ? &ref : NULL; - - switch (erts_port_link(BIF_P, prt, BIF_P->common.id, refp)) { - case ERTS_PORT_OP_DROPPED: - case ERTS_PORT_OP_BADARG: - goto res_no_proc; - case ERTS_PORT_OP_SCHEDULED: - if (refp) { - ASSERT(is_internal_ordinary_ref(ref)); - BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true); - } - default: - break; - } - } + switch (erts_port_link(BIF_P, prt, &ldp->b, refp)) { + case ERTS_PORT_OP_DROPPED: + case ERTS_PORT_OP_BADARG: + erts_link_tree_delete(&ERTS_P_LINKS(BIF_P), lnk); + erts_link_release_both(ldp); + goto res_no_proc; + case ERTS_PORT_OP_SCHEDULED: + if (refp) { + ASSERT(is_internal_ordinary_ref(ref)); + BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true); + } + default: + break; + } BIF_RET(am_true); } else if (is_external_port(BIF_ARG_1) @@ -210,317 +179,203 @@ BIF_RETTYPE link_1(BIF_ALIST_1) } if (is_external_pid(BIF_ARG_1)) { + ErtsLinkData *ldp; + int created; + DistEntry *dep; + ErtsLink *lnk; + int code; + ErtsDSigData dsd; + + dep = external_pid_dist_entry(BIF_ARG_1); + if (dep == erts_this_dist_entry) + goto res_no_proc; - erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK); - - /* We may earn time by checking first that we're not linked already */ - if (erts_lookup_link(ERTS_P_LINKS(BIF_P), BIF_ARG_1) != NULL) { - erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK); - BIF_RET(am_true); - } - else { - ErtsLink *lnk; - int code; - ErtsDSigData dsd; - dep = external_pid_dist_entry(BIF_ARG_1); - if (dep == erts_this_dist_entry) { - erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK); - goto res_no_proc; - } - - code = erts_dsig_prepare(&dsd, dep, BIF_P, - (ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK), - ERTS_DSP_RLOCK, 0, 1); - switch (code) { - case ERTS_DSIG_PREP_NOT_ALIVE: - case ERTS_DSIG_PREP_NOT_CONNECTED: { - ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK; - erts_aint32_t state; - erts_proc_lock(BIF_P, (ERTS_PROC_LOCKS_ALL & ~locks)); - locks = ERTS_PROC_LOCKS_ALL; - erts_send_exit_signal(BIF_P, BIF_ARG_1, BIF_P, &locks, - am_noconnection, NIL, NULL, 0); - erts_proc_unlock(BIF_P, locks & ERTS_PROC_LOCKS_ALL_MINOR); - - /* - * Copy-paste from old dist_exit_3, not sure if we really - * need erts_handle_pending_exit when exit_2 does not. - */ - state = erts_atomic32_read_acqb(&BIF_P->state); - if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) { - if (state & ERTS_PSFLG_PENDING_EXIT) - erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN); - ERTS_BIF_EXITED(BIF_P); - } - BIF_RET(am_true); - } - case ERTS_DSIG_PREP_PENDING: - case ERTS_DSIG_PREP_CONNECTED: - /* - * We have (pending) connection. - * Setup link and enqueue link signal. - */ - erts_de_links_lock(dep); - - erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, BIF_ARG_1); - lnk = erts_add_or_lookup_link(&(dep->nlinks), - LINK_PID, - BIF_P->common.id); - ASSERT(lnk != NULL); - erts_add_link(&ERTS_LINK_ROOT(lnk), LINK_PID, BIF_ARG_1); - - erts_de_links_unlock(dep); - erts_de_runlock(dep); - erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK); - - code = erts_dsig_send_link(&dsd, BIF_P->common.id, BIF_ARG_1); - if (code == ERTS_DSIG_SEND_YIELD) - ERTS_BIF_YIELD_RETURN(BIF_P, am_true); - BIF_RET(am_true); - default: - ERTS_ASSERT(! "Invalid dsig prepare result"); - } - } + lnk = erts_link_tree_lookup_create(&ERTS_P_LINKS(BIF_P), + &created, + ERTS_LNK_TYPE_DIST_PROC, + BIF_P->common.id, + BIF_ARG_1); + + if (!created) + BIF_RET(am_true); /* Already present... */ + + ldp = erts_link_to_data(lnk); + + code = erts_dsig_prepare(&dsd, dep, BIF_P, + ERTS_PROC_LOCK_MAIN, + ERTS_DSP_RLOCK, 0, 1); + switch (code) { + case ERTS_DSIG_PREP_NOT_ALIVE: + case ERTS_DSIG_PREP_NOT_CONNECTED: + erts_link_set_dead_dist(&ldp->b, dep->sysname); + erts_proc_sig_send_link_exit(NULL, BIF_ARG_1, &ldp->b, + am_noconnection, NIL); + BIF_RET(am_true); + + case ERTS_DSIG_PREP_PENDING: + case ERTS_DSIG_PREP_CONNECTED: { + /* + * We have (pending) connection. + * Setup link and enqueue link signal. + */ +#ifdef DEBUG + int inserted = +#endif + erts_link_dist_insert(&ldp->b, dep->mld); + ASSERT(inserted); + erts_de_runlock(dep); + + code = erts_dsig_send_link(&dsd, BIF_P->common.id, BIF_ARG_1); + if (code == ERTS_DSIG_SEND_YIELD) + ERTS_BIF_YIELD_RETURN(BIF_P, am_true); + BIF_RET(am_true); + break; + } + default: + ERTS_ASSERT(! "Invalid dsig prepare result"); + } } BIF_ERROR(BIF_P, BADARG); -res_no_proc: { - erts_aint32_t state = erts_atomic32_read_nob(&BIF_P->state); - if (state & ERTS_PSFLG_TRAP_EXIT) { - ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN; - erts_deliver_exit_message(BIF_ARG_1, BIF_P, &locks, am_noproc, NIL); - erts_proc_unlock(BIF_P, ~ERTS_PROC_LOCK_MAIN & locks); - BIF_RET(am_true); - } - else - BIF_ERROR(BIF_P, EXC_NOPROC); +res_no_proc: + if (BIF_P->flags & F_TRAP_EXIT) { + ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN; + erts_deliver_exit_message(BIF_ARG_1, BIF_P, &locks, am_noproc, NIL); + erts_proc_unlock(BIF_P, ~ERTS_PROC_LOCK_MAIN & locks); + BIF_RET(am_true); + } + else { + /* + * This behaviour is *really* sad but link/1 has + * behaved like this for ages (and this behaviour is + * actually documented)... :'-( + * + * The proper behavior would have been to + * send calling process an exit signal.. + */ + BIF_ERROR(BIF_P, EXC_NOPROC); } } -/* 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) +demonitor(Process *c_p, Eterm ref, Eterm *multip) { - ErtsDSigData dsd; - ErtsMonitor *dmon; - ErtsMonitor *mon; - int code; - Eterm res = am_false; - - ERTS_LC_ASSERT((ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK) - == erts_proc_lc_my_proc_locks(c_p)); - - code = erts_dsig_prepare(&dsd, dep, c_p, ERTS_PROC_LOCK_MAIN, - ERTS_DSP_RLOCK, 0, 0); - switch (code) { - case ERTS_DSIG_PREP_NOT_ALIVE: - case ERTS_DSIG_PREP_NOT_CONNECTED: - /* - * In the smp case this is possible if the node goes - * down just before the call to demonitor. - */ - if (dep) { - erts_de_links_lock(dep); - dmon = erts_remove_monitor(&dep->monitors, ref); - erts_de_links_unlock(dep); - if (dmon) - erts_destroy_monitor(dmon); - } - mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref); - erts_proc_unlock(c_p, ERTS_PROC_LOCK_LINK); - - res = am_true; - break; - - case ERTS_DSIG_PREP_PENDING: - case ERTS_DSIG_PREP_CONNECTED: + ErtsMonitor *mon; /* The monitor entry to delete */ - erts_de_links_lock(dep); - mon = erts_remove_monitor(&ERTS_P_MONITORS(c_p), ref); - dmon = erts_remove_monitor(&dep->monitors, ref); - erts_de_links_unlock(dep); - erts_de_runlock(dep); - erts_proc_unlock(c_p, ERTS_PROC_LOCK_LINK); + *multip = am_false; - if (!dmon) { - /* - * This is possible when smp support is enabled. - * 'DOWN' message just arrived. - */ - res = am_true; - } - else { - /* - * Soft (no force) send, use ->data in dist slot - * monitor list since in case of monitor name - * the atom is stored there. Yield if necessary. - */ - code = erts_dsig_send_demonitor(&dsd, - c_p->common.id, - (mon->name != NIL - ? mon->name - : mon->u.pid), - ref, - 0); - res = (code == ERTS_DSIG_SEND_YIELD ? am_yield : am_true); - erts_destroy_monitor(dmon); - } - break; - default: - ERTS_ASSERT(! "Invalid dsig prepare result"); - } + if (is_not_internal_ref(ref)) { + if (is_external_ref(ref) + && (erts_this_dist_entry + == external_ref_dist_entry(ref))) { + return am_false; + } + return am_badarg; /* Not monitored by this monitor's ref */ + } + mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p), ref); + if (!mon) + return am_false; - /* - * We aren't allowed to destroy 'mon' until now, since 'to' - * may refer into 'mon' (external pid). - */ - ASSERT(mon); /* Since link lock wasn't released between - lookup and remove */ - erts_destroy_monitor(mon); + if (!erts_monitor_is_origin(mon)) + return am_badarg; - ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p)); - return res; -} + erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), mon); -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); - - if (!mon) - *res = am_false; - else - { - *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_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - if (rmon != NULL) - erts_destroy_monitor(rmon); - } - else { - ERTS_ASSERT_IS_NOT_EXITING(c_p); - } -} - -static ERTS_INLINE BIF_RETTYPE -demonitor_local_port(Process *origin, Eterm ref, Eterm target) -{ - BIF_RETTYPE res = am_false; - Port *port = erts_port_lookup_raw(target); - - if (!port) { - BIF_ERROR(origin, BADARG); - } - erts_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_ASSERT_IS_NOT_EXITING(origin); - } - BIF_RET(res); -} + switch (mon->type) { -/* Can return atom true, false, yield, internal_error, badarg or - * THE_NON_VALUE if error occurred 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 */ - BIF_RETTYPE res = am_false; - int unlock_link = 1; + case ERTS_MON_TYPE_TIME_OFFSET: + *multip = am_true; + erts_demonitor_time_offset(mon); + return am_true; + + case ERTS_MON_TYPE_PORT: { + Port *prt; + ASSERT(is_internal_port(mon->other.item)); + prt = erts_port_lookup(mon->other.item, ERTS_PORT_SFLGS_DEAD); + if (!prt || erts_port_demonitor(c_p, prt, mon) == ERTS_PORT_OP_DROPPED) + erts_monitor_release(mon); + return am_true; + } - erts_proc_lock(c_p, ERTS_PROC_LOCK_LINK); + case ERTS_MON_TYPE_PROC: + erts_proc_sig_send_demonitor(mon); + return am_true; - if (is_not_internal_ref(ref)) { - res = am_badarg; - goto done; /* Cannot be this monitor's ref */ - } + case ERTS_MON_TYPE_DIST_PROC: { + ErtsMonitorData *mdp = erts_monitor_to_data(mon); + Eterm to = mon->other.item; + DistEntry *dep; + int code = ERTS_DSIG_SEND_OK; + int deleted; + ErtsDSigData dsd; - mon = erts_lookup_monitor(ERTS_P_MONITORS(c_p), ref); - if (!mon) { - goto done; - } + ASSERT(is_external_pid(to) || is_node_name_atom(to)); - switch (mon->type) { - case MON_TIME_OFFSET: - *multip = am_true; - erts_demonitor_time_offset(ref); - res = am_true; - break; - case MON_ORIGIN: - to = mon->u.pid; - *multip = am_false; - if (is_atom(to)) { + if (is_external_pid(to)) + dep = external_pid_dist_entry(to); + else { /* 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); - } else if (is_port(to)) { - if (port_dist_entry(to) != erts_this_dist_entry) { - goto badarg; + if (!dep) { + erts_monitor_release(mon); + return am_false; } - res = demonitor_local_port(c_p, ref, to); - unlock_link = 0; - goto done; - } else { - 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; + + code = erts_dsig_prepare(&dsd, dep, c_p, ERTS_PROC_LOCK_MAIN, + ERTS_DSP_RLOCK, 0, 0); + + deleted = erts_monitor_dist_delete(&mdp->target); + + switch (code) { + case ERTS_DSIG_PREP_NOT_ALIVE: + case ERTS_DSIG_PREP_NOT_CONNECTED: + /* + * In the smp case this is possible if the node goes + * down just before the call to demonitor. + */ + break; + + case ERTS_DSIG_PREP_PENDING: + case ERTS_DSIG_PREP_CONNECTED: { + Eterm watched; + + erts_de_runlock(dep); + + if (mon->flags & ERTS_ML_FLG_NAME) + watched = ((ErtsMonitorDataExtended *) mdp)->u.name; + else + watched = to; + + /* + * Soft (no force) send, use ->data in dist slot + * monitor list since in case of monitor name + * the atom is stored there. Yield if necessary. + */ + code = erts_dsig_send_demonitor(&dsd, c_p->common.id, + watched, mdp->ref, 0); + break; } - else { /* Local monitor */ - demonitor_local_process(c_p, ref, to, &res); + + default: + ERTS_INTERNAL_ERROR("invalid result from erts_dsig_prepare()"); + break; } - break; - default /* case */ : -badarg: - res = am_badarg; /* will be converted to error by caller */ - *multip = am_false; - break; - } -done: - if (unlock_link) - erts_proc_unlock(c_p, ERTS_PROC_LOCK_LINK); + if (deleted) + erts_monitor_release(&mdp->target); + + erts_monitor_release(mon); + return code == ERTS_DSIG_SEND_YIELD ? am_yield : am_true; + } - ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p)); - BIF_RET(res); + default: + ERTS_INTERNAL_ERROR("Unexpected monitor type"); + return am_false; + } } BIF_RETTYPE demonitor_1(BIF_ALIST_1) @@ -528,25 +383,23 @@ BIF_RETTYPE demonitor_1(BIF_ALIST_1) Eterm multi; switch (demonitor(BIF_P, BIF_ARG_1, &multi)) { 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: + case am_true: + BIF_RET(am_true); + case am_yield: + ERTS_BIF_YIELD_RETURN(BIF_P, am_true); + case am_badarg: default: - ASSERT(! "demonitor(): internal error"); - BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); + BIF_ERROR(BIF_P, BADARG); } } BIF_RETTYPE demonitor_2(BIF_ALIST_2) { - BIF_RETTYPE res = am_true; - Eterm multi = am_false; - int info = 0; - int flush = 0; - Eterm list = BIF_ARG_2; + BIF_RETTYPE res; + Eterm multi = am_false; + int info = 0; + int flush = 0; + Eterm list = BIF_ARG_2; while (is_list(list)) { Eterm* consp = list_val(list); @@ -566,10 +419,9 @@ BIF_RETTYPE demonitor_2(BIF_ALIST_2) if (is_not_nil(list)) goto badarg; + res = am_true; switch (demonitor(BIF_P, BIF_ARG_1, &multi)) { - case THE_NON_VALUE: - /* If other error occurred or trap has been set up - pass through */ - BIF_RET(THE_NON_VALUE); + case am_false: if (info) res = am_false; @@ -578,20 +430,29 @@ flush_messages: BIF_TRAP3(flush_monitor_messages_trap, BIF_P, BIF_ARG_1, multi, res); } + /* Fall through... */ + case am_true: if (multi == am_true && flush) goto flush_messages; BIF_RET(res); + case am_yield: - ERTS_BIF_YIELD_RETURN(BIF_P, am_true); + /* return true after yield... */ + if (flush) { + ERTS_VBUMP_ALL_REDS(BIF_P); + goto flush_messages; + } + ERTS_BIF_YIELD_RETURN(BIF_P, am_true); + case am_badarg: -badarg: - BIF_ERROR(BIF_P, BADARG); - case am_internal_error: default: - ASSERT(! "demonitor(): internal error"); - BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); + break; + } + +badarg: + BIF_ERROR(BIF_P, BADARG); } /* Type must be atomic object! */ @@ -631,292 +492,212 @@ erts_queue_monitor_message(Process *p, erts_queue_message(p, *p_locksp, msgp, tup, am_system); } -static Eterm -local_pid_monitor(Process *p, Eterm target, Eterm mon_ref, int boolean) +BIF_RETTYPE monitor_2(BIF_ALIST_2) { - Eterm ret = mon_ref; - Process *rp; - ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK; - - if (target == p->common.id) { - return ret; - } + Eterm target = BIF_ARG_2; + Eterm tmp_heap[3]; + Eterm ref, id, name; + ErtsMonitorData *mdp; + + if (BIF_ARG_1 == am_process) { + DistEntry *dep; + int byname; + + if (is_internal_pid(target)) { + name = NIL; + id = target; + + local_process: + + ref = erts_make_ref(BIF_P); + if (id != BIF_P->common.id) { + mdp = erts_monitor_create(ERTS_MON_TYPE_PROC, + ref, BIF_P->common.id, + id, name); + erts_monitor_tree_insert(&ERTS_P_MONITORS(BIF_P), + &mdp->origin); + + if (!erts_proc_sig_send_monitor(&mdp->target, id)) + erts_proc_sig_send_monitor_down(&mdp->target, + am_noproc); + } + BIF_RET(ref); + } - erts_proc_lock(p, ERTS_PROC_LOCK_LINK); - rp = erts_pid2proc_opt(p, p_locks, - target, ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X); - if (!rp) { - erts_proc_unlock(p, ERTS_PROC_LOCK_LINK); - p_locks &= ~ERTS_PROC_LOCK_LINK; - if (boolean) - ret = am_false; - else - erts_queue_monitor_message(p, &p_locks, - mon_ref, am_process, target, am_noproc); - } - else { - ASSERT(rp != p); + if (is_atom(target)) { + local_named_process: + name = target; + id = erts_whereis_name_to_id(BIF_P, target); + if (is_internal_pid(id)) + goto local_process; + target = TUPLE2(&tmp_heap[0], name, + erts_this_dist_entry->sysname); + goto noproc; + } - if (boolean) - ret = am_true; + if (is_external_pid(target)) { + ErtsDSigData dsd; + int code; + + dep = external_pid_dist_entry(target); + if (dep == erts_this_dist_entry) + goto noproc; + + id = target; + name = NIL; + byname = 0; + + remote_process: + + ref = erts_make_ref(BIF_P); + mdp = erts_monitor_create(ERTS_MON_TYPE_DIST_PROC, ref, + BIF_P->common.id, id, name); + erts_monitor_tree_insert(&ERTS_P_MONITORS(BIF_P), &mdp->origin); + + code = erts_dsig_prepare(&dsd, dep, + BIF_P, ERTS_PROC_LOCK_MAIN, + ERTS_DSP_RLOCK, 0, 1); + switch (code) { + case ERTS_DSIG_PREP_NOT_ALIVE: + case ERTS_DSIG_PREP_NOT_CONNECTED: + erts_monitor_set_dead_dist(&mdp->target, dep->sysname); + erts_proc_sig_send_monitor_down(&mdp->target, am_noconnection); + code = ERTS_DSIG_SEND_OK; + break; - erts_add_monitor(&ERTS_P_MONITORS(p), MON_ORIGIN, mon_ref, target, NIL); - erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, mon_ref, p->common.id, NIL); + case ERTS_DSIG_PREP_PENDING: + case ERTS_DSIG_PREP_CONNECTED: { +#ifdef DEBUG + int inserted = +#endif - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - } + erts_monitor_dist_insert(&mdp->target, dep->mld); + ASSERT(inserted); + erts_de_runlock(dep); - erts_proc_unlock(p, p_locks & ~ERTS_PROC_LOCK_MAIN); + code = erts_dsig_send_monitor(&dsd, BIF_P->common.id, target, ref); + break; + } - return ret; -} + default: + ERTS_ASSERT(! "Invalid dsig prepare result"); + code = ERTS_DSIG_SEND_OK; + break; + } -static BIF_RETTYPE -local_port_monitor(Process *origin, Eterm target) -{ - BIF_RETTYPE ref = erts_make_ref(origin); - Port *port = erts_sig_lookup_port(origin, target); - ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN; + if (byname) + erts_deref_dist_entry(dep); - 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; + if (code == ERTS_DSIG_SEND_YIELD) + ERTS_BIF_YIELD_RETURN(BIF_P, ref); + BIF_RET(ref); } - } - erts_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); + if (is_tuple(target)) { + Eterm *tpl = tuple_val(target); + if (arityval(tpl[0]) != 2) + goto badarg; + if (is_not_atom(tpl[1]) || is_not_atom(tpl[2])) + goto badarg; + if (!erts_is_alive && tpl[2] != am_Noname) + goto badarg; + target = tpl[1]; + dep = erts_find_or_insert_dist_entry(tpl[2]); + if (dep == erts_this_dist_entry) { + erts_deref_dist_entry(dep); + goto local_named_process; + } - ErtsProcLocks p_locks = ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK; - Process *proc = NULL; - Port *port = NULL; + id = dep->sysname; + name = target; + byname = 1; + goto remote_process; + } - erts_proc_lock(self, ERTS_PROC_LOCK_LINK); + /* badarg... */ + } + else if (BIF_ARG_1 == am_port) { + + if (is_internal_port(target)) { + Port *prt; + name = NIL; + id = target; + local_port: + ref = erts_make_ref(BIF_P); + mdp = erts_monitor_create(ERTS_MON_TYPE_PORT, ref, + BIF_P->common.id, id, name); + erts_monitor_tree_insert(&ERTS_P_MONITORS(BIF_P), &mdp->origin); + prt = erts_port_lookup(id, ERTS_PORT_SFLGS_INVALID_LOOKUP); + if (!prt || erts_port_monitor(BIF_P, prt, &mdp->target) == ERTS_PORT_OP_DROPPED) + erts_proc_sig_send_monitor_down(&mdp->target, am_noproc); + BIF_RET(ref); + } - erts_whereis_name(self, p_locks, target_name, - &proc, ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X, - &port, 0); + if (is_atom(target)) { + local_named_port: + name = target; + id = erts_whereis_name_to_id(BIF_P, target); + if (is_internal_port(id)) + goto local_port; + target = TUPLE2(&tmp_heap[0], name, + erts_this_dist_entry->sysname); + goto noproc; + } - /* 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,self); - - erts_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(self, &p_locks, - ret, - type, /* = process|port :: atom() */ - item, am_noproc); - UnUseTmpHeap(3,self); - } - else if (port) { - erts_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_ordinary_ref(ret)); - BIF_TRAP3(await_port_send_result_trap, self, - ret, am_true, ret); - /* bif_trap returns */ - } break; - default: + if (is_external_port(target)) { + if (erts_this_dist_entry == external_port_dist_entry(target)) + goto noproc; 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_proc_unlock(proc, ERTS_PROC_LOCK_LINK); - } - - if (p_locks) { - erts_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN); - } - BIF_RET(ret); -badarg: - if (p_locks) { - erts_proc_unlock(self, p_locks & ~ERTS_PROC_LOCK_MAIN); - } - BIF_ERROR(self, BADARG); -} -static BIF_RETTYPE -remote_monitor(Process *p, Eterm bifarg1, Eterm bifarg2, - DistEntry *dep, Eterm target, int byname) -{ - ErtsDSigData dsd; - BIF_RETTYPE ret; - int code; - - ASSERT(dep); - erts_proc_lock(p, ERTS_PROC_LOCK_LINK); - code = erts_dsig_prepare(&dsd, dep, - p, (ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK), - ERTS_DSP_RLOCK, 0, 1); - switch (code) { - case ERTS_DSIG_PREP_NOT_ALIVE: - case ERTS_DSIG_PREP_NOT_CONNECTED: - erts_proc_unlock(p, ERTS_PROC_LOCK_LINK); - ERTS_BIF_PREP_TRAP2(ret, dmonitor_p_trap, p, bifarg1, bifarg2); - break; - case ERTS_DSIG_PREP_PENDING: - case ERTS_DSIG_PREP_CONNECTED: - { - Eterm p_trgt, p_name, d_name, mon_ref; + if (is_tuple(target)) { + Eterm *tpl = tuple_val(target); + if (arityval(tpl[0]) != 2) + goto badarg; + if (is_not_atom(tpl[1]) || is_not_atom(tpl[2])) + goto badarg; + if (tpl[2] == erts_this_dist_entry->sysname) { + target = tpl[1]; + goto local_named_port; + } + } - mon_ref = erts_make_ref(p); + /* badarg... */ + } + else if (BIF_ARG_1 == am_time_offset) { - if (byname) { - p_trgt = dep->sysname; - p_name = target; - d_name = target; - } - else { - p_trgt = target; - p_name = NIL; - d_name = NIL; - } + if (target != am_clock_service) + goto badarg; + ref = erts_make_ref(BIF_P); + mdp = erts_monitor_create(ERTS_MON_TYPE_TIME_OFFSET, + ref, BIF_P->common.id, + am_clock_service, NIL); + erts_monitor_tree_insert(&ERTS_P_MONITORS(BIF_P), &mdp->origin); - erts_de_links_lock(dep); + erts_monitor_time_offset(&mdp->target); - erts_add_monitor(&ERTS_P_MONITORS(p), MON_ORIGIN, mon_ref, p_trgt, - p_name); - erts_add_monitor(&(dep->monitors), MON_TARGET, mon_ref, p->common.id, - d_name); + BIF_RET(ref); + } - erts_de_links_unlock(dep); - erts_de_runlock(dep); - erts_proc_unlock(p, ERTS_PROC_LOCK_LINK); +badarg: - code = erts_dsig_send_monitor(&dsd, p->common.id, target, mon_ref); - if (code == ERTS_DSIG_SEND_YIELD) - ERTS_BIF_PREP_YIELD_RETURN(ret, p, mon_ref); - else - ERTS_BIF_PREP_RET(ret, mon_ref); - } - break; - default: - ERTS_ASSERT(! "Invalid dsig prepare result"); - } + BIF_ERROR(BIF_P, BADARG); - BIF_RET(ret); -} - -BIF_RETTYPE monitor_2(BIF_ALIST_2) -{ - Eterm target = BIF_ARG_2; - BIF_RETTYPE ret; - DistEntry *dep = NULL; +noproc: { + ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN; - /* Only process monitors are implemented */ - switch (BIF_ARG_1) { - case am_time_offset: { - Eterm ref; - if (BIF_ARG_2 != am_clock_service) { - goto badarg; - } - ref = erts_make_ref(BIF_P); - erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK); - erts_add_monitor(&ERTS_P_MONITORS(BIF_P), MON_TIME_OFFSET, - ref, am_clock_service, NIL); - erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK); - erts_monitor_time_offset(BIF_P->common.id, ref); - BIF_RET(ref); - } - case am_process: - case am_port: - break; - default: - goto badarg; - } + ref = erts_make_ref(BIF_P); + erts_queue_monitor_message(BIF_P, + &locks, + ref, + BIF_ARG_1, + target, + am_noproc); + if (locks != ERTS_PROC_LOCK_MAIN) + erts_proc_unlock(BIF_P, locks & ~ERTS_PROC_LOCK_MAIN); - 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, BIF_ARG_1, target); - } else if (is_tuple(target)) { - Eterm *tp = tuple_val(target); - Eterm remote_node; - Eterm name; - if (arityval(*tp) != 2) { - goto badarg; - } - remote_node = tp[2]; - name = tp[1]; - if (!is_atom(remote_node) || !is_atom(name)) { - goto badarg; - } - if (!erts_is_alive && remote_node != am_Noname) { - goto badarg; /* Remote monitor from (this) undistributed node */ - } - dep = erts_find_or_insert_dist_entry(remote_node); - if (dep == erts_this_dist_entry) { - ret = local_name_monitor(BIF_P, BIF_ARG_1, name); - } else { - ret = remote_monitor(BIF_P, BIF_ARG_1, BIF_ARG_2, dep, name, 1); - } - erts_deref_dist_entry(dep); - } else { -badarg: - ERTS_BIF_PREP_ERROR(ret, BIF_P, BADARG); + BIF_RET(ref); } - return ret; } /**********************************************************************/ @@ -1089,91 +870,74 @@ BIF_RETTYPE spawn_opt_1(BIF_ALIST_1) /* remove a link from a process */ BIF_RETTYPE unlink_1(BIF_ALIST_1) { - Process *rp; - DistEntry *dep; - ErtsLink *l = NULL, *rl = NULL; - ErtsProcLocks cp_locks = ERTS_PROC_LOCK_MAIN; - - /* - * SMP specific note concerning incoming exit signals: - * We have to have at least the status lock during removal of - * the link half on current process, and check for and handle - * a present pending exit while the status lock is held. This - * in order to ensure that we wont be exited by a link after - * it has been removed. - * - * (We also have to have the link lock, of course, in order to - * be allowed to remove the link...) - */ - if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS)) { - trace_proc(BIF_P, cp_locks, BIF_P, am_unlink, BIF_ARG_1); + trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN, + BIF_P, am_unlink, BIF_ARG_1); } - if (is_internal_port(BIF_ARG_1)) { - erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS); - if (ERTS_PROC_PENDING_EXIT(BIF_P)) - goto handle_pending_exit; - - l = erts_remove_link(&ERTS_P_LINKS(BIF_P), BIF_ARG_1); + if (is_internal_pid(BIF_ARG_1)) { + ErtsLink *lnk = erts_link_tree_lookup(ERTS_P_LINKS(BIF_P), BIF_ARG_1); + if (lnk) { + erts_link_tree_delete(&ERTS_P_LINKS(BIF_P), lnk); + erts_proc_sig_send_unlink(BIF_P, lnk); + } + BIF_RET(am_true); + } - erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS); + if (is_internal_port(BIF_ARG_1)) { + ErtsLink *lnk = erts_link_tree_lookup(ERTS_P_LINKS(BIF_P), BIF_ARG_1); - if (l) { + if (lnk) { + Eterm ref; + Eterm *refp = erts_port_synchronous_ops ? &ref : NULL; + ErtsPortOpResult res = ERTS_PORT_OP_DROPPED; Port *prt; - erts_destroy_link(l); + erts_link_tree_delete(&ERTS_P_LINKS(BIF_P), lnk); /* Send unlink signal */ prt = erts_port_lookup(BIF_ARG_1, ERTS_PORT_SFLGS_DEAD); if (prt) { - ErtsPortOpResult res; - Eterm ref; - Eterm *refp = erts_port_synchronous_ops ? &ref : NULL; #ifdef DEBUG ref = NIL; #endif - res = erts_port_unlink(BIF_P, prt, BIF_P->common.id, refp); + res = erts_port_unlink(BIF_P, prt, lnk, refp); - if (refp && res == ERTS_PORT_OP_SCHEDULED) { - ASSERT(is_internal_ordinary_ref(ref)); - BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true); - } } + + if (res == ERTS_PORT_OP_DROPPED) + erts_link_release(lnk); + else if (refp && res == ERTS_PORT_OP_SCHEDULED) { + ASSERT(is_internal_ordinary_ref(ref)); + BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true); + } } BIF_RET(am_true); } - else if (is_external_port(BIF_ARG_1) - && external_port_dist_entry(BIF_ARG_1) == erts_this_dist_entry) { - BIF_RET(am_true); - } - - if (is_not_pid(BIF_ARG_1)) - BIF_ERROR(BIF_P, BADARG); if (is_external_pid(BIF_ARG_1)) { - ErtsDistLinkData dld; + ErtsLink *lnk, *dlnk; + ErtsLinkData *ldp; + DistEntry *dep; int code; ErtsDSigData dsd; - /* Blind removal, we might have trapped or anything, this leaves - us in a state where monitors might be inconsistent, but the dist - code should take care of it. */ - erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS); - if (ERTS_PROC_PENDING_EXIT(BIF_P)) - goto handle_pending_exit; - l = erts_remove_link(&ERTS_P_LINKS(BIF_P), BIF_ARG_1); - - erts_proc_unlock(BIF_P, - ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS); - - if (l) - erts_destroy_link(l); dep = external_pid_dist_entry(BIF_ARG_1); - if (dep == erts_this_dist_entry) { + if (dep == erts_this_dist_entry) BIF_RET(am_true); - } + + lnk = erts_link_tree_lookup(ERTS_P_LINKS(BIF_P), BIF_ARG_1); + if (!lnk) + BIF_RET(am_true); + + erts_link_tree_delete(&ERTS_P_LINKS(BIF_P), lnk); + dlnk = erts_link_to_other(lnk, &ldp); + + if (erts_link_dist_delete(dlnk)) + erts_link_release_both(ldp); + else + erts_link_release(lnk); code = erts_dsig_prepare(&dsd, dep, BIF_P, ERTS_PROC_LOCK_MAIN, ERTS_DSP_NO_LOCK, 0, 0); @@ -1181,74 +945,27 @@ BIF_RETTYPE unlink_1(BIF_ALIST_1) case ERTS_DSIG_PREP_NOT_ALIVE: case ERTS_DSIG_PREP_NOT_CONNECTED: BIF_RET(am_true); - case ERTS_DSIG_PREP_PENDING: case ERTS_DSIG_PREP_CONNECTED: - erts_remove_dist_link(&dld, BIF_P->common.id, BIF_ARG_1, dep); code = erts_dsig_send_unlink(&dsd, BIF_P->common.id, BIF_ARG_1); - erts_destroy_dist_link(&dld); if (code == ERTS_DSIG_SEND_YIELD) ERTS_BIF_YIELD_RETURN(BIF_P, am_true); - BIF_RET(am_true); - + break; default: ASSERT(! "Invalid dsig prepare result"); BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); } - } - - /* Internal pid... */ - erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS); - - cp_locks |= ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS; - - /* get process struct */ - rp = erts_pid2proc_opt(BIF_P, cp_locks, - BIF_ARG_1, ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X); - - if (ERTS_PROC_PENDING_EXIT(BIF_P)) { - if (rp && rp != BIF_P) - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - goto handle_pending_exit; + BIF_RET(am_true); } - /* unlink and ignore errors */ - l = erts_remove_link(&ERTS_P_LINKS(BIF_P), BIF_ARG_1); - if (l != NULL) - erts_destroy_link(l); - - if (!rp) { - ERTS_ASSERT_IS_NOT_EXITING(BIF_P); + if (is_external_port(BIF_ARG_1)) { + if (external_port_dist_entry(BIF_ARG_1) == erts_this_dist_entry) + BIF_RET(am_true); + /* Links to Remote ports not supported... */ } - else { - rl = erts_remove_link(&ERTS_P_LINKS(rp), BIF_P->common.id); - if (rl != NULL) - erts_destroy_link(rl); - - if (IS_TRACED_FL(rp, F_TRACE_PROCS) && rl != NULL) { - erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS); - cp_locks &= ~ERTS_PROC_LOCK_STATUS; - trace_proc(BIF_P, (ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK), - rp, am_getting_unlinked, BIF_P->common.id); - } - if (rp != BIF_P) - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - } - - erts_proc_unlock(BIF_P, cp_locks & ~ERTS_PROC_LOCK_MAIN); - - BIF_RET(am_true); - - handle_pending_exit: - erts_handle_pending_exit(BIF_P, (ERTS_PROC_LOCK_MAIN - | ERTS_PROC_LOCK_LINK - | ERTS_PROC_LOCK_STATUS)); - ASSERT(ERTS_PROC_IS_EXITING(BIF_P)); - erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS); - ERTS_BIF_EXITED(BIF_P); + BIF_ERROR(BIF_P, BADARG); } BIF_RETTYPE hibernate_3(BIF_ALIST_3) @@ -1493,22 +1210,69 @@ BIF_RETTYPE raise_3(BIF_ALIST_3) return am_badarg; } +static BIF_RETTYPE +erts_internal_await_exit_trap(BIF_ALIST_0) +{ + /* + * We have sent ourselves an exit signal which will + * terminate ourselves. Handle all signals until + * terminated in order to ensure that signal order + * is preserved. Yield if necessary. + */ + erts_aint32_t state; + int reds = ERTS_BIF_REDS_LEFT(BIF_P); + (void) erts_proc_sig_handle_incoming(BIF_P, &state, &reds, + reds, !0); + BUMP_REDS(BIF_P, reds); + if (state & ERTS_PSFLG_EXITING) + ERTS_BIF_EXITED(BIF_P); + + ERTS_BIF_YIELD0(&await_exit_trap, BIF_P); +} + /**********************************************************************/ -/* send an exit message to another process (if trapping exits) or - exit the other process */ +/* send an exit signal to another process */ -BIF_RETTYPE exit_2(BIF_ALIST_2) +static BIF_RETTYPE send_exit_signal_bif(Process *c_p, Eterm id, Eterm reason, int exit2) { - Process *rp; + BIF_RETTYPE ret_val; - /* - * If the first argument is not a pid, or a local port it is an error. - */ + /* + * 'id' not a process id, nor a local port id is a 'badarg' error. + */ - if (is_internal_port(BIF_ARG_1)) { + if (is_internal_pid(id)) { + /* + * Preserve the very old and *very strange* behaviour + * of erlang:exit/2... + * + * - terminate ourselves even though exit reason + * is normal (unless we trap exit) + * - terminate ourselves before exit/2 return + */ + int exit2_suicide = (exit2 + && c_p->common.id == id + && (reason == am_kill + || !(c_p->flags & F_TRAP_EXIT))); + erts_proc_sig_send_exit(c_p, c_p->common.id, id, + reason, NIL, exit2_suicide); + if (!exit2_suicide) + ERTS_BIF_PREP_RET(ret_val, am_true); + else { + erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ); + erts_proc_sig_fetch(c_p); + erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ); + ERTS_BIF_PREP_TRAP0(ret_val, &await_exit_trap, c_p); + } + } + else if (is_internal_port(id)) { Eterm ref, *refp; Uint32 invalid_flags; Port *prt; + ErtsPortOpResult res = ERTS_PORT_OP_DONE; +#ifdef DEBUG + ref = NIL; +#endif if (erts_port_synchronous_ops) { refp = &ref; @@ -1519,108 +1283,75 @@ BIF_RETTYPE exit_2(BIF_ALIST_2) invalid_flags = ERTS_PORT_SFLGS_INVALID_LOOKUP; } - prt = erts_port_lookup(BIF_ARG_1, invalid_flags); - - if (prt) { - ErtsPortOpResult res; - -#ifdef DEBUG - ref = NIL; -#endif - - res = erts_port_exit(BIF_P, 0, prt, BIF_P->common.id, BIF_ARG_2, refp); - - ERTS_BIF_CHK_EXITED(BIF_P); - - if (refp && res == ERTS_PORT_OP_SCHEDULED) { - ASSERT(is_internal_ordinary_ref(ref)); - BIF_TRAP3(await_port_send_result_trap, BIF_P, ref, am_true, am_true); - } - - } - - BIF_RET(am_true); + prt = erts_port_lookup(id, invalid_flags); + if (prt) + res = erts_port_exit(c_p, 0, prt, c_p->common.id, reason, refp); + + if (!refp || res != ERTS_PORT_OP_SCHEDULED) + ERTS_BIF_PREP_RET(ret_val, am_true); + else { + ASSERT(is_internal_ordinary_ref(ref)); + ERTS_BIF_PREP_TRAP3(ret_val, await_port_send_result_trap, + c_p, ref, am_true, am_true); + } } - else if(is_external_port(BIF_ARG_1) - && external_port_dist_entry(BIF_ARG_1) == erts_this_dist_entry) - BIF_RET(am_true); - - /* - * If it is a remote pid, send a signal to the remote node. - */ - - if (is_external_pid(BIF_ARG_1)) { - int code; - ErtsDSigData dsd; - DistEntry *dep; - - dep = external_pid_dist_entry(BIF_ARG_1); - ERTS_ASSERT(dep); - if(dep == erts_this_dist_entry) - BIF_RET(am_true); - - code = erts_dsig_prepare(&dsd, dep, BIF_P, ERTS_PROC_LOCK_MAIN, - ERTS_DSP_NO_LOCK, 0, 1); - switch (code) { - case ERTS_DSIG_PREP_NOT_ALIVE: - case ERTS_DSIG_PREP_NOT_CONNECTED: - BIF_RET(am_true); - case ERTS_DSIG_PREP_PENDING: - case ERTS_DSIG_PREP_CONNECTED: - code = erts_dsig_send_exit2(&dsd, BIF_P->common.id, BIF_ARG_1, BIF_ARG_2); - if (code == ERTS_DSIG_SEND_YIELD) - ERTS_BIF_YIELD_RETURN(BIF_P, am_true); - BIF_RET(am_true); - default: - ERTS_ASSERT(! "Invalid dsig prepare result"); - } + else if (is_external_pid(id)) { + DistEntry *dep = external_pid_dist_entry(id); + if (dep == erts_this_dist_entry) + ERTS_BIF_PREP_RET(ret_val, am_true); /* Old incarnation of this node... */ + else { + int code; + ErtsDSigData dsd; + + code = erts_dsig_prepare(&dsd, dep, c_p, ERTS_PROC_LOCK_MAIN, + ERTS_DSP_NO_LOCK, 0, 1); + switch (code) { + case ERTS_DSIG_PREP_NOT_ALIVE: + case ERTS_DSIG_PREP_NOT_CONNECTED: + ERTS_BIF_PREP_RET(ret_val, am_true); + break; + case ERTS_DSIG_PREP_PENDING: + case ERTS_DSIG_PREP_CONNECTED: + code = erts_dsig_send_exit2(&dsd, c_p->common.id, id, reason); + if (code == ERTS_DSIG_SEND_YIELD) + ERTS_BIF_PREP_YIELD_RETURN(ret_val, c_p, am_true); + else + ERTS_BIF_PREP_RET(ret_val, am_true); + break; + default: + ASSERT(! "Invalid dsig prepare result"); + ERTS_BIF_PREP_ERROR(ret_val, c_p, EXC_INTERNAL_ERROR); + break; + } + } } - else if (is_not_internal_pid(BIF_ARG_1)) { - BIF_ERROR(BIF_P, BADARG); + else if (is_external_port(id)) { + DistEntry *dep = external_port_dist_entry(id); + if(dep == erts_this_dist_entry) + ERTS_BIF_PREP_RET(ret_val, am_true); /* Old incarnation of this node... */ + else + ERTS_BIF_PREP_ERROR(ret_val, c_p, BADARG); } else { - /* - * The pid is internal. Verify that it refers to an existing process. - */ - ErtsProcLocks rp_locks; - - if (BIF_ARG_1 == BIF_P->common.id) { - rp_locks = ERTS_PROC_LOCKS_ALL; - rp = BIF_P; - erts_proc_lock(rp, ERTS_PROC_LOCKS_ALL_MINOR); - } - else { - rp_locks = ERTS_PROC_LOCKS_XSIG_SEND; - rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN, - BIF_ARG_1, rp_locks); - if (!rp) { - BIF_RET(am_true); - } - } + /* Not an id of a process or a port... */ - /* - * Send an exit signal. - */ - erts_send_exit_signal(BIF_P, - BIF_P->common.id, - rp, - &rp_locks, - BIF_ARG_2, - NIL, - NULL, - BIF_P == rp ? ERTS_XSIG_FLG_NO_IGN_NORMAL : 0); - if (rp == BIF_P) - rp_locks &= ~ERTS_PROC_LOCK_MAIN; - if (rp_locks) - erts_proc_unlock(rp, rp_locks); - /* - * We may have exited ourselves and may have to take action. - */ - ERTS_BIF_CHK_EXITED(BIF_P); - BIF_RET(am_true); + ERTS_BIF_PREP_ERROR(ret_val, c_p, BADARG); } + + return ret_val; +} + +BIF_RETTYPE exit_2(BIF_ALIST_2) +{ + return send_exit_signal_bif(BIF_P, BIF_ARG_1, BIF_ARG_2, !0); } +BIF_RETTYPE exit_signal_2(BIF_ALIST_2) +{ + return send_exit_signal_bif(BIF_P, BIF_ARG_1, BIF_ARG_2, 0); +} + + /**********************************************************************/ /* this sets some process info- trapping exits or the error handler */ @@ -1709,36 +1440,13 @@ BIF_RETTYPE process_flag_2(BIF_ALIST_2) BIF_RET(old_value); } else if (BIF_ARG_1 == am_trap_exit) { - erts_aint32_t state; - Uint trap_exit; - if (BIF_ARG_2 == am_true) { - trap_exit = 1; - } else if (BIF_ARG_2 == am_false) { - trap_exit = 0; - } else { - goto error; - } - /* - * NOTE: It is important that we check for pending exit signals - * and handle them before returning if trap_exit is set to - * true. For more info, see implementation of - * erts_send_exit_signal(). - */ - erts_proc_lock(BIF_P, ERTS_PROC_LOCKS_XSIG_SEND); - if (trap_exit) - state = erts_atomic32_read_bor_mb(&BIF_P->state, - ERTS_PSFLG_TRAP_EXIT); + old_value = (BIF_P->flags & F_TRAP_EXIT) ? am_true : am_false; + if (BIF_ARG_2 == am_true) + BIF_P->flags |= F_TRAP_EXIT; + else if (BIF_ARG_2 == am_false) + BIF_P->flags &= ~F_TRAP_EXIT; else - state = erts_atomic32_read_band_mb(&BIF_P->state, - ~ERTS_PSFLG_TRAP_EXIT); - erts_proc_unlock(BIF_P, ERTS_PROC_LOCKS_XSIG_SEND); - - if (state & ERTS_PSFLG_PENDING_EXIT) { - erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN); - ERTS_BIF_EXITED(BIF_P); - } - - old_value = (state & ERTS_PSFLG_TRAP_EXIT) ? am_true : am_false; + goto error; BIF_RET(old_value); } else if (BIF_ARG_1 == am_scheduler) { @@ -2142,9 +1850,6 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm *refp, ErtsSendContext *ctx) } switch (erts_port_command(p, ps_flags, pt, msg, refp)) { - case ERTS_PORT_OP_CALLER_EXIT: - /* We are exiting... */ - return SEND_USER_ERROR; case ERTS_PORT_OP_BUSY: /* Nothing has been sent */ if (ctx->suspend) @@ -3101,7 +2806,7 @@ BIF_RETTYPE list_to_integer_2(BIF_ALIST_2) static int do_float_to_charbuf(Process *p, Eterm efloat, Eterm list, char *fbuf, int sizeof_fbuf) { - const static int arity_two = make_arityval(2); + Eterm arity_two = make_arityval(2); int decimals = SYS_DEFAULT_FLOAT_DECIMALS; int compact = 0; enum fmt_type_ { @@ -3465,7 +3170,7 @@ BIF_RETTYPE binary_to_float_1(BIF_ALIST_1) if (bit_offs) erts_copy_bits(bytes, bit_offs, 1, buf, 0, 1, size*8); else - memcpy(buf, bytes, size); + sys_memcpy(buf, bytes, size); buf[size] = '\0'; @@ -4199,10 +3904,10 @@ BIF_RETTYPE list_to_port_1(BIF_ALIST_1) buf[i] = '\0'; /* null terminal */ cp = &buf[0]; - if (strncmp("#Port<", cp, 6) != 0) + if (sys_strncmp("#Port<", cp, 6) != 0) goto bad; - cp += 6; /* strlen("#Port<") */ + cp += 6; /* sys_strlen("#Port<") */ if (sscanf(cp, "%u.%u>", (unsigned int*)&n, (unsigned int*)&p) < 2) goto bad; @@ -4373,14 +4078,88 @@ BIF_RETTYPE group_leader_0(BIF_ALIST_0) } /**********************************************************************/ -/* arg1 == leader, arg2 == new member */ +/* set group leader */ -BIF_RETTYPE group_leader_2(BIF_ALIST_2) +int +erts_set_group_leader(Process *proc, Eterm new_gl) { - Process* new_member; + + erts_aint32_t state; - if (is_not_pid(BIF_ARG_1)) { - BIF_ERROR(BIF_P, BADARG); + ASSERT(is_pid(new_gl)); + + state = erts_atomic32_read_nob(&proc->state); + + if (state & ERTS_PSFLG_EXITING) + return 0; + + ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN & erts_proc_lc_my_proc_locks(proc)); + + if (!(state & ERTS_PSFLG_DIRTY_RUNNING)) + proc->group_leader = STORE_NC_IN_PROC(proc, new_gl); + else { + ErlHeapFragment *bp; + Eterm *hp; + /* + * Currently executing on a dirty scheduler, + * so we are not allowed to write to its heap. + * Store group leader pid in heap fragment. + */ + bp = new_message_buffer(NC_HEAP_SIZE(new_gl)); + hp = bp->mem; + proc->group_leader = STORE_NC(&hp, + &proc->off_heap, + new_gl); + bp->next = proc->mbuf; + proc->mbuf = bp; + proc->mbuf_sz += bp->used_size; + } + + return !0; +} + +BIF_RETTYPE erts_internal_group_leader_3(BIF_ALIST_3) +{ + if (is_not_pid(BIF_ARG_1)) + BIF_ERROR(BIF_P, BADARG); + if (is_not_internal_pid(BIF_ARG_2)) + BIF_ERROR(BIF_P, BADARG); + if (is_not_internal_ref(BIF_ARG_3)) + BIF_ERROR(BIF_P, BADARG); + + erts_proc_sig_send_group_leader(BIF_P, + BIF_ARG_2, + BIF_ARG_1, + BIF_ARG_3); + BIF_RET(am_ok); +} + +BIF_RETTYPE erts_internal_group_leader_2(BIF_ALIST_2) +{ + if (is_not_pid(BIF_ARG_1)) + BIF_RET(am_badarg); + + if (is_internal_pid(BIF_ARG_2)) { + Process *rp; + int res; + + if (BIF_ARG_2 == BIF_P->common.id) + rp = BIF_P; + else { + rp = erts_try_lock_sig_free_proc(BIF_ARG_2, + ERTS_PROC_LOCK_MAIN); + if (!rp) + BIF_RET(am_badarg); + if (rp == ERTS_PROC_LOCK_BUSY) + BIF_RET(am_false); + } + + res = erts_set_group_leader(rp, BIF_ARG_1); + + if (rp != BIF_P) + erts_proc_unlock(rp, ERTS_PROC_LOCK_MAIN); + + BIF_RET(res ? am_true : am_badarg); } if (is_external_pid(BIF_ARG_2)) { @@ -4408,74 +4187,8 @@ BIF_RETTYPE group_leader_2(BIF_ALIST_2) ERTS_ASSERT(! "Invalid dsig prepare result"); } } - else if (is_internal_pid(BIF_ARG_2)) { - int await_x; - ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS; - new_member = erts_pid2proc_nropt(BIF_P, ERTS_PROC_LOCK_MAIN, - BIF_ARG_2, locks); - if (!new_member) - BIF_ERROR(BIF_P, BADARG); - - if (new_member == ERTS_PROC_LOCK_BUSY) - ERTS_BIF_YIELD2(bif_export[BIF_group_leader_2], BIF_P, - BIF_ARG_1, BIF_ARG_2); - - await_x = (new_member != BIF_P - && ERTS_PROC_PENDING_EXIT(new_member)); - if (!await_x) { - if (is_immed(BIF_ARG_1)) - new_member->group_leader = BIF_ARG_1; - else { - locks &= ~ERTS_PROC_LOCK_STATUS; - erts_proc_unlock(new_member, ERTS_PROC_LOCK_STATUS); - if (new_member == BIF_P - || !(erts_atomic32_read_nob(&new_member->state) - & ERTS_PSFLG_DIRTY_RUNNING)) { - new_member->group_leader = STORE_NC_IN_PROC(new_member, - BIF_ARG_1); - } - else { - ErlHeapFragment *bp; - Eterm *hp; - /* - * Other process executing on a dirty scheduler, - * so we are not allowed to write to its heap. - * Store in heap fragment. - */ - - bp = new_message_buffer(NC_HEAP_SIZE(BIF_ARG_1)); - hp = bp->mem; - new_member->group_leader = STORE_NC(&hp, - &new_member->off_heap, - BIF_ARG_1); - bp->next = new_member->mbuf; - new_member->mbuf = bp; - new_member->mbuf_sz += bp->used_size; - } - } - } - - if (new_member == BIF_P) - locks &= ~ERTS_PROC_LOCK_MAIN; - if (locks) - erts_proc_unlock(new_member, locks); - - if (await_x) { - /* Wait for new_member to terminate; then badarg */ - Eterm args[2] = {BIF_ARG_1, BIF_ARG_2}; - ERTS_BIF_AWAIT_X_APPLY_TRAP(BIF_P, - BIF_ARG_2, - am_erlang, - am_group_leader, - args, - 2); - } - BIF_RET(am_true); - } - else { - BIF_ERROR(BIF_P, BADARG); - } + BIF_RET(am_badarg); } BIF_RETTYPE system_flag_2(BIF_ALIST_2) @@ -4662,7 +4375,6 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) BIF_RET(ret); } else if (BIF_ARG_1 == make_small(1)) { int i, max; - ErtsMessage* mp; erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN); erts_thr_progress_block(); @@ -4677,16 +4389,8 @@ BIF_RETTYPE system_flag_2(BIF_ALIST_2) #endif p->seq_trace_clock = 0; p->seq_trace_lastcnt = 0; - ERTS_MSGQ_MV_INQ2PRIVQ(p); - mp = p->msg.first; - while(mp != NULL) { -#ifdef USE_VM_PROBES - ERL_MESSAGE_TOKEN(mp) = (ERL_MESSAGE_DT_UTAG(mp) != NIL) ? am_have_dt_utag : NIL; -#else - ERL_MESSAGE_TOKEN(mp) = NIL; -#endif - mp = mp->next; - } + + erts_proc_sig_clear_seq_trace_tokens(p); } } @@ -4949,85 +4653,6 @@ static BIF_RETTYPE bif_return_trap(BIF_ALIST_2) BIF_RET(res); } -/* - * NOTE: The erts_bif_prep_await_proc_exit_*() functions are - * tightly coupled with the implementation of erlang:await_proc_exit/3. - * The erts_bif_prep_await_proc_exit_*() functions can safely call - * skip_current_msgq() since they know that erlang:await_proc_exit/3 - * unconditionally will do a monitor and then unconditionally will - * wait for the corresponding 'DOWN' message in a receive, and no other - * receive is done before this receive. This optimization removes an - * unnecessary scan of the currently existing message queue (which - * can be large). If the erlang:await_proc_exit/3 implementation - * is changed so that the above isn't true, nasty bugs in later - * receives, etc, may appear. - */ - -static ERTS_INLINE int -skip_current_msgq(Process *c_p) -{ - int res; -#if defined(ERTS_ENABLE_LOCK_CHECK) - erts_proc_lc_chk_only_proc_main(c_p); -#endif - - erts_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); - if (ERTS_PROC_PENDING_EXIT(c_p)) { - KILL_CATCHES(c_p); - c_p->freason = EXC_EXIT; - res = 0; - } - else { - ERTS_MSGQ_MV_INQ2PRIVQ(c_p); - c_p->msg.save = c_p->msg.last; - res = 1; - } - erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); - return res; -} - -void -erts_bif_prep_await_proc_exit_data_trap(Process *c_p, Eterm pid, Eterm ret) -{ - if (skip_current_msgq(c_p)) { - ERTS_BIF_PREP_TRAP3_NO_RET(await_proc_exit_trap, c_p, pid, am_data, ret); - } -} - -void -erts_bif_prep_await_proc_exit_reason_trap(Process *c_p, Eterm pid) -{ - if (skip_current_msgq(c_p)) { - ERTS_BIF_PREP_TRAP3_NO_RET(await_proc_exit_trap, c_p, - pid, am_reason, am_undefined); - } -} - -void -erts_bif_prep_await_proc_exit_apply_trap(Process *c_p, - Eterm pid, - Eterm module, - Eterm function, - Eterm args[], - int nargs) -{ - ASSERT(is_atom(module) && is_atom(function)); - if (skip_current_msgq(c_p)) { - Eterm term; - Eterm *hp; - int i; - - hp = HAlloc(c_p, 4+2*nargs); - term = NIL; - for (i = nargs-1; i >= 0; i--) { - term = CONS(hp, args[i], term); - hp += 2; - } - term = TUPLE3(hp, module, function, term); - ERTS_BIF_PREP_TRAP3_NO_RET(await_proc_exit_trap, c_p, pid, am_apply, term); - } -} - Export bif_return_trap_export; void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a, @@ -5063,6 +4688,9 @@ void erts_init_bif(void) am_erts_internal, am_dsend_continue_trap, 1, dsend_continue_trap_1); + erts_init_trap_export(&await_exit_trap, am_erts_internal, + am_await_exit, 0, erts_internal_await_exit_trap); + flush_monitor_messages_trap = erts_export_put(am_erts_internal, am_flush_monitor_messages, 3); @@ -5077,7 +4705,6 @@ void erts_init_bif(void) erts_format_cpu_topology_trap = erts_export_put(am_erlang, am_format_cpu_topology, 1); - await_proc_exit_trap = erts_export_put(am_erlang,am_await_proc_exit,3); await_port_send_result_trap = erts_export_put(am_erts_internal, am_await_port_send_result, 3); system_flag_scheduler_wall_time_trap diff --git a/erts/emulator/beam/bif.h b/erts/emulator/beam/bif.h index a2bc883dbe..a33421d762 100644 --- a/erts/emulator/beam/bif.h +++ b/erts/emulator/beam/bif.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2016. All Rights Reserved. + * Copyright Ericsson AB 1996-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -306,7 +306,7 @@ do { \ (Proc)->freason = TRAP; \ } while (0) -#define BIF_TRAP0(p, Trap_) do { \ +#define BIF_TRAP0(Trap_, p) do { \ (p)->arity = 0; \ (p)->i = (BeamInstr*) ((Trap_)->addressv[erts_active_code_ix()]); \ (p)->freason = TRAP; \ @@ -404,7 +404,7 @@ do { \ #define ERTS_BIF_YIELD0(TRP, P) \ do { \ ERTS_VBUMP_ALL_REDS((P)); \ - BIF_TRAP0((TRP), (P)); \ + BIF_TRAP0((TRP), (P)); \ } while (0) #define ERTS_BIF_YIELD1(TRP, P, A0) \ @@ -428,7 +428,7 @@ do { \ #define ERTS_BIF_EXITED(PROC) \ do { \ KILL_CATCHES((PROC)); \ - BIF_ERROR((PROC), EXC_EXIT); \ + BIF_ERROR((PROC), EXTAG_EXIT); \ } while (0) #define ERTS_BIF_CHK_EXITED(PROC) \ @@ -437,67 +437,6 @@ do { \ ERTS_BIF_EXITED((PROC)); \ } while (0) -/* - * The ERTS_BIF_*_AWAIT_X_*_TRAP makros either exits the caller, or - * sets up a trap to erlang:await_proc_exit/3. - * - * The caller is acquired to hold the 'main' lock on C_P. No other locks - * are allowed to be held. - */ - -#define ERTS_BIF_PREP_AWAIT_X_DATA_TRAP(RET, C_P, PID, DATA) \ -do { \ - erts_bif_prep_await_proc_exit_data_trap((C_P), (PID), (DATA)); \ - (RET) = THE_NON_VALUE; \ -} while (0) - -#define ERTS_BIF_PREP_AWAIT_X_REASON_TRAP(RET, C_P, PID) \ -do { \ - erts_bif_prep_await_proc_exit_reason_trap((C_P), (PID)); \ - (RET) = THE_NON_VALUE; \ -} while (0) - -#define ERTS_BIF_PREP_AWAIT_X_APPLY_TRAP(RET, C_P, PID, M, F, A, AN) \ -do { \ - erts_bif_prep_await_proc_exit_apply_trap((C_P), (PID), \ - (M), (F), (A), (AN)); \ - (RET) = THE_NON_VALUE; \ -} while (0) - -#define ERTS_BIF_AWAIT_X_DATA_TRAP(C_P, PID, DATA) \ -do { \ - erts_bif_prep_await_proc_exit_data_trap((C_P), (PID), (DATA)); \ - return THE_NON_VALUE; \ -} while (0) - -#define ERTS_BIF_AWAIT_X_REASON_TRAP(C_P, PID) \ -do { \ - erts_bif_prep_await_proc_exit_reason_trap((C_P), (PID)); \ - return THE_NON_VALUE; \ -} while (0) - -#define ERTS_BIF_AWAIT_X_APPLY_TRAP(C_P, PID, M, F, A, AN) \ -do { \ - erts_bif_prep_await_proc_exit_apply_trap((C_P), (PID), \ - (M), (F), (A), (AN)); \ - return THE_NON_VALUE; \ -} while (0) - -void -erts_bif_prep_await_proc_exit_data_trap(Process *c_p, - Eterm pid, - Eterm data); -void -erts_bif_prep_await_proc_exit_reason_trap(Process *c_p, - Eterm pid); -void -erts_bif_prep_await_proc_exit_apply_trap(Process *c_p, - Eterm pid, - Eterm module, - Eterm function, - Eterm args[], - int nargs); - int erts_call_dirty_bif(ErtsSchedulerData *esdp, Process *c_p, BeamInstr *I, Eterm *reg); diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 0d1166f6ed..687fd39d58 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2017. All Rights Reserved. +# Copyright Ericsson AB 1996-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -62,6 +62,7 @@ bif erlang:erase/0 bif erlang:erase/1 bif erlang:exit/1 bif erlang:exit/2 +bif erlang:exit_signal/2 bif erlang:external_size/1 bif erlang:external_size/2 gcbif erlang:float/1 @@ -73,7 +74,8 @@ bif erlang:get/0 bif erlang:get/1 bif erlang:get_keys/1 bif erlang:group_leader/0 -bif erlang:group_leader/2 +bif erts_internal:group_leader/2 +bif erts_internal:group_leader/3 bif erlang:halt/2 bif erlang:phash/2 bif erlang:phash2/1 @@ -187,6 +189,8 @@ bif erts_internal:release_literal_area_switch/0 bif erts_internal:scheduler_wall_time/1 +bif erts_internal:dirty_process_handle_signals/1 + # inet_db support bif erlang:port_set_data/2 bif erlang:port_get_data/1 @@ -262,6 +266,7 @@ bif erlang:demonitor/1 bif erlang:demonitor/2 bif erlang:is_process_alive/1 +bif erts_internal:is_process_alive/2 bif erlang:error/1 error_1 bif erlang:error/2 error_2 @@ -435,13 +440,6 @@ bif erts_debug:dirty_io/2 bif erts_debug:dirty/3 # -# Monitor testing bif's... -# -bif erts_debug:dump_monitors/1 -bif erts_debug:dump_links/1 - - -# # Lock counter bif's # bif erts_debug:lcnt_control/2 @@ -690,6 +688,7 @@ bif erlang:iolist_to_iovec/1 # New in 21.0 # +bif erts_internal:get_dflags/0 bif erts_internal:new_connection/1 bif erts_internal:abort_connection/2 bif erts_internal:map_next/3 diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c index b9b70cd8ef..4dabac3512 100644 --- a/erts/emulator/beam/break.c +++ b/erts/emulator/beam/break.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2017. All Rights Reserved. + * Copyright Ericsson AB 1996-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,6 +39,7 @@ #include "erl_instrument.h" #include "erl_hl_timer.h" #include "erl_thr_progress.h" +#include "erl_proc_sig_queue.h" /* Forward declarations -- should really appear somewhere else */ static void process_killer(void); @@ -107,36 +108,11 @@ process_killer(void) if ((j = sys_get_key(0)) <= 0) erts_exit(0, ""); switch(j) { - case 'k': { - ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND; - erts_aint32_t state; - erts_proc_inc_refc(rp); - erts_proc_lock(rp, rp_locks); - state = erts_atomic32_read_acqb(&rp->state); - if (state & (ERTS_PSFLG_FREE - | ERTS_PSFLG_EXITING - | ERTS_PSFLG_ACTIVE - | ERTS_PSFLG_ACTIVE_SYS - | ERTS_PSFLG_IN_RUNQ - | ERTS_PSFLG_RUNNING - | ERTS_PSFLG_RUNNING_SYS - | ERTS_PSFLG_DIRTY_RUNNING - | ERTS_PSFLG_DIRTY_RUNNING_SYS)) { - erts_printf("Can only kill WAITING processes this way\n"); - } - else { - (void) erts_send_exit_signal(NULL, - NIL, - rp, - &rp_locks, - am_kill, - NIL, - NULL, - 0); - } - erts_proc_unlock(rp, rp_locks); - erts_proc_dec_refc(rp); - } + case 'k': + /* Send a 'kill' exit signal from init process */ + erts_proc_sig_send_exit(NULL, erts_init_process_id, + rp->common.id, am_kill, NIL, + 0); case 'n': br = 1; break; case 'r': return; default: return; @@ -161,47 +137,64 @@ static void doit_print_link(ErtsLink *lnk, void *vpcontext) if (pcontext->is_first) { pcontext->is_first = 0; - erts_print(to, to_arg, "%T", lnk->pid); + erts_print(to, to_arg, "%T", lnk->other.item); } else { - erts_print(to, to_arg, ", %T", lnk->pid); + erts_print(to, to_arg, ", %T", lnk->other.item); } } static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext) { + ErtsMonitorData *mdp; PrintMonitorContext *pcontext = vpcontext; fmtfn_t to = pcontext->to; void *to_arg = pcontext->to_arg; char *prefix = ", "; - if (pcontext->is_first) { - pcontext->is_first = 0; - prefix = ""; - } - + mdp = erts_monitor_to_data(mon); switch (mon->type) { - case MON_ORIGIN: - if (is_atom(mon->u.pid)) { /* dist by name */ - ASSERT(is_node_name_atom(mon->u.pid)); - erts_print(to, to_arg, "%s{to,{%T,%T},%T}", prefix, mon->name, - mon->u.pid, mon->ref); - } else if (is_atom(mon->name)){ /* local by name */ - erts_print(to, to_arg, "%s{to,{%T,%T},%T}", prefix, mon->name, - erts_this_dist_entry->sysname, mon->ref); - } else { /* local and distributed by pid */ - erts_print(to, to_arg, "%s{to,%T,%T}", prefix, mon->u.pid, mon->ref); - } - break; - case MON_TARGET: - erts_print(to, to_arg, "%s{from,%T,%T}", prefix, mon->u.pid, mon->ref); - break; - case MON_NIF_TARGET: { - ErtsResource* rsrc = mon->u.resource; - erts_print(to, to_arg, "%s{from,{%T,%T},%T}", prefix, rsrc->type->module, - rsrc->type->name, mon->ref); - break; - } + case ERTS_MON_TYPE_PROC: + case ERTS_MON_TYPE_PORT: + case ERTS_MON_TYPE_TIME_OFFSET: + case ERTS_MON_TYPE_DIST_PROC: + case ERTS_MON_TYPE_RESOURCE: + case ERTS_MON_TYPE_NODE: + + if (pcontext->is_first) { + pcontext->is_first = 0; + prefix = ""; + } + + if (erts_monitor_is_target(mon)) { + if (mon->type != ERTS_MON_TYPE_RESOURCE) + erts_print(to, to_arg, "%s{from,%T,%T}", prefix, mon->other.item, mdp->ref); + else { + ErtsResource* rsrc = mon->other.ptr; + erts_print(to, to_arg, "%s{from,{%T,%T},%T}", prefix, rsrc->type->module, + rsrc->type->name, mdp->ref); + } + } + else { + if (!(mon->flags & ERTS_ML_FLG_NAME)) + erts_print(to, to_arg, "%s{to,%T,%T}", prefix, mon->other.item, mdp->ref); + else { + ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp; + Eterm node; + if (mdep->dist) + node = mdep->dist->nodename; + else + node = erts_this_dist_entry->sysname; + erts_print(to, to_arg, "%s{to,{%T,%T},%T}", prefix, mdep->u.name, + node, mdp->ref); + } + } + + break; + + default: + /* ignore other monitors... */ + break; } } @@ -209,7 +202,6 @@ static void doit_print_monitor(ErtsMonitor *mon, void *vpcontext) void print_process_info(fmtfn_t to, void *to_arg, Process *p) { - time_t approx_started; int garbing = 0; int running = 0; struct saved_calls *scb; @@ -258,17 +250,22 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p) } erts_print(to, to_arg, "Spawned by: %T\n", p->parent); - approx_started = (time_t) p->approx_started; - erts_print(to, to_arg, "Started: %s", ctime(&approx_started)); - ERTS_MSGQ_MV_INQ2PRIVQ(p); - erts_print(to, to_arg, "Message queue length: %d\n", p->msg.len); + + erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ); + erts_proc_sig_fetch(p); + erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ); + erts_print(to, to_arg, "Message queue length: %d\n", p->sig_qs.len); /* display the message queue only if there is anything in it */ - if (!ERTS_IS_CRASH_DUMPING && p->msg.first != NULL && !garbing) { - ErtsMessage* mp; + if (!ERTS_IS_CRASH_DUMPING && p->sig_qs.first != NULL && !garbing) { erts_print(to, to_arg, "Message queue: ["); - for (mp = p->msg.first; mp; mp = mp->next) - erts_print(to, to_arg, mp->next ? "%T," : "%T", ERL_MESSAGE_TERM(mp)); + ERTS_FOREACH_SIG_PRIVQS( + p, mp, + { + if (ERTS_SIG_IS_NON_MSG((ErtsSignal *) mp)) + erts_print(to, to_arg, mp->next ? "%T," : "%T", + ERL_MESSAGE_TERM(mp)); + }); erts_print(to, to_arg, "]\n"); } @@ -309,11 +306,12 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p) } /* display the links only if there are any*/ - if (ERTS_P_LINKS(p) || ERTS_P_MONITORS(p)) { + if (ERTS_P_LINKS(p) || ERTS_P_MONITORS(p) || ERTS_P_LT_MONITORS(p)) { PrintMonitorContext context = {1, to, to_arg}; erts_print(to, to_arg,"Link list: ["); - erts_doforall_links(ERTS_P_LINKS(p), &doit_print_link, &context); - erts_doforall_monitors(ERTS_P_MONITORS(p), &doit_print_monitor, &context); + erts_link_tree_foreach(ERTS_P_LINKS(p), doit_print_link, &context); + erts_monitor_tree_foreach(ERTS_P_MONITORS(p), doit_print_monitor, &context); + erts_monitor_list_foreach(ERTS_P_LT_MONITORS(p), doit_print_monitor, &context); erts_print(to, to_arg,"]\n"); } @@ -509,7 +507,7 @@ do_break(void) /* check if we're in console mode and, if so, halt immediately if break is called */ mode = erts_read_env("ERL_CONSOLE_MODE"); - if (mode && strcmp(mode, "window") != 0) + if (mode && sys_strcmp(mode, "window") != 0) erts_exit(0, ""); erts_free_read_env(mode); #endif /* __WIN32__ */ diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c index 30390cdb5e..f203d85ca9 100644 --- a/erts/emulator/beam/dist.c +++ b/erts/emulator/beam/dist.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2017. All Rights Reserved. + * Copyright Ericsson AB 1996-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,6 +45,7 @@ #include "erl_binary.h" #include "erl_thr_progress.h" #include "dtrace-wrapper.h" +#include "erl_proc_sig_queue.h" #define DIST_CTL_DEFAULT_SIZE 64 @@ -109,7 +110,6 @@ int erts_dist_buf_busy_limit; /* distribution trap functions */ Export* dmonitor_node_trap = NULL; -Export* dmonitor_p_trap = NULL; /* local variables */ static Export *dist_ctrl_put_data_trap; @@ -184,6 +184,161 @@ get_suspended_on_de(DistEntry *dep, erts_aint32_t unset_qflgs) } } +#define ERTS_MON_LNK_FIRE_LIMIT 100 + +static void monitor_connection_down(ErtsMonitor *mon, void *unused) +{ + if (erts_monitor_is_origin(mon)) + erts_proc_sig_send_demonitor(mon); + else + erts_proc_sig_send_monitor_down(mon, am_noconnection); +} + +static void link_connection_down(ErtsLink *lnk, void *vdist) +{ + erts_proc_sig_send_link_exit(NULL, THE_NON_VALUE, lnk, + am_noconnection, NIL); +} + +typedef enum { + ERTS_CML_CLEANUP_STATE_LINKS, + ERTS_CML_CLEANUP_STATE_MONITORS, + ERTS_CML_CLEANUP_STATE_ONAME_MONITORS, + ERTS_CML_CLEANUP_STATE_NODE_MONITORS +} ErtsConMonLnkCleaupState; + +typedef struct { + ErtsConMonLnkCleaupState state; + ErtsMonLnkDist *dist; + void *yield_state; + int trigger_node_monitors; + Eterm nodename; + Eterm visability; + Eterm reason; + ErlOffHeap oh; + Eterm heap[1]; +} ErtsConMonLnkCleanup; + +static void +con_monitor_link_cleanup(void *vcmlcp) +{ + ErtsConMonLnkCleanup *cmlcp = vcmlcp; + ErtsMonLnkDist *dist = cmlcp->dist; + ErtsSchedulerData *esdp; + int yield; + + switch (cmlcp->state) { + case ERTS_CML_CLEANUP_STATE_LINKS: + yield = erts_link_list_foreach_delete_yielding(&dist->links, + link_connection_down, + NULL, &cmlcp->yield_state, + ERTS_MON_LNK_FIRE_LIMIT); + if (yield) + break; + + ASSERT(!cmlcp->yield_state); + cmlcp->state = ERTS_CML_CLEANUP_STATE_MONITORS; + case ERTS_CML_CLEANUP_STATE_MONITORS: + yield = erts_monitor_list_foreach_delete_yielding(&dist->monitors, + monitor_connection_down, + NULL, &cmlcp->yield_state, + ERTS_MON_LNK_FIRE_LIMIT); + if (yield) + break; + + ASSERT(!cmlcp->yield_state); + cmlcp->state = ERTS_CML_CLEANUP_STATE_ONAME_MONITORS; + case ERTS_CML_CLEANUP_STATE_ONAME_MONITORS: + yield = erts_monitor_tree_foreach_delete_yielding(&dist->orig_name_monitors, + monitor_connection_down, + NULL, &cmlcp->yield_state, + ERTS_MON_LNK_FIRE_LIMIT/2); + if (yield) + break; + + cmlcp->dist = NULL; + erts_mon_link_dist_dec_refc(dist); + + ASSERT(!cmlcp->yield_state); + cmlcp->state = ERTS_CML_CLEANUP_STATE_NODE_MONITORS; + case ERTS_CML_CLEANUP_STATE_NODE_MONITORS: + if (cmlcp->trigger_node_monitors) { + send_nodes_mon_msgs(NULL, + am_nodedown, + cmlcp->nodename, + cmlcp->visability, + cmlcp->reason); + } + erts_cleanup_offheap(&cmlcp->oh); + erts_free(ERTS_ALC_T_CML_CLEANUP, vcmlcp); + return; /* done */ + } + + /* yield... */ + + esdp = erts_get_scheduler_data(); + ASSERT(esdp && esdp->type == ERTS_SCHED_NORMAL); + erts_schedule_misc_aux_work((int) esdp->no, + con_monitor_link_cleanup, + (void *) cmlcp); +} + +static void +schedule_con_monitor_link_cleanup(ErtsMonLnkDist *dist, + Eterm nodename, + Eterm visability, + Eterm reason) +{ + if (dist || is_value(nodename)) { + ErtsSchedulerData *esdp; + ErtsConMonLnkCleanup *cmlcp; + Uint rsz, size; + + size = sizeof(ErtsConMonLnkCleanup); + + if (is_non_value(reason) || is_immed(reason)) { + rsz = 0; + size -= sizeof(Eterm); + } + else { + rsz = size_object(reason); + size += sizeof(Eterm) * (rsz - 1); + } + + cmlcp = erts_alloc(ERTS_ALC_T_CML_CLEANUP, size); + + ERTS_INIT_OFF_HEAP(&cmlcp->oh); + + cmlcp->yield_state = NULL; + cmlcp->dist = dist; + if (!dist) + cmlcp->state = ERTS_CML_CLEANUP_STATE_NODE_MONITORS; + else { + cmlcp->state = ERTS_CML_CLEANUP_STATE_LINKS; + erts_mtx_lock(&dist->mtx); + ASSERT(dist->alive); + dist->alive = 0; + erts_mtx_unlock(&dist->mtx); + } + + cmlcp->trigger_node_monitors = is_value(nodename); + cmlcp->nodename = nodename; + cmlcp->visability = visability; + if (rsz == 0) + cmlcp->reason = reason; + else { + Eterm *hp = &cmlcp->heap[0]; + cmlcp->reason = copy_struct(reason, rsz, &hp, &cmlcp->oh); + } + + esdp = erts_get_scheduler_data(); + ASSERT(esdp && esdp->type == ERTS_SCHED_NORMAL); + erts_schedule_misc_aux_work((int) esdp->no, + con_monitor_link_cleanup, + (void *) cmlcp); + } +} + /* ** A full node name constists of a "n@h" ** @@ -235,170 +390,6 @@ int is_node_name_atom(Eterm a) return is_node_name((char*)atom_tab(i)->name, atom_tab(i)->len); } -typedef struct { - DistEntry *dep; - Eterm *lhp; -} NetExitsContext; - -/* -** This function is called when a distribution -** port or process terminates -*/ -static void doit_monitor_net_exits(ErtsMonitor *mon, void *vnecp) -{ - Process *rp; - ErtsMonitor *rmon; - DistEntry *dep = ((NetExitsContext *) vnecp)->dep; - ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK; - - rp = erts_pid2proc(NULL, 0, mon->u.pid, rp_locks); - if (!rp) - goto done; - - if (mon->type == MON_ORIGIN) { - /* local pid is being monitored */ - rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref); - /* ASSERT(rmon != NULL); nope, can happen during process exit */ - if (rmon != NULL) { - erts_destroy_monitor(rmon); - } - } else { - DeclareTmpHeapNoproc(lhp,3); - Eterm watched; - UseTmpHeapNoproc(3); - ASSERT(mon->type == MON_TARGET); - rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref); - /* ASSERT(rmon != NULL); can happen during process exit */ - if (rmon != NULL) { - ASSERT(rmon->type == MON_ORIGIN); - ASSERT(is_atom(rmon->name) || is_nil(rmon->name)); - watched = (is_atom(rmon->name) - ? TUPLE2(lhp, rmon->name, dep->sysname) - : rmon->u.pid); - rp_locks |= ERTS_PROC_LOCKS_MSG_SEND; - erts_proc_lock(rp, ERTS_PROC_LOCKS_MSG_SEND); - erts_queue_monitor_message(rp, &rp_locks, mon->ref, am_process, - watched, am_noconnection); - erts_destroy_monitor(rmon); - } - UnUseTmpHeapNoproc(3); - } - erts_proc_unlock(rp, rp_locks); - done: - erts_destroy_monitor(mon); -} - -typedef struct { - NetExitsContext *necp; - ErtsLink *lnk; -} LinkNetExitsContext; - -/* -** This is the function actually doing the job of sending exit messages -** for links in a dist entry upon net_exit (the node goes down), NB, -** only process links, not node monitors are handled here, -** they reside in a separate tree.... -*/ -static void doit_link_net_exits_sub(ErtsLink *sublnk, void *vlnecp) -{ - ErtsLink *lnk = ((LinkNetExitsContext *) vlnecp)->lnk; /* the local pid */ - ErtsLink *rlnk; - Process *rp; - - ASSERT(lnk->type == LINK_PID); - if (is_internal_pid(lnk->pid)) { - int xres; - ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCKS_XSIG_SEND; - - rp = erts_pid2proc(NULL, 0, lnk->pid, rp_locks); - if (!rp) { - goto done; - } - - rlnk = erts_remove_link(&ERTS_P_LINKS(rp), sublnk->pid); - xres = erts_send_exit_signal(NULL, - sublnk->pid, - rp, - &rp_locks, - am_noconnection, - NIL, - NULL, - 0); - - if (rlnk) { - erts_destroy_link(rlnk); - if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) { - /* We didn't exit the process and it is traced */ - trace_proc(NULL, 0, rp, am_getting_unlinked, sublnk->pid); - } - } - erts_proc_unlock(rp, rp_locks); - } - done: - erts_destroy_link(sublnk); - -} - - - - - -/* -** This function is called when a distribution -** port or process terminates, once for each link on the high level, -** it in turn traverses the link subtree for the specific link node... -*/ -static void doit_link_net_exits(ErtsLink *lnk, void *vnecp) -{ - LinkNetExitsContext lnec = {(NetExitsContext *) vnecp, lnk}; - ASSERT(lnk->type == LINK_PID); - erts_sweep_links(ERTS_LINK_ROOT(lnk), &doit_link_net_exits_sub, (void *) &lnec); -#ifdef DEBUG - ERTS_LINK_ROOT(lnk) = NULL; -#endif - erts_destroy_link(lnk); -} - - -static void doit_node_link_net_exits(ErtsLink *lnk, void *vnecp) -{ - DistEntry *dep = ((NetExitsContext *) vnecp)->dep; - Eterm name = dep->sysname; - Process *rp; - ErtsLink *rlnk; - Uint i,n; - ASSERT(lnk->type == LINK_NODE); - if (is_internal_pid(lnk->pid)) { - ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK; - ErlOffHeap *ohp; - rp = erts_proc_lookup(lnk->pid); - if (!rp) - goto done; - erts_proc_lock(rp, rp_locks); - if (!ERTS_PROC_IS_EXITING(rp)) { - rlnk = erts_remove_link(&ERTS_P_LINKS(rp), name); - if (rlnk != NULL) { - ASSERT(is_atom(rlnk->pid) && (rlnk->type == LINK_NODE)); - erts_destroy_link(rlnk); - } - n = ERTS_LINK_REFC(lnk); - for (i = 0; i < n; ++i) { - Eterm tup; - Eterm *hp; - ErtsMessage *msgp; - - msgp = erts_alloc_message_heap(rp, &rp_locks, - 3, &hp, &ohp); - tup = TUPLE2(hp, am_nodedown, name); - erts_queue_message(rp, rp_locks, msgp, tup, am_system); - } - } - erts_proc_unlock(rp, rp_locks); - } - done: - erts_destroy_link(lnk); -} - static void set_node_not_alive(void *unused) { @@ -444,15 +435,8 @@ inc_no_nodes(void) static void kill_dist_ctrl_proc(void *vpid) { - Eterm pid = (Eterm) vpid; - ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND; - Process *rp = erts_pid2proc(NULL, 0, pid, rp_locks); - if (rp) { - erts_send_exit_signal(NULL, rp->common.id, rp, &rp_locks, - am_kill, NIL, NULL, 0); - if (rp_locks) - erts_proc_unlock(rp, rp_locks); - } + erts_proc_sig_send_exit(NULL, (Eterm) vpid, (Eterm) vpid, + am_kill, NIL, 0); } static void @@ -572,10 +556,7 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason) } } else { /* Call from distribution controller (port/process) */ - NetExitsContext nec = {dep}; - ErtsLink *nlinks; - ErtsLink *node_links; - ErtsMonitor *monitors; + ErtsMonLnkDist *mld; Uint32 flags; erts_atomic_set_mb(&dep->dist_cmd_scheduled, 1); @@ -588,27 +569,21 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason) erts_port_task_abort(&dep->dist_cmd); } - if (dep->status & ERTS_DE_SFLG_EXITING) { + if (dep->state == ERTS_DE_STATE_EXITING) { #ifdef DEBUG ASSERT(erts_atomic32_read_nob(&dep->qflgs) & ERTS_DE_QFLG_EXIT); #endif } else { - dep->status |= ERTS_DE_SFLG_EXITING; + dep->state = ERTS_DE_STATE_EXITING; erts_mtx_lock(&dep->qlock); ASSERT(!(erts_atomic32_read_nob(&dep->qflgs) & ERTS_DE_QFLG_EXIT)); erts_atomic32_read_bor_relb(&dep->qflgs, ERTS_DE_QFLG_EXIT); erts_mtx_unlock(&dep->qlock); } - erts_de_links_lock(dep); - monitors = dep->monitors; - nlinks = dep->nlinks; - node_links = dep->node_links; - dep->monitors = NULL; - dep->nlinks = NULL; - dep->node_links = NULL; - erts_de_links_unlock(dep); + mld = dep->mld; + dep->mld = NULL; nodename = dep->sysname; flags = dep->flags; @@ -617,15 +592,14 @@ int erts_do_net_exits(DistEntry *dep, Eterm reason) erts_de_rwunlock(dep); - erts_sweep_monitors(monitors, &doit_monitor_net_exits, (void *) &nec); - erts_sweep_links(nlinks, &doit_link_net_exits, (void *) &nec); - erts_sweep_links(node_links, &doit_node_link_net_exits, (void *) &nec); - - send_nodes_mon_msgs(NULL, - am_nodedown, - nodename, - flags & DFLAG_PUBLISHED ? am_visible : am_hidden, - reason == am_normal ? am_connection_closed : reason); + schedule_con_monitor_link_cleanup(mld, + nodename, + (flags & DFLAG_PUBLISHED + ? am_visible + : am_hidden), + (reason == am_normal + ? am_connection_closed + : reason)); clear_dist_entry(dep); } @@ -641,6 +615,14 @@ trap_function(Eterm func, int arity) return erts_export_put(am_erlang, func, arity); } +/* + * Sync with dist_util.erl: + * + * -record(erts_dflags, + * {default, mandatory, addable, rejectable, strict_order}). + */ +static Eterm erts_dflags_record; + void init_dist(void) { init_nodes_monitors(); @@ -653,10 +635,19 @@ void init_dist(void) /* Lookup/Install all references to trap functions */ dmonitor_node_trap = trap_function(am_dmonitor_node,3); - dmonitor_p_trap = trap_function(am_dmonitor_p, 2); dist_ctrl_put_data_trap = erts_export_put(am_erts_internal, am_dist_ctrl_put_data, 2); + { + Eterm* hp = erts_alloc(ERTS_ALC_T_LITERAL, (1+6)*sizeof(Eterm)); + erts_dflags_record = TUPLE6(hp, am_erts_dflags, + make_small(DFLAG_DIST_DEFAULT), + make_small(DFLAG_DIST_MANDATORY), + make_small(DFLAG_DIST_ADDABLE), + make_small(DFLAG_DIST_REJECTABLE), + make_small(DFLAG_DIST_STRICT_ORDER)); + erts_set_literal_tag(&erts_dflags_record, hp, (1+6)); + } } #define ErtsDistOutputBuf2Binary(OB) \ @@ -753,21 +744,13 @@ static void clear_dist_entry(DistEntry *dep) cache = dep->cache; dep->cache = NULL; -#ifdef DEBUG - erts_de_links_lock(dep); - ASSERT(!dep->nlinks); - ASSERT(!dep->node_links); - ASSERT(!dep->monitors); - erts_de_links_unlock(dep); -#endif - erts_mtx_lock(&dep->qlock); erts_atomic64_set_nob(&dep->in, 0); erts_atomic64_set_nob(&dep->out, 0); obuf = clear_de_out_queues(dep); - dep->status = 0; + dep->state = ERTS_DE_STATE_IDLE; suspendees = get_suspended_on_de(dep, ERTS_DE_QFLGS_ALL); erts_mtx_unlock(&dep->qlock); @@ -869,8 +852,7 @@ erts_dsig_send_unlink(ErtsDSigData *dsdp, Eterm local, Eterm remote) /* A local process that's being monitored by a remote one exits. We send: - {DOP_MONITOR_P_EXIT, Local pid or name, Remote pid, ref, reason}, - which is rather sad as only the ref is needed, no pid's... */ + {DOP_MONITOR_P_EXIT, Local pid or name, Remote pid, ref, reason} */ int erts_dsig_send_m_exit(ErtsDSigData *dsdp, Eterm watcher, Eterm watched, Eterm ref, Eterm reason) @@ -891,12 +873,6 @@ erts_dsig_send_m_exit(ErtsDSigData *dsdp, Eterm watcher, Eterm watched, ctl = TUPLE5(&ctl_heap[0], make_small(DOP_MONITOR_P_EXIT), watched, watcher, ref, reason); -#ifdef DEBUG - erts_de_links_lock(dsdp->dep); - ASSERT(!erts_lookup_monitor(dsdp->dep->monitors, ref)); - erts_de_links_unlock(dsdp->dep); -#endif - res = dsig_send_ctl(dsdp, ctl, 1); UnUseTmpHeapNoproc(6); return res; @@ -935,8 +911,7 @@ erts_dsig_send_monitor(ErtsDSigData *dsdp, Eterm watcher, Eterm watched, /* A local process monitoring a remote one wants to stop monitoring, either because of a demonitor bif call or because the local process died. We send - {DOP_DEMONITOR_P, Local pid, Remote pid or name, ref}, which is once again - rather redundant as only the ref will be needed on the other side... */ + {DOP_DEMONITOR_P, Local pid, Remote pid or name, ref} */ int erts_dsig_send_demonitor(ErtsDSigData *dsdp, Eterm watcher, Eterm watched, Eterm ref, int force) @@ -962,6 +937,24 @@ erts_dsig_send_demonitor(ErtsDSigData *dsdp, Eterm watcher, return res; } +static int can_send_seqtrace_token(ErtsSendContext* ctx, Eterm token) { + Eterm label; + + if (ctx->dep->flags & DFLAG_BIG_SEQTRACE_LABELS) { + /* The other end is capable of handling arbitrary seq_trace labels. */ + return 1; + } + + /* The other end only tolerates smalls, but since we could potentially be + * talking to an old 32-bit emulator from a 64-bit one, we have to check + * whether the label is small on any emulator. */ + label = SEQ_TRACE_T_LABEL(token); + + return is_small(label) && + signed_val(label) <= (ERTS_SINT32_MAX >> _TAG_IMMED1_SIZE) && + signed_val(label) >= (ERTS_SINT32_MIN >> _TAG_IMMED1_SIZE); +} + int erts_dsig_send_msg(Eterm remote, Eterm message, ErtsSendContext* ctx) { @@ -995,37 +988,38 @@ erts_dsig_send_msg(Eterm remote, Eterm message, ErtsSendContext* ctx) "%T", remote); msize = size_object(message); if (have_seqtrace(token)) { - tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); + tok_label = SEQ_TRACE_T_DTRACE_LABEL(token); tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); } } #endif - if (token != NIL) { - Eterm el1, el2; - if (ctx->dep->flags & DFLAG_SEND_SENDER) { - el1 = make_small(DOP_SEND_SENDER_TT); - el2 = sender->common.id; - } - else { - el1 = make_small(DOP_SEND_TT); - el2 = am_Empty; - } - ctl = TUPLE4(&ctx->ctl_heap[0], el1, el2, remote, token); - } - else { - Eterm el1, el2; + { + Eterm dist_op, sender_id; + int send_token; + + send_token = (token != NIL && can_send_seqtrace_token(ctx, token)); + if (ctx->dep->flags & DFLAG_SEND_SENDER) { - el1 = make_small(DOP_SEND_SENDER); - el2 = sender->common.id; + dist_op = make_small(send_token ? + DOP_SEND_SENDER_TT : + DOP_SEND_SENDER); + sender_id = sender->common.id; + } else { + dist_op = make_small(send_token ? + DOP_SEND_TT : + DOP_SEND); + sender_id = am_Empty; } - else { - el1 = make_small(DOP_SEND); - el2 = am_Empty; + + if (send_token) { + ctl = TUPLE4(&ctx->ctl_heap[0], dist_op, sender_id, remote, token); + } else { + ctl = TUPLE3(&ctx->ctl_heap[0], dist_op, sender_id, remote); } - ctl = TUPLE3(&ctx->ctl_heap[0], el1, el2, remote); } + DTRACE6(message_send, sender_name, receiver_name, msize, tok_label, tok_lastcnt, tok_serial); DTRACE7(message_send_remote, sender_name, node_name, receiver_name, @@ -1071,19 +1065,20 @@ erts_dsig_send_reg_msg(Eterm remote_name, Eterm message, "{%T,%s}", remote_name, node_name); msize = size_object(message); if (have_seqtrace(token)) { - tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); + tok_label = SEQ_TRACE_T_DTRACE_LABEL(token); tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); } } #endif - if (token != NIL) + if (token != NIL && can_send_seqtrace_token(ctx, token)) ctl = TUPLE5(&ctx->ctl_heap[0], make_small(DOP_REG_SEND_TT), sender->common.id, am_Empty, remote_name, token); else ctl = TUPLE4(&ctx->ctl_heap[0], make_small(DOP_REG_SEND), sender->common.id, am_Empty, remote_name); + DTRACE6(message_send, sender_name, receiver_name, msize, tok_label, tok_lastcnt, tok_serial); DTRACE7(message_send_remote, sender_name, node_name, receiver_name, @@ -1135,7 +1130,7 @@ erts_dsig_send_exit_tt(ErtsDSigData *dsdp, Eterm local, Eterm remote, erts_snprintf(reason_str, sizeof(DTRACE_CHARBUF_NAME(reason_str)), "%T", reason); if (have_seqtrace(token)) { - tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); + tok_label = SEQ_TRACE_T_DTRACE_LABEL(token); tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); } @@ -1258,8 +1253,6 @@ int erts_net_message(Port *prt, Sint type; Eterm token; Eterm token_size; - ErtsMonitor *mon; - ErtsLink *lnk; Uint tuple_arity; int res; Uint32 connection_id; @@ -1357,88 +1350,89 @@ int erts_net_message(Port *prt, token = NIL; switch (type = unsigned_val(tuple[1])) { - case DOP_LINK: + case DOP_LINK: { + ErtsDSigData dsd; + int code; + if (tuple_arity != 3) { goto invalid_message; } from = tuple[2]; to = tuple[3]; /* local proc to link to */ - if (is_not_pid(from) || is_not_pid(to)) { - goto invalid_message; - } + if (is_not_external_pid(from)) + goto invalid_message; - rp = erts_pid2proc_opt(NULL, 0, - to, ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X); - if (!rp) { - /* This is tricky (we MUST force a distributed send) */ - ErtsDSigData dsd; - int code; - code = erts_dsig_prepare(&dsd, dep, NULL, 0, ERTS_DSP_NO_LOCK, 0, 0); - if (code == ERTS_DSIG_PREP_CONNECTED) { - code = erts_dsig_send_exit(&dsd, to, from, am_noproc); - ASSERT(code == ERTS_DSIG_SEND_OK); - } - break; - } + if (dep != external_pid_dist_entry(from)) + goto invalid_message; - erts_de_links_lock(dep); - res = erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, from); + if (is_external_pid(to)) { + if (external_pid_dist_entry(to) != erts_this_dist_entry) + goto invalid_message; + /* old incarnation of node; reply noproc... */ + } + else if (is_internal_pid(to)) { + ErtsLinkData *ldp = erts_link_create(ERTS_LNK_TYPE_DIST_PROC, + from, to); + ASSERT(ldp->a.other.item == to); + ASSERT(eq(ldp->b.other.item, from)); +#ifdef DEBUG + code = +#endif + erts_link_dist_insert(&ldp->a, dep->mld); + ASSERT(code); - if (res < 0) { - /* It was already there! Lets skip the rest... */ - erts_de_links_unlock(dep); - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - break; - } - lnk = erts_add_or_lookup_link(&(dep->nlinks), LINK_PID, rp->common.id); - erts_add_link(&(ERTS_LINK_ROOT(lnk)), LINK_PID, from); - erts_de_links_unlock(dep); + if (erts_proc_sig_send_link(NULL, to, &ldp->b)) + break; /* done */ - if (IS_TRACED_FL(rp, F_TRACE_PROCS)) - trace_proc(NULL, 0, rp, am_getting_linked, from); + /* Failed to send signal; cleanup and reply noproc... */ + +#ifdef DEBUG + code = +#endif + erts_link_dist_delete(&ldp->a); + ASSERT(code); + erts_link_release_both(ldp); + } + + code = erts_dsig_prepare(&dsd, dep, NULL, 0, ERTS_DSP_NO_LOCK, 0, 0); + if (code == ERTS_DSIG_PREP_CONNECTED) { + code = erts_dsig_send_exit(&dsd, to, from, am_noproc); + ASSERT(code == ERTS_DSIG_SEND_OK); + } - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); break; + } case DOP_UNLINK: { - ErtsDistLinkData dld; if (tuple_arity != 3) { goto invalid_message; } from = tuple[2]; to = tuple[3]; - if (is_not_pid(from) || is_not_pid(to)) { + if (is_not_external_pid(from)) + goto invalid_message; + if (dep != external_pid_dist_entry(from)) goto invalid_message; - } - - rp = erts_pid2proc_opt(NULL, 0, - to, ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X); - if (!rp) - break; - - lnk = erts_remove_link(&ERTS_P_LINKS(rp), from); - if (IS_TRACED_FL(rp, F_TRACE_PROCS) && lnk != NULL) { - trace_proc(NULL, 0, rp, am_getting_unlinked, from); - } + if (is_external_pid(to) + && erts_this_dist_entry == external_pid_dist_entry(from)) + break; - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); + if (is_not_internal_pid(to)) + goto invalid_message; - erts_remove_dist_link(&dld, to, from, dep); - erts_destroy_dist_link(&dld); - if (lnk) - erts_destroy_link(lnk); + erts_proc_sig_send_dist_unlink(dep, from, to); break; } case DOP_MONITOR_P: { /* A remote process wants to monitor us, we get: {DOP_MONITOR_P, Remote pid, local pid or name, ref} */ - Eterm name; - + Eterm pid, name; + ErtsDSigData dsd; + int code; + if (tuple_arity != 4) { goto invalid_message; } @@ -1447,44 +1441,64 @@ int erts_net_message(Port *prt, watched = tuple[3]; /* local proc to monitor */ ref = tuple[4]; - if (is_not_ref(ref)) { + if (is_not_external_pid(watcher)) + goto invalid_message; + else if (external_pid_dist_entry(watcher) != dep) + goto invalid_message; + + if (is_not_ref(ref)) goto invalid_message; - } - if (is_atom(watched)) { - name = watched; - rp = erts_whereis_process(NULL, 0, - watched, ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X); - } - else { - name = NIL; - rp = erts_pid2proc_opt(NULL, 0, - watched, ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X); - } + if (is_internal_pid(watched)) { + name = NIL; + pid = watched; + } + else if (is_atom(watched)) { + name = watched; + pid = erts_whereis_name_to_id(NULL, watched); + /* if port or undefined; reply noproc... */ + } + else if (is_external_pid(watched) + && external_pid_dist_entry(watched) == erts_this_dist_entry) { + name = NIL; + pid = am_undefined; /* old incarnation; reply noproc... */ + } + else + goto invalid_message; - if (!rp) { - ErtsDSigData dsd; - int code; - code = erts_dsig_prepare(&dsd, dep, NULL, 0, ERTS_DSP_NO_LOCK, 0, 0); - if (code == ERTS_DSIG_PREP_CONNECTED) { - code = erts_dsig_send_m_exit(&dsd, watcher, watched, ref, - am_noproc); - ASSERT(code == ERTS_DSIG_SEND_OK); - } - } - else { - if (is_atom(watched)) - watched = rp->common.id; - erts_de_links_lock(dep); - erts_add_monitor(&(dep->monitors), MON_ORIGIN, ref, watched, name); - erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, ref, watcher, name); - erts_de_links_unlock(dep); - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - } + if (is_internal_pid(pid)) { + ErtsMonitorData *mdp; + mdp = erts_monitor_create(ERTS_MON_TYPE_DIST_PROC, + ref, watcher, pid, name); - break; +#ifdef DEBUG + code = +#endif + erts_monitor_dist_insert(&mdp->origin, dep->mld); + ASSERT(code); + + if (erts_proc_sig_send_monitor(&mdp->target, pid)) + break; /* done */ + + /* Failed to send to local proc; cleanup reply noproc... */ + +#ifdef DEBUG + code = +#endif + erts_monitor_dist_delete(&mdp->origin); + ASSERT(code); + erts_monitor_release_both(mdp); + + } + + code = erts_dsig_prepare(&dsd, dep, NULL, 0, ERTS_DSP_NO_LOCK, 0, 0); + if (code == ERTS_DSIG_PREP_CONNECTED) { + code = erts_dsig_send_m_exit(&dsd, watcher, watched, ref, + am_noproc); + ASSERT(code == ERTS_DSIG_SEND_OK); + } + + break; } case DOP_DEMONITOR_P: @@ -1495,36 +1509,43 @@ int erts_net_message(Port *prt, if (tuple_arity != 4) { goto invalid_message; } - /* watcher = tuple[2]; */ - /* watched = tuple[3]; May be an atom in case of monitor name */ + + watcher = tuple[2]; + watched = tuple[3]; ref = tuple[4]; - if(is_not_ref(ref)) { + if (is_not_ref(ref)) { goto invalid_message; } - erts_de_links_lock(dep); - mon = erts_remove_monitor(&(dep->monitors),ref); - erts_de_links_unlock(dep); - /* ASSERT(mon != NULL); can happen in case of broken dist message */ - if (mon == NULL) { - break; - } - watched = mon->u.pid; - erts_destroy_monitor(mon); - rp = erts_pid2proc_opt(NULL, 0, - watched, ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X); - if (!rp) { - break; - } - mon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref); - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - ASSERT(mon != NULL); - if (mon == NULL) { - break; - } - erts_destroy_monitor(mon); + if (is_not_external_pid(watcher) || external_pid_dist_entry(watcher) != dep) + goto invalid_message; + + if (is_internal_pid(watched)) + erts_proc_sig_send_dist_demonitor(watched, ref); + else if (is_external_pid(watched) + && external_pid_dist_entry(watched) == erts_this_dist_entry) { + /* old incarnation; ignore it */ + ; + } + else if (is_atom(watched)) { + ErtsMonLnkDist *mld = dep->mld; + ErtsMonitor *mon; + + erts_mtx_lock(&mld->mtx); + + mon = erts_monitor_tree_lookup(mld->orig_name_monitors, ref); + if (mon) + erts_monitor_tree_delete(&mld->orig_name_monitors, mon); + + erts_mtx_unlock(&mld->mtx); + + if (mon) + erts_proc_sig_send_demonitor(mon); + } + else + goto invalid_message; + break; case DOP_REG_SEND_TT: @@ -1651,68 +1672,36 @@ int erts_net_message(Port *prt, /* We are monitoring a process on the remote node which dies, we get {DOP_MONITOR_P_EXIT, Remote pid or name, Local pid, ref, reason} */ - - DeclareTmpHeapNoproc(lhp,3); - Eterm sysname; - ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_MSG_SEND|ERTS_PROC_LOCK_LINK; - if (tuple_arity != 5) { goto invalid_message; } - /* watched = tuple[2]; */ /* remote proc which died */ - /* watcher = tuple[3]; */ + watched = tuple[2]; /* remote proc or name which died */ + watcher = tuple[3]; ref = tuple[4]; reason = tuple[5]; - if(is_not_ref(ref)) { + if (is_not_ref(ref)) goto invalid_message; - } - - erts_de_links_lock(dep); - sysname = dep->sysname; - mon = erts_remove_monitor(&(dep->monitors), ref); - /* - * If demonitor was performed at the same time as the - * monitored process exits, monitoring side will have - * removed info about monitor. In this case, do nothing - * and everything will be as it should. - */ - erts_de_links_unlock(dep); - if (mon == NULL) { - break; - } - rp = erts_pid2proc(NULL, 0, mon->u.pid, rp_locks); - erts_destroy_monitor(mon); - if (rp == NULL) { - break; - } + if (is_not_external_pid(watched) && is_not_atom(watched)) + goto invalid_message; - mon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref); + if (is_not_internal_pid(watcher)) { + if (!is_external_pid(watcher)) + goto invalid_message; + if (erts_this_dist_entry == external_pid_dist_entry(watcher)) + break; + goto invalid_message; + } - if (mon == NULL) { - erts_proc_unlock(rp, rp_locks); - break; - } - UseTmpHeapNoproc(3); - - watched = (is_not_nil(mon->name) - ? TUPLE2(&lhp[0], mon->name, sysname) - : mon->u.pid); - - erts_queue_monitor_message(rp, &rp_locks, - ref, am_process, watched, reason); - erts_proc_unlock(rp, rp_locks); - erts_destroy_monitor(mon); - UnUseTmpHeapNoproc(3); + erts_proc_sig_send_dist_monitor_down(dep, ref, watched, + watcher, reason); break; } case DOP_EXIT_TT: case DOP_EXIT: { - ErtsDistLinkData dld; - ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCKS_XSIG_SEND; /* 'from', which 'to' is linked to, died */ if (type == DOP_EXIT) { if (tuple_arity != 4) { @@ -1732,56 +1721,19 @@ int erts_net_message(Port *prt, token = tuple[4]; reason = tuple[5]; } - if (is_not_pid(from) || is_not_internal_pid(to)) { + if (is_not_external_pid(from) + || dep != external_pid_dist_entry(from) + || is_not_internal_pid(to)) { goto invalid_message; } - rp = erts_pid2proc(NULL, 0, to, rp_locks); - if (!rp) - lnk = NULL; - else { - lnk = erts_remove_link(&ERTS_P_LINKS(rp), from); - - /* If lnk == NULL, we have unlinked on this side, i.e. - * ignore exit. - */ - if (lnk) { - int xres; -#if 0 - /* Arndt: Maybe it should never be 'kill', but it can be, - namely when a linked process does exit(kill). Until we know - whether that is incorrect and what should happen instead, - we leave the assertion out. */ - ASSERT(reason != am_kill); /* should never be kill (killed) */ -#endif - xres = erts_send_exit_signal(NULL, - from, - rp, - &rp_locks, - reason, - token, - NULL, - ERTS_XSIG_FLG_IGN_KILL); - if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) { - /* We didn't exit the process and it is traced */ - if (rp_locks & ERTS_PROC_LOCKS_XSIG_SEND) { - erts_proc_unlock(rp, ERTS_PROC_LOCKS_XSIG_SEND); - rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND; - } - trace_proc(NULL, 0, rp, am_getting_unlinked, from); - } - } - erts_proc_unlock(rp, rp_locks); - } - erts_remove_dist_link(&dld, to, from, dep); - if (lnk) - erts_destroy_link(lnk); - erts_destroy_dist_link(&dld); + erts_proc_sig_send_dist_link_exit(dep, + from, to, + reason, token); break; } case DOP_EXIT2_TT: - case DOP_EXIT2: { - ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND; + case DOP_EXIT2: /* 'from' is send an exit signal to 'to' */ if (type == DOP_EXIT2) { if (tuple_arity != 4) { @@ -1803,20 +1755,10 @@ int erts_net_message(Port *prt, if (is_not_pid(from) || is_not_internal_pid(to)) { goto invalid_message; } - rp = erts_pid2proc(NULL, 0, to, rp_locks); - if (rp) { - (void) erts_send_exit_signal(NULL, - from, - rp, - &rp_locks, - reason, - token, - NULL, - 0); - erts_proc_unlock(rp, rp_locks); - } + + erts_proc_sig_send_exit(NULL, from, to, reason, token, 0); break; - } + case DOP_GROUP_LEADER: if (tuple_arity != 3) { goto invalid_message; @@ -1827,11 +1769,7 @@ int erts_net_message(Port *prt, goto invalid_message; } - rp = erts_pid2proc(NULL, 0, to, ERTS_PROC_LOCK_MAIN); - if (!rp) - break; - rp->group_leader = STORE_NC_IN_PROC(rp, from); - erts_proc_unlock(rp, ERTS_PROC_LOCK_MAIN); + (void) erts_proc_sig_send_group_leader(NULL, to, from, NIL); break; default: @@ -1982,6 +1920,7 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx) break; } ctx->u.ec.flags = ctx->flags; + ctx->u.ec.hopefull_flags = 0; ctx->u.ec.level = 0; ctx->u.ec.wstack.wstart = NULL; ctx->obuf->msg_start = ctx->obuf->ext_endp; @@ -2005,6 +1944,7 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx) ctx->data_size = ctx->obuf->ext_endp - ctx->obuf->extp; + ctx->obuf->hopefull_flags = ctx->u.ec.hopefull_flags; /* * Signal encoded; now verify that the connection still exists, * and if so enqueue the signal and schedule it for send. @@ -2012,8 +1952,8 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx) ctx->obuf->next = NULL; erts_de_rlock(dep); cid = dep->cid; - if (!(dep->status & (ERTS_DE_SFLG_PENDING | ERTS_DE_SFLG_CONNECTED)) - || dep->status & ERTS_DE_SFLG_EXITING + if (dep->state == ERTS_DE_STATE_EXITING + || dep->state == ERTS_DE_STATE_IDLE || dep->connection_id != dsdp->connection_id) { /* Not the same connection as when we started; drop message... */ erts_de_runlock(dep); @@ -2088,7 +2028,7 @@ erts_dsig_send(ErtsDSigData *dsdp, struct erts_dsig_send_context* ctx) } erts_mtx_unlock(&dep->qlock); - if (!(dep->status & ERTS_DE_SFLG_PENDING)) { + if (dep->state != ERTS_DE_STATE_PENDING) { if (is_internal_port(dep->cid)) erts_schedule_dist_command(NULL, dep); } @@ -2270,7 +2210,7 @@ int erts_dist_command(Port *prt, int initial_reds) { Sint reds = initial_reds - ERTS_PORT_REDS_DIST_CMD_START; - Uint32 status; + enum dist_entry_state state; Uint32 flags; Sint qsize, obufsize = 0; ErtsDistOutputQueue oq, foq; @@ -2285,17 +2225,17 @@ erts_dist_command(Port *prt, int initial_reds) erts_de_rlock(dep); flags = dep->flags; - status = dep->status; + state = dep->state; send = dep->send; erts_de_runlock(dep); - if (status & ERTS_DE_SFLG_EXITING) { + if (state == ERTS_DE_STATE_EXITING) { erts_deliver_port_exit(prt, prt->common.id, am_killed, 0, 1); reds -= ERTS_PORT_REDS_DIST_CMD_EXIT; return initial_reds - reds; } - ASSERT(!(status & ERTS_DE_SFLG_PENDING)); + ASSERT(state != ERTS_DE_STATE_PENDING); ASSERT(send); @@ -2831,7 +2771,7 @@ dist_ctrl_get_data_1(BIF_ALIST_1) erts_de_rlock(dep); - if (dep->status & ERTS_DE_SFLG_EXITING) + if (dep->state == ERTS_DE_STATE_EXITING) goto return_none; ASSERT(dep->cid == BIF_P->common.id); @@ -2934,9 +2874,9 @@ erts_dist_port_not_busy(Port *prt) static void kill_connection(DistEntry *dep) { ERTS_LC_ASSERT(erts_lc_is_de_rwlocked(dep)); - ASSERT(dep->status == ERTS_DE_SFLG_CONNECTED); + ASSERT(dep->state == ERTS_DE_STATE_CONNECTED); - dep->status |= ERTS_DE_SFLG_EXITING; + dep->state = ERTS_DE_STATE_EXITING; erts_mtx_lock(&dep->qlock); ASSERT(!(erts_atomic32_read_nob(&dep->qflgs) & ERTS_DE_QFLG_EXIT)); erts_atomic32_read_bor_nob(&dep->qflgs, ERTS_DE_QFLG_EXIT); @@ -2953,7 +2893,7 @@ erts_kill_dist_connection(DistEntry *dep, Uint32 connection_id) { erts_de_rwlock(dep); if (connection_id == dep->connection_id - && dep->status == ERTS_DE_SFLG_CONNECTED) { + && dep->state == ERTS_DE_STATE_CONNECTED) { kill_connection(dep); } @@ -2969,57 +2909,55 @@ static void doit_print_monitor_info(ErtsMonitor *mon, void *vptdp) { fmtfn_t to = ((struct print_to_data *) vptdp)->to; void *arg = ((struct print_to_data *) vptdp)->arg; - Process *rp; - ErtsMonitor *rmon; - rp = erts_proc_lookup(mon->u.pid); - if (!rp || (rmon = erts_lookup_monitor(ERTS_P_MONITORS(rp), mon->ref)) == NULL) { - erts_print(to, arg, "Warning, stray monitor for: %T\n", mon->u.pid); - } else if (mon->type == MON_ORIGIN) { - /* Local pid is being monitored */ + ErtsMonitorDataExtended *mdep; + + ASSERT(mon->flags & ERTS_ML_FLG_EXTENDED); + + mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon); + + ASSERT(mdep->dist); + + if (erts_monitor_is_origin(mon)) { erts_print(to, arg, "Remotely monitored by: %T %T\n", - mon->u.pid, rmon->u.pid); - } else { - erts_print(to, arg, "Remote monitoring: %T ", mon->u.pid); - if (is_not_atom(rmon->u.pid)) - erts_print(to, arg, "%T\n", rmon->u.pid); - else - erts_print(to, arg, "{%T, %T}\n", - rmon->name, - rmon->u.pid); /* which in this case is the - remote system name... */ + mon->other.item, mdep->md.target.other.item); + } + else { + erts_print(to, arg, "Remote monitoring: %T ", mon->other.item); + if (mon->flags & ERTS_ML_FLG_NAME) + erts_print(to, arg, "{%T, %T}\n", mdep->u.name, mdep->dist->nodename); + else + erts_print(to, arg, "%T\n", mdep->md.origin.other.item); } } -static void print_monitor_info(fmtfn_t to, void *arg, ErtsMonitor *mon) +static void print_monitor_info(fmtfn_t to, void *arg, DistEntry *dep) { struct print_to_data ptd = {to, arg}; - erts_doforall_monitors(mon,&doit_print_monitor_info,&ptd); -} - -typedef struct { - struct print_to_data *ptdp; - Eterm from; -} PrintLinkContext; - -static void doit_print_link_info2(ErtsLink *lnk, void *vpplc) -{ - PrintLinkContext *pplc = (PrintLinkContext *) vpplc; - erts_print(pplc->ptdp->to, pplc->ptdp->arg, "Remote link: %T %T\n", - pplc->from, lnk->pid); + if (dep->mld) { + erts_monitor_list_foreach(dep->mld->monitors, + doit_print_monitor_info, + (void *) &ptd); + erts_monitor_tree_foreach(dep->mld->orig_name_monitors, + doit_print_monitor_info, + (void *) &ptd); + } } static void doit_print_link_info(ErtsLink *lnk, void *vptdp) { - if (is_internal_pid(lnk->pid) && erts_proc_lookup(lnk->pid)) { - PrintLinkContext plc = {(struct print_to_data *) vptdp, lnk->pid}; - erts_doforall_links(ERTS_LINK_ROOT(lnk), &doit_print_link_info2, &plc); - } + struct print_to_data *ptdp = vptdp; + ErtsLink *lnk2 = erts_link_to_other(lnk, NULL); + erts_print(ptdp->to, ptdp->arg, "Remote link: %T %T\n", + lnk2->other.item, lnk->other.item); } -static void print_link_info(fmtfn_t to, void *arg, ErtsLink *lnk) +static void print_link_info(fmtfn_t to, void *arg, DistEntry *dep) { struct print_to_data ptd = {to, arg}; - erts_doforall_links(lnk, &doit_print_link_info, (void *) &ptd); + if (dep->mld) + erts_link_list_foreach(dep->mld->links, + doit_print_link_info, + (void *) &ptd); } typedef struct { @@ -3027,23 +2965,6 @@ typedef struct { Eterm sysname; } PrintNodeLinkContext; - -static void doit_print_nodelink_info(ErtsLink *lnk, void *vpcontext) -{ - PrintNodeLinkContext *pcontext = vpcontext; - - if (is_internal_pid(lnk->pid) && erts_proc_lookup(lnk->pid)) - erts_print(pcontext->ptd.to, pcontext->ptd.arg, - "Remote monitoring: %T %T\n", lnk->pid, pcontext->sysname); -} - -static void print_nodelink_info(fmtfn_t to, void *arg, ErtsLink *lnk, Eterm sysname) -{ - PrintNodeLinkContext context = {{to, arg}, sysname}; - erts_doforall_links(lnk, &doit_print_nodelink_info, &context); -} - - static int info_dist_entry(fmtfn_t to, void *arg, DistEntry *dep, int visible, int connected) { @@ -3074,8 +2995,8 @@ info_dist_entry(fmtfn_t to, void *arg, DistEntry *dep, int visible, int connecte erts_print(to, arg, "Name: %T", dep->sysname); erts_print(to, arg, "\n"); if (!connected && is_nil(dep->cid)) { - if (dep->nlinks) { - erts_print(to, arg, "Error: Got links to not connected node:%T\n", + if (dep->mld) { + erts_print(to, arg, "Error: Got links/monitors to not connected node:%T\n", dep->sysname); } return 0; @@ -3084,9 +3005,8 @@ info_dist_entry(fmtfn_t to, void *arg, DistEntry *dep, int visible, int connecte erts_print(to, arg, "Controller: %T\n", dep->cid, to); erts_print_node_info(to, arg, dep->sysname, NULL, NULL); - print_monitor_info(to, arg, dep->monitors); - print_link_info(to, arg, dep->nlinks); - print_nodelink_info(to, arg, dep->node_links, dep->sysname); + print_monitor_info(to, arg, dep); + print_link_info(to, arg, dep); return 0; @@ -3173,8 +3093,7 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2) goto error; /* Check that all trap functions are defined !! */ - if (dmonitor_node_trap->addressv[0] == NULL || - dmonitor_p_trap->addressv[0] == NULL) { + if (dmonitor_node_trap->addressv[0] == NULL) { goto error; } @@ -3341,7 +3260,7 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3) goto badarg; } - if (dep->status & ERTS_DE_SFLG_EXITING) { + if (dep->state == ERTS_DE_STATE_EXITING) { /* Suspend on dist entry waiting for the exit to finish */ ErtsProcList *plp = erts_proclist_create(BIF_P); plp->next = NULL; @@ -3351,8 +3270,8 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3) erts_mtx_unlock(&dep->qlock); goto yield; } - if (dep->status != ERTS_DE_SFLG_PENDING) { - if (dep->status == 0) + if (dep->state != ERTS_DE_STATE_PENDING) { + if (dep->state == ERTS_DE_STATE_IDLE) erts_set_dist_entry_pending(dep); else goto badarg; @@ -3390,7 +3309,7 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3) goto done; /* Already set */ } - if (dep->status & ERTS_DE_SFLG_EXITING) { + if (dep->state == ERTS_DE_STATE_EXITING) { /* Suspend on dist entry waiting for the exit to finish */ ErtsProcList *plp = erts_proclist_create(BIF_P); plp->next = NULL; @@ -3400,8 +3319,8 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3) erts_mtx_unlock(&dep->qlock); goto yield; } - if (dep->status != ERTS_DE_SFLG_PENDING) { - if (dep->status == 0) + if (dep->state != ERTS_DE_STATE_PENDING) { + if (dep->state == ERTS_DE_STATE_IDLE) erts_set_dist_entry_pending(dep); else goto badarg; @@ -3437,7 +3356,7 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3) #ifdef DEBUG ASSERT(erts_atomic_read_nob(&dep->qsize) == 0 - || (dep->status & ERTS_DE_SFLG_PENDING)); + || (dep->state == ERTS_DE_STATE_PENDING)); #endif if (flags & DFLAG_DIST_HDR_ATOM_CACHE) @@ -3506,6 +3425,11 @@ BIF_RETTYPE setnode_3(BIF_ALIST_3) goto done; } +BIF_RETTYPE erts_internal_get_dflags_0(BIF_ALIST_0) +{ + return erts_dflags_record; +} + BIF_RETTYPE erts_internal_new_connection_1(BIF_ALIST_1) { DistEntry* dep; @@ -3525,15 +3449,20 @@ BIF_RETTYPE erts_internal_new_connection_1(BIF_ALIST_1) erts_de_rwlock(dep); - if (ERTS_DE_IS_CONNECTED(dep) || dep->status & ERTS_DE_SFLG_PENDING) + switch (dep->state) { + case ERTS_DE_STATE_PENDING: + case ERTS_DE_STATE_CONNECTED: conn_id = dep->connection_id; - else if (dep->status == 0) { + break; + case ERTS_DE_STATE_IDLE: erts_set_dist_entry_pending(dep); conn_id = dep->connection_id; - } - else { - ASSERT(dep->status & ERTS_DE_SFLG_EXITING); + break; + case ERTS_DE_STATE_EXITING: conn_id = (dep->connection_id + 1) & ERTS_DIST_CON_ID_MASK; + break; + default: + erts_exit(ERTS_ABORT_EXIT, "Invalid dep->state (%d)\n", dep->state); } erts_de_rwunlock(dep); hp = HAlloc(BIF_P, 3 + ERTS_MAGIC_REF_THING_SIZE); @@ -3548,29 +3477,20 @@ static Sint abort_connection(DistEntry* dep, Uint32 conn_id) if (dep->connection_id != conn_id) ; - else if (dep->status == ERTS_DE_SFLG_CONNECTED) { + else if (dep->state == ERTS_DE_STATE_CONNECTED) { kill_connection(dep); } - else if (dep->status == ERTS_DE_SFLG_PENDING) { - NetExitsContext nec = {dep}; - ErtsLink *nlinks; - ErtsLink *node_links; - ErtsMonitor *monitors; + else if (dep->state == ERTS_DE_STATE_PENDING) { ErtsAtomCache *cache; ErtsDistOutputBuf *obuf; ErtsProcList *resume_procs; Sint reds = 0; + ErtsMonLnkDist *mld; ASSERT(is_nil(dep->cid)); - erts_de_links_lock(dep); - monitors = dep->monitors; - nlinks = dep->nlinks; - node_links = dep->node_links; - dep->monitors = NULL; - dep->nlinks = NULL; - dep->node_links = NULL; - erts_de_links_unlock(dep); + mld = dep->mld; + dep->mld = NULL; cache = dep->cache; dep->cache = NULL; @@ -3589,9 +3509,8 @@ static Sint abort_connection(DistEntry* dep, Uint32 conn_id) erts_de_rwunlock(dep); - erts_sweep_monitors(monitors, &doit_monitor_net_exits, &nec); - erts_sweep_links(nlinks, &doit_link_net_exits, &nec); - erts_sweep_links(node_links, &doit_node_link_net_exits, &nec); + schedule_con_monitor_link_cleanup(mld, THE_NON_VALUE, + THE_NON_VALUE, THE_NON_VALUE); if (resume_procs) { int resumed = erts_resume_processes(resume_procs); @@ -3600,6 +3519,17 @@ static Sint abort_connection(DistEntry* dep, Uint32 conn_id) delete_cache(cache); free_de_out_queues(dep, obuf); + + /* + * We wait to make DistEntry idle and accept new connection attempts + * until all is cleared and deallocated. This to get some back pressure + * against repeated failing connection attempts saturating all CPUs + * with cleanup jobs. + */ + erts_de_rwlock(dep); + ASSERT(dep->state == ERTS_DE_STATE_EXITING); + dep->state = ERTS_DE_STATE_IDLE; + erts_de_rwunlock(dep); return reds; } erts_de_rwunlock(dep); @@ -3631,7 +3561,7 @@ BIF_RETTYPE erts_internal_abort_connection_2(BIF_ALIST_2) int erts_auto_connect(DistEntry* dep, Process *proc, ErtsProcLocks proc_locks) { erts_de_rwlock(dep); - if (dep->status != 0) { + if (dep->state != ERTS_DE_STATE_IDLE) { erts_de_rwunlock(dep); } else { @@ -3820,8 +3750,8 @@ BIF_RETTYPE is_alive_0(BIF_ALIST_0) static BIF_RETTYPE monitor_node(Process* p, Eterm Node, Eterm Bool, Eterm Options) { - DistEntry *dep; - ErtsLink *lnk; + BIF_RETTYPE ret; + DistEntry *dep = NULL; Eterm l; int async_connect = 1; @@ -3840,33 +3770,71 @@ monitor_node(Process* p, Eterm Node, Eterm Bool, Eterm Options) if (l != NIL) { BIF_ERROR(p, BADARG); } + if (l != NIL) + goto badarg; - if (is_not_atom(Node) || - ((Bool != am_true) && (Bool != am_false)) || - ((erts_this_node->sysname == am_Noname) - && (Node != erts_this_node->sysname))) { - BIF_ERROR(p, BADARG); + if (is_not_atom(Node)) + goto badarg; + + if (erts_this_node->sysname == am_Noname && Node != am_Noname) + goto badarg; + + switch (Bool) { + + case am_false: { + ErtsMonitor *mon; + /* + * Before OTP-21, monitor_node(Node, false) triggered + * auto-connect and a 'nodedown' message if that failed. + * Now it's a simple no-op which feels more reasonable. + */ + mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(p), Node); + if (mon) { + ErtsMonitorDataExtended *mdep; + ASSERT(erts_monitor_is_origin(mon)); + + mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon); + + ASSERT((mdep->u.refc > 0)); + if (--mdep->u.refc == 0) { + if (!mdep->uptr.node_monitors) + erts_monitor_tree_delete(&ERTS_P_MONITORS(p), mon); + else { + ErtsMonitor *sub_mon; + ErtsMonitorDataExtended *sub_mdep; + sub_mon = erts_monitor_list_last(mdep->uptr.node_monitors); + erts_monitor_list_delete(&mdep->uptr.node_monitors, sub_mon); + sub_mon->flags &= ~ERTS_ML_FLG_IN_SUBTABLE; + sub_mdep = ((ErtsMonitorDataExtended *) + erts_monitor_to_data(sub_mon)); + sub_mdep->uptr.node_monitors = mdep->uptr.node_monitors; + mdep->uptr.node_monitors = NULL; + erts_monitor_tree_replace(&ERTS_P_MONITORS(p), mon, sub_mon); + } + if (erts_monitor_dist_delete(&mdep->md.target)) + erts_monitor_release_both((ErtsMonitorData *) mdep); + else + erts_monitor_release(mon); + } + } + break; } - if (Bool == am_true) { + case am_true: { ErtsDSigData dsd; dsd.node = Node; + dep = erts_find_or_insert_dist_entry(Node); if (dep == erts_this_dist_entry) - goto done; - - erts_proc_lock(p, ERTS_PROC_LOCK_LINK); + break; switch (erts_dsig_prepare(&dsd, dep, p, - (ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_LINK), + ERTS_PROC_LOCK_MAIN, ERTS_DSP_RLOCK, 0, async_connect)) { case ERTS_DSIG_PREP_NOT_ALIVE: case ERTS_DSIG_PREP_NOT_CONNECTED: /* Trap to either send 'nodedown' or do passive connection attempt */ - trap: - erts_proc_unlock(p, ERTS_PROC_LOCK_LINK); - erts_deref_dist_entry(dep); - BIF_TRAP3(dmonitor_node_trap, p, Node, Bool, Options); + goto do_trap; case ERTS_DSIG_PREP_PENDING: if (!async_connect) { /* @@ -3874,67 +3842,87 @@ monitor_node(Process* p, Eterm Node, Eterm Bool, Eterm Options) * to ensure passive connection attempt */ erts_de_runlock(dep); - goto trap; + goto do_trap; } /*fall through*/ - case ERTS_DSIG_PREP_CONNECTED: - erts_de_links_lock(dep); - erts_de_runlock(dep); - lnk = erts_add_or_lookup_link(&(dep->node_links), LINK_NODE, - p->common.id); - ++ERTS_LINK_REFC(lnk); - lnk = erts_add_or_lookup_link(&ERTS_P_LINKS(p), LINK_NODE, Node); - ++ERTS_LINK_REFC(lnk); - erts_de_links_unlock(dep); + case ERTS_DSIG_PREP_CONNECTED: { + ErtsMonitor *mon; + ErtsMonitorDataExtended *mdep; + int created; + + mon = erts_monitor_tree_lookup_create(&ERTS_P_MONITORS(p), + &created, + ERTS_MON_TYPE_NODE, + p->common.id, + Node); + mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon); + if (created) { +#ifdef DEBUG + int inserted = +#endif + erts_monitor_dist_insert(&mdep->md.target, dep->mld); + ASSERT(inserted); + ASSERT(mdep->dist->connection_id == dep->connection_id); + } + else if (mdep->dist->connection_id != dep->connection_id) { + ErtsMonitorDataExtended *mdep2; + ErtsMonitor *mon2; +#ifdef DEBUG + int inserted; +#endif + mdep2 = ((ErtsMonitorDataExtended *) + erts_monitor_create(ERTS_MON_TYPE_NODE, NIL, + p->common.id, Node, NIL)); + mon2 = &mdep2->md.origin; +#ifdef DEBUG + inserted = +#endif + erts_monitor_dist_insert(&mdep->md.target, dep->mld); + ASSERT(inserted); + ASSERT(mdep2->dist->connection_id == dep->connection_id); + + mdep2->uptr.node_monitors = mdep->uptr.node_monitors; + mdep->uptr.node_monitors = NULL; + erts_monitor_tree_replace(&ERTS_P_MONITORS(p), mon, mon2); + erts_monitor_list_insert(&mdep2->uptr.node_monitors, mon); + mon->flags |= ERTS_ML_FLG_IN_SUBTABLE; + mdep = mdep2; + } + + mdep->u.refc++; + break; + } + default: ERTS_ASSERT(! "Invalid dsig prepare result"); } - erts_deref_dist_entry(dep); - } - else { /* Bool == false */ - dep = erts_sysname_to_connected_dist_entry(Node); - if (!dep) { - /* - * Before OTP-21 this case triggered auto-connect - * and a 'nodedown' message if that failed. - * Now it's a simple no-op which feels more reasonable. - */ - BIF_RET(am_true); - } - if (dep == erts_this_dist_entry) - goto done; - erts_proc_lock(p, ERTS_PROC_LOCK_LINK); - erts_de_rlock(dep); - if (!(dep->status & (ERTS_DE_SFLG_PENDING | ERTS_DE_SFLG_CONNECTED))) { - erts_proc_unlock(p, ERTS_PROC_LOCK_LINK); - erts_de_runlock(dep); - goto done; - } - erts_de_links_lock(dep); erts_de_runlock(dep); - lnk = erts_lookup_link(dep->node_links, p->common.id); - if (lnk != NULL) { - if ((--ERTS_LINK_REFC(lnk)) == 0) { - erts_destroy_link(erts_remove_link(&(dep->node_links), - p->common.id)); - } - } - lnk = erts_lookup_link(ERTS_P_LINKS(p), Node); - if (lnk != NULL) { - if ((--ERTS_LINK_REFC(lnk)) == 0) { - erts_destroy_link(erts_remove_link(&ERTS_P_LINKS(p), - Node)); - } - } - erts_de_links_unlock(dep); + + break; } - erts_proc_unlock(p, ERTS_PROC_LOCK_LINK); + default: + goto badarg; + } - done: - BIF_RET(am_true); + ERTS_BIF_PREP_RET(ret, am_true); + +do_return: + + if (dep) + erts_deref_dist_entry(dep); + + return ret; + +do_trap: + ERTS_BIF_PREP_TRAP3(ret, dmonitor_node_trap, p, Node, Bool, Options); + goto do_return; + +badarg: + ERTS_BIF_PREP_ERROR(ret, p, BADARG); + goto do_return; } BIF_RETTYPE monitor_node_3(BIF_ALIST_3) @@ -3985,18 +3973,9 @@ BIF_RETTYPE net_kernel_dflag_unicode_io_1(BIF_ALIST_1) #define ERTS_NODES_MON_OPT_TYPES \ (ERTS_NODES_MON_OPT_TYPE_VISIBLE|ERTS_NODES_MON_OPT_TYPE_HIDDEN) -typedef struct ErtsNodesMonitor_ ErtsNodesMonitor; -struct ErtsNodesMonitor_ { - ErtsNodesMonitor *prev; - ErtsNodesMonitor *next; - Process *proc; - Uint16 opts; - Uint16 no; -}; - static erts_mtx_t nodes_monitors_mtx; -static ErtsNodesMonitor *nodes_monitors; -static ErtsNodesMonitor *nodes_monitors_end; +static ErtsMonitor *nodes_monitors; +static Uint no_nodes_monitors; /* * Nodes monitors are stored in a double linked list. 'nodes_monitors' @@ -4015,109 +3994,169 @@ init_nodes_monitors(void) erts_mtx_init(&nodes_monitors_mtx, "nodes_monitors", NIL, ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION); nodes_monitors = NULL; - nodes_monitors_end = NULL; + no_nodes_monitors = 0; } -static ERTS_INLINE Uint -nodes_mon_msg_sz(ErtsNodesMonitor *nmp, Eterm what, Eterm reason) +Eterm +erts_monitor_nodes(Process *c_p, Eterm on, Eterm olist) { - Uint sz; - if (!nmp->opts) { - sz = 3; - } - else { - sz = 0; + Eterm key, old_value, opts_list = olist; + Uint opts = (Uint) 0; + + ASSERT(c_p); + ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN); - if (nmp->opts & ERTS_NODES_MON_OPT_TYPES) - sz += 2 + 3; + if (on != am_true && on != am_false) + return THE_NON_VALUE; - if (what == am_nodedown - && (nmp->opts & ERTS_NODES_MON_OPT_DOWN_REASON)) { - if (is_not_immed(reason)) - sz += size_object(reason); - sz += 2 + 3; + if (is_not_nil(opts_list)) { + int all = 0, visible = 0, hidden = 0; + + while (is_list(opts_list)) { + Eterm *cp = list_val(opts_list); + Eterm opt = CAR(cp); + opts_list = CDR(cp); + if (opt == am_nodedown_reason) + opts |= ERTS_NODES_MON_OPT_DOWN_REASON; + else if (is_tuple(opt)) { + Eterm* tp = tuple_val(opt); + if (arityval(tp[0]) != 2) + return THE_NON_VALUE; + switch (tp[1]) { + case am_node_type: + switch (tp[2]) { + case am_visible: + if (hidden || all) + return THE_NON_VALUE; + opts |= ERTS_NODES_MON_OPT_TYPE_VISIBLE; + visible = 1; + break; + case am_hidden: + if (visible || all) + return THE_NON_VALUE; + opts |= ERTS_NODES_MON_OPT_TYPE_HIDDEN; + hidden = 1; + break; + case am_all: + if (visible || hidden) + return THE_NON_VALUE; + opts |= ERTS_NODES_MON_OPT_TYPES; + all = 1; + break; + default: + return THE_NON_VALUE; + } + break; + default: + return THE_NON_VALUE; + } + } + else { + return THE_NON_VALUE; + } } - sz += 4; + if (is_not_nil(opts_list)) + return THE_NON_VALUE; + } + + key = make_small(opts); + + if (on == am_true) { + ErtsMonitorDataExtended *mdep; + ErtsMonitor *omon; + int created; + omon = erts_monitor_tree_lookup_create(&ERTS_P_MONITORS(c_p), + &created, + ERTS_MON_TYPE_NODES, + c_p->common.id, + key); + mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(omon); + if (created) { + erts_mtx_lock(&nodes_monitors_mtx); + no_nodes_monitors++; + erts_monitor_list_insert(&nodes_monitors, &mdep->md.target); + erts_mtx_unlock(&nodes_monitors_mtx); + } + old_value = mdep->u.refc; + mdep->u.refc++; + } + else { + ErtsMonitorDataExtended *mdep; + ErtsMonitor *omon; + omon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p), key); + if (!omon) + old_value = 0; + else { + mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(omon); + old_value = mdep->u.refc; + ASSERT(mdep->u.refc > 0); + erts_mtx_lock(&nodes_monitors_mtx); + ASSERT(no_nodes_monitors > 0); + no_nodes_monitors--; + ASSERT(erts_monitor_is_in_table(&mdep->md.target)); + erts_monitor_list_delete(&nodes_monitors, &mdep->md.target); + erts_mtx_unlock(&nodes_monitors_mtx); + erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), omon); + erts_monitor_release_both((ErtsMonitorData *) mdep); + } } - return sz; + + return erts_make_integer(old_value, c_p); } -static ERTS_INLINE void -send_nodes_mon_msg(Process *rp, - ErtsProcLocks *rp_locksp, - ErtsNodesMonitor *nmp, - Eterm node, - Eterm what, - Eterm type, - Eterm reason, - Uint sz) +void +erts_monitor_nodes_delete(ErtsMonitor *omon) { - Eterm msg; - Eterm *hp; - ErtsMessage *mp; - ErlOffHeap *ohp; -#ifdef DEBUG - Eterm *hend; -#endif + ErtsMonitorData *mdp; - mp = erts_alloc_message_heap(rp, rp_locksp, sz, &hp, &ohp); -#ifdef DEBUG - hend = hp + sz; -#endif + ASSERT(omon->type == ERTS_MON_TYPE_NODES); + ASSERT(erts_monitor_is_origin(omon)); - if (!nmp->opts) { - msg = TUPLE2(hp, what, node); -#ifdef DEBUG - hp += 3; -#endif - } - else { - Eterm tup; - Eterm info = NIL; + mdp = erts_monitor_to_data(omon); - if (nmp->opts & (ERTS_NODES_MON_OPT_TYPE_VISIBLE - | ERTS_NODES_MON_OPT_TYPE_HIDDEN)) { + erts_mtx_lock(&nodes_monitors_mtx); + ASSERT(erts_monitor_is_in_table(&mdp->target)); + ASSERT(no_nodes_monitors > 0); + no_nodes_monitors--; + erts_monitor_list_delete(&nodes_monitors, &mdp->target); + erts_mtx_unlock(&nodes_monitors_mtx); + erts_monitor_release_both(mdp); +} - tup = TUPLE2(hp, am_node_type, type); - hp += 3; - info = CONS(hp, tup, info); - hp += 2; - } +typedef struct { + Eterm pid; + Eterm options; +} ErtsNodesMonitorData; - if (what == am_nodedown - && (nmp->opts & ERTS_NODES_MON_OPT_DOWN_REASON)) { - Eterm rsn_cpy; - - if (is_immed(reason)) - rsn_cpy = reason; - else { - Eterm rsn_sz = size_object(reason); - rsn_cpy = copy_struct(reason, rsn_sz, &hp, ohp); - } +typedef struct { + ErtsNodesMonitorData *nmdp; + Uint i; +} ErtsNodesMonitorContext; - tup = TUPLE2(hp, am_nodedown_reason, rsn_cpy); - hp += 3; - info = CONS(hp, tup, info); - hp += 2; - } +static void +save_nodes_monitor(ErtsMonitor *mon, void *vctxt) +{ + ErtsNodesMonitorContext *ctxt = vctxt; + ErtsMonitorData *mdp = erts_monitor_to_data(mon); - msg = TUPLE3(hp, what, node, info); -#ifdef DEBUG - hp += 4; -#endif - } + ASSERT(erts_monitor_is_target(mon)); + ASSERT(mon->type == ERTS_MON_TYPE_NODES); - ASSERT(hend == hp); - erts_queue_message(rp, *rp_locksp, mp, msg, am_system); + ctxt->nmdp[ctxt->i].pid = mon->other.item; + ctxt->nmdp[ctxt->i].options = mdp->origin.other.item; + + ctxt->i++; } static void send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reason) { - ErtsNodesMonitor *nmp; - ErtsProcLocks rp_locks = 0; /* Init to shut up false warning */ - Process *rp = NULL; + Uint opts; + Uint i, no, reason_size; + ErtsNodesMonitorData def_buf[100]; + ErtsNodesMonitorData *nmdp = &def_buf[0]; + ErtsNodesMonitorContext ctxt; ASSERT(is_immed(what)); ASSERT(is_immed(node)); @@ -4138,31 +4177,44 @@ send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reas } #endif - ERTS_LC_ASSERT(!c_p - || (erts_proc_lc_my_proc_locks(c_p) - == ERTS_PROC_LOCK_MAIN)); + ctxt.i = 0; + + reason_size = is_immed(reason) ? 0 : size_object(reason); + erts_mtx_lock(&nodes_monitors_mtx); + if (no_nodes_monitors > sizeof(def_buf)/sizeof(def_buf[0])) + nmdp = erts_alloc(ERTS_ALC_T_TMP, + no_nodes_monitors*sizeof(ErtsNodesMonitorData)); + ctxt.nmdp = nmdp; + erts_monitor_list_foreach(nodes_monitors, + save_nodes_monitor, + (void *) &ctxt); + + ASSERT(ctxt.i == no_nodes_monitors); + no = no_nodes_monitors; - for (nmp = nodes_monitors; nmp; nmp = nmp->next) { - int i; - Uint16 no; - Uint sz; + erts_mtx_unlock(&nodes_monitors_mtx); - ASSERT(nmp->proc != NULL); + for (i = 0; i < no; i++) { + Eterm tmp_heap[3+2+3+2+4 /* max need */]; + Eterm *hp, msg; + Uint hsz; - if (!nmp->opts) { + ASSERT(is_small(nmdp[i].options)); + opts = (Uint) signed_val(nmdp[i].options); + if (!opts) { if (type != am_visible) continue; } else { switch (type) { case am_hidden: - if (!(nmp->opts & ERTS_NODES_MON_OPT_TYPE_HIDDEN)) + if (!(opts & ERTS_NODES_MON_OPT_TYPE_HIDDEN)) continue; break; case am_visible: - if ((nmp->opts & ERTS_NODES_MON_OPT_TYPES) - && !(nmp->opts & ERTS_NODES_MON_OPT_TYPE_VISIBLE)) + if ((opts & ERTS_NODES_MON_OPT_TYPES) + && !(opts & ERTS_NODES_MON_OPT_TYPE_VISIBLE)) continue; break; default: @@ -4170,342 +4222,162 @@ send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reas } } - if (rp != nmp->proc) { - if (rp) { - if (rp == c_p) - rp_locks &= ~ERTS_PROC_LOCK_MAIN; - erts_proc_unlock(rp, rp_locks); - } - - rp = nmp->proc; - rp_locks = 0; - if (rp == c_p) - rp_locks |= ERTS_PROC_LOCK_MAIN; - } - - ASSERT(rp); + hsz = 0; + hp = &tmp_heap[0]; - sz = nodes_mon_msg_sz(nmp, what, reason); + if (!opts) { + msg = TUPLE2(hp, what, node); + hp += 3; + } + else { + Eterm tup; + Eterm info = NIL; - for (i = 0, no = nmp->no; i < no; i++) - send_nodes_mon_msg(rp, - &rp_locks, - nmp, - node, - what, - type, - reason, - sz); - } + if (opts & (ERTS_NODES_MON_OPT_TYPE_VISIBLE + | ERTS_NODES_MON_OPT_TYPE_HIDDEN)) { - if (rp) { - if (rp == c_p) - rp_locks &= ~ERTS_PROC_LOCK_MAIN; - erts_proc_unlock(rp, rp_locks); - } - - erts_mtx_unlock(&nodes_monitors_mtx); -} + tup = TUPLE2(hp, am_node_type, type); + hp += 3; + info = CONS(hp, tup, info); + hp += 2; + } -static Eterm -insert_nodes_monitor(Process *c_p, Uint32 opts) -{ - Uint16 no = 1; - Eterm res = am_false; - ErtsNodesMonitor *xnmp, *nmp; + if (what == am_nodedown + && (opts & ERTS_NODES_MON_OPT_DOWN_REASON)) { + hsz += reason_size; + tup = TUPLE2(hp, am_nodedown_reason, reason); + hp += 3; + info = CONS(hp, tup, info); + hp += 2; + } - ERTS_LC_ASSERT(erts_lc_mtx_is_locked(&nodes_monitors_mtx)); - ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN); + msg = TUPLE3(hp, what, node, info); + hp += 4; + } - xnmp = c_p->nodes_monitors; - if (xnmp) { - ASSERT(!xnmp->prev || xnmp->prev->proc != c_p); + ASSERT(hp - &tmp_heap[0] <= sizeof(tmp_heap)/sizeof(tmp_heap[0])); - while (1) { - ASSERT(xnmp->proc == c_p); - if (xnmp->opts == opts) - break; - if (!xnmp->next || xnmp->next->proc != c_p) - break; - xnmp = xnmp->next; - } - ASSERT(xnmp); - ASSERT(xnmp->proc == c_p); - ASSERT(xnmp->opts == opts - || !xnmp->next - || xnmp->next->proc != c_p); - - if (xnmp->opts != opts) - goto alloc_new; - else { - res = am_true; - no = xnmp->no++; - if (!xnmp->no) { - /* - * 'no' wrapped; transfer all prevous monitors to new - * element (which will be the next element in the list) - * and set this to one... - */ - xnmp->no = 1; - goto alloc_new; - } - } - } - else { - alloc_new: - nmp = erts_alloc(ERTS_ALC_T_NODES_MON, sizeof(ErtsNodesMonitor)); - nmp->proc = c_p; - nmp->opts = opts; - nmp->no = no; - - if (xnmp) { - ASSERT(nodes_monitors); - ASSERT(c_p->nodes_monitors); - nmp->next = xnmp->next; - nmp->prev = xnmp; - xnmp->next = nmp; - if (nmp->next) { - ASSERT(nodes_monitors_end != xnmp); - ASSERT(nmp->next->prev == xnmp); - nmp->next->prev = nmp; - } - else { - ASSERT(nodes_monitors_end == xnmp); - nodes_monitors_end = nmp; - } - } - else { - ASSERT(!c_p->nodes_monitors); - c_p->nodes_monitors = nmp; - nmp->next = NULL; - nmp->prev = nodes_monitors_end; - if (nodes_monitors_end) { - ASSERT(nodes_monitors); - nodes_monitors_end->next = nmp; - } - else { - ASSERT(!nodes_monitors); - nodes_monitors = nmp; - } - nodes_monitors_end = nmp; - } - } - return res; -} + hsz += hp - &tmp_heap[0]; -static Eterm -remove_nodes_monitors(Process *c_p, Uint32 opts, int all) -{ - Eterm res = am_false; - ErtsNodesMonitor *nmp; - - ERTS_LC_ASSERT(erts_lc_mtx_is_locked(&nodes_monitors_mtx)); - ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) & ERTS_PROC_LOCK_MAIN); - - nmp = c_p->nodes_monitors; - ASSERT(!nmp || !nmp->prev || nmp->prev->proc != c_p); - - while (nmp && nmp->proc == c_p) { - if (!all && nmp->opts != opts) - nmp = nmp->next; - else { /* if (all || nmp->opts == opts) */ - ErtsNodesMonitor *free_nmp; - res = am_true; - if (nmp->prev) { - ASSERT(nodes_monitors != nmp); - nmp->prev->next = nmp->next; - } - else { - ASSERT(nodes_monitors == nmp); - nodes_monitors = nmp->next; - } - if (nmp->next) { - ASSERT(nodes_monitors_end != nmp); - nmp->next->prev = nmp->prev; - } - else { - ASSERT(nodes_monitors_end == nmp); - nodes_monitors_end = nmp->prev; - } - free_nmp = nmp; - nmp = nmp->next; - if (c_p->nodes_monitors == free_nmp) - c_p->nodes_monitors = nmp && nmp->proc == c_p ? nmp : NULL; - erts_free(ERTS_ALC_T_NODES_MON, free_nmp); - } + erts_proc_sig_send_persistent_monitor_msg(ERTS_MON_TYPE_NODES, + nmdp[i].options, + am_system, + nmdp[i].pid, + msg, + hsz); } - - ASSERT(!all || !c_p->nodes_monitors); - return res; -} -void -erts_delete_nodes_monitors(Process *c_p, ErtsProcLocks locks) -{ -#if defined(ERTS_ENABLE_LOCK_CHECK) - if (c_p) { - ErtsProcLocks might_unlock = locks & ~ERTS_PROC_LOCK_MAIN; - if (might_unlock) - erts_proc_lc_might_unlock(c_p, might_unlock); - } -#endif - if (erts_mtx_trylock(&nodes_monitors_mtx) == EBUSY) { - ErtsProcLocks unlock_locks = locks & ~ERTS_PROC_LOCK_MAIN; - if (c_p && unlock_locks) - erts_proc_unlock(c_p, unlock_locks); - erts_mtx_lock(&nodes_monitors_mtx); - if (c_p && unlock_locks) - erts_proc_lock(c_p, unlock_locks); - } - remove_nodes_monitors(c_p, 0, 1); - erts_mtx_unlock(&nodes_monitors_mtx); + if (nmdp != &def_buf[0]) + erts_free(ERTS_ALC_T_TMP, nmdp); } + -Eterm -erts_monitor_nodes(Process *c_p, Eterm on, Eterm olist) -{ +typedef struct { + Eterm **hpp; + Uint *szp; Eterm res; - Eterm opts_list = olist; - Uint16 opts = (Uint16) 0; - - ASSERT(c_p); - ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN); - - if (on != am_true && on != am_false) - return THE_NON_VALUE; - - if (is_not_nil(opts_list)) { - int all = 0, visible = 0, hidden = 0; - - while (is_list(opts_list)) { - Eterm *cp = list_val(opts_list); - Eterm opt = CAR(cp); - opts_list = CDR(cp); - if (opt == am_nodedown_reason) - opts |= ERTS_NODES_MON_OPT_DOWN_REASON; - else if (is_tuple(opt)) { - Eterm* tp = tuple_val(opt); - if (arityval(tp[0]) != 2) - return THE_NON_VALUE; - switch (tp[1]) { - case am_node_type: - switch (tp[2]) { - case am_visible: - if (hidden || all) - return THE_NON_VALUE; - opts |= ERTS_NODES_MON_OPT_TYPE_VISIBLE; - visible = 1; - break; - case am_hidden: - if (visible || all) - return THE_NON_VALUE; - opts |= ERTS_NODES_MON_OPT_TYPE_HIDDEN; - hidden = 1; - break; - case am_all: - if (visible || hidden) - return THE_NON_VALUE; - opts |= ERTS_NODES_MON_OPT_TYPES; - all = 1; - break; - default: - return THE_NON_VALUE; - } - break; - default: - return THE_NON_VALUE; - } - } - else { - return THE_NON_VALUE; - } - } - - if (is_not_nil(opts_list)) - return THE_NON_VALUE; - } +} ErtsNodesMonitorInfoContext; - erts_mtx_lock(&nodes_monitors_mtx); - - if (on == am_true) - res = insert_nodes_monitor(c_p, opts); - else - res = remove_nodes_monitors(c_p, opts, 0); - erts_mtx_unlock(&nodes_monitors_mtx); - - return res; +static void +nodes_monitor_info(ErtsMonitor *mon, void *vctxt) +{ + ErtsMonitorDataExtended *mdep; + ErtsNodesMonitorInfoContext *ctxt = vctxt; + Uint no, i, opts, *szp; + Eterm **hpp, res; + + hpp = ctxt->hpp; + szp = ctxt->szp; + res = ctxt->res; + + ASSERT(erts_monitor_is_target(mon)); + ASSERT(mon->type == ERTS_MON_TYPE_NODES); + mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon); + no = mdep->u.refc; + + ASSERT(is_small(mdep->md.origin.other.item)); + opts = (Uint) signed_val(mdep->md.origin.other.item); + + for (i = 0; i < no; i++) { + Eterm olist = NIL; + if (opts & ERTS_NODES_MON_OPT_TYPES) { + Eterm type; + switch (opts & ERTS_NODES_MON_OPT_TYPES) { + case ERTS_NODES_MON_OPT_TYPES: type = am_all; break; + case ERTS_NODES_MON_OPT_TYPE_VISIBLE: type = am_visible; break; + case ERTS_NODES_MON_OPT_TYPE_HIDDEN: type = am_hidden; break; + default: erts_exit(ERTS_ABORT_EXIT, "Bad node type found\n"); + } + olist = erts_bld_cons(hpp, szp, + erts_bld_tuple(hpp, szp, 2, + am_node_type, + type), + olist); + } + if (opts & ERTS_NODES_MON_OPT_DOWN_REASON) + olist = erts_bld_cons(hpp, szp, am_nodedown_reason, olist); + res = erts_bld_cons(hpp, szp, + erts_bld_tuple(hpp, szp, 2, + mon->other.item, + olist), + res); + } + + ctxt->hpp = hpp; + ctxt->szp = szp; + ctxt->res = res; } -/* - * Note, this function is only used for debuging. - */ - Eterm erts_processes_monitoring_nodes(Process *c_p) { - ErtsNodesMonitor *nmp; - Eterm res; + /* + * Note, this function is only used for debugging. + */ + ErtsNodesMonitorInfoContext ctxt; Eterm *hp; - Eterm **hpp; Uint sz; - Uint *szp; #ifdef DEBUG Eterm *hend; #endif ASSERT(c_p); ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN); + + erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); + erts_thr_progress_block(); + erts_mtx_lock(&nodes_monitors_mtx); sz = 0; - szp = &sz; - hpp = NULL; + ctxt.szp = &sz; + ctxt.hpp = NULL; - bld_result: - res = NIL; - - for (nmp = nodes_monitors_end; nmp; nmp = nmp->prev) { - Uint16 i; - for (i = 0; i < nmp->no; i++) { - Eterm olist = NIL; - if (nmp->opts & ERTS_NODES_MON_OPT_TYPES) { - Eterm type; - switch (nmp->opts & ERTS_NODES_MON_OPT_TYPES) { - case ERTS_NODES_MON_OPT_TYPES: type = am_all; break; - case ERTS_NODES_MON_OPT_TYPE_VISIBLE: type = am_visible; break; - case ERTS_NODES_MON_OPT_TYPE_HIDDEN: type = am_hidden; break; - default: erts_exit(ERTS_ABORT_EXIT, "Bad node type found\n"); - } - olist = erts_bld_cons(hpp, szp, - erts_bld_tuple(hpp, szp, 2, - am_node_type, - type), - olist); - } - if (nmp->opts & ERTS_NODES_MON_OPT_DOWN_REASON) - olist = erts_bld_cons(hpp, szp, am_nodedown_reason, olist); - res = erts_bld_cons(hpp, szp, - erts_bld_tuple(hpp, szp, 2, - nmp->proc->common.id, - olist), - res); - } - } + while (1) { + ctxt.res = NIL; + + erts_monitor_list_foreach(nodes_monitors, + nodes_monitor_info, + (void *) &ctxt); + + if (ctxt.hpp) + break; - if (!hpp) { hp = HAlloc(c_p, sz); #ifdef DEBUG hend = hp + sz; #endif - hpp = &hp; - szp = NULL; - goto bld_result; + ctxt.hpp = &hp; + ctxt.szp = NULL; } ASSERT(hp == hend); erts_mtx_unlock(&nodes_monitors_mtx); - return res; + erts_thr_progress_unblock(); + erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); + + return ctxt.res; } diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h index ea4697815f..dda2029a4c 100644 --- a/erts/emulator/beam/dist.h +++ b/erts/emulator/beam/dist.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2017. All Rights Reserved. + * Copyright Ericsson AB 1996-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,26 +40,54 @@ #define DFLAG_UNICODE_IO 0x1000 #define DFLAG_DIST_HDR_ATOM_CACHE 0x2000 #define DFLAG_SMALL_ATOM_TAGS 0x4000 -#define DFLAG_INTERNAL_TAGS 0x8000 +#define DFLAG_INTERNAL_TAGS 0x8000 /* used by ETS 'compressed' option */ #define DFLAG_UTF8_ATOMS 0x10000 #define DFLAG_MAP_TAG 0x20000 #define DFLAG_BIG_CREATION 0x40000 #define DFLAG_SEND_SENDER 0x80000 -#define DFLAG_NO_MAGIC 0x100000 +#define DFLAG_BIG_SEQTRACE_LABELS 0x100000 +#define DFLAG_NO_MAGIC 0x200000 /* internal for pending connection */ -/* Mandatory flags for distribution (sync with dist_util.erl) */ +/* Mandatory flags for distribution */ #define DFLAG_DIST_MANDATORY (DFLAG_EXTENDED_REFERENCES \ | DFLAG_EXTENDED_PIDS_PORTS \ | DFLAG_UTF8_ATOMS \ | DFLAG_NEW_FUN_TAGS) -/* Additional optimistic flags when encoding toward pending connection */ -#define DFLAG_DIST_HOPEFULLY (DFLAG_NO_MAGIC \ - | DFLAG_EXPORT_PTR_TAG \ +/* + * Additional optimistic flags when encoding toward pending connection. + * If remote node (erl_interface) does not supporting these then we may need + * to transcode messages enqueued before connection setup was finished. + */ +#define DFLAG_DIST_HOPEFULLY (DFLAG_EXPORT_PTR_TAG \ | DFLAG_BIT_BINARIES \ | DFLAG_DIST_MONITOR \ | DFLAG_DIST_MONITOR_NAME) +/* Our preferred set of flags. Used for connection setup handshake */ +#define DFLAG_DIST_DEFAULT (DFLAG_DIST_MANDATORY | DFLAG_DIST_HOPEFULLY \ + | DFLAG_FUN_TAGS \ + | DFLAG_NEW_FLOATS \ + | DFLAG_UNICODE_IO \ + | DFLAG_DIST_HDR_ATOM_CACHE \ + | DFLAG_SMALL_ATOM_TAGS \ + | DFLAG_UTF8_ATOMS \ + | DFLAG_MAP_TAG \ + | DFLAG_BIG_CREATION \ + | DFLAG_SEND_SENDER \ + | DFLAG_BIG_SEQTRACE_LABELS) + +/* Flags addable by local distr implementations */ +#define DFLAG_DIST_ADDABLE DFLAG_DIST_DEFAULT + +/* Flags rejectable by local distr implementation */ +#define DFLAG_DIST_REJECTABLE (DFLAG_DIST_HDR_ATOM_CACHE \ + | DFLAG_HIDDEN_ATOM_CACHE \ + | DFLAG_ATOM_CACHE) + +/* Flags for all features needing strict order delivery */ +#define DFLAG_DIST_STRICT_ORDER DFLAG_DIST_HDR_ATOM_CACHE + /* All flags that should be enabled when term_to_binary/1 is used. */ #define TERM_TO_BINARY_DFLAGS (DFLAG_EXTENDED_REFERENCES \ | DFLAG_NEW_FUN_TAGS \ @@ -94,7 +122,6 @@ /* distribution trap functions */ extern Export* dmonitor_node_trap; -extern Export* dmonitor_p_trap; typedef enum { ERTS_DSP_NO_LOCK, @@ -111,14 +138,6 @@ typedef struct { int no_suspend; } ErtsDSigData; -#define ERTS_DE_IS_NOT_CONNECTED(DEP) \ - (ERTS_LC_ASSERT(erts_lc_rwmtx_is_rlocked(&(DEP)->rwmtx) \ - || erts_lc_rwmtx_is_rwlocked(&(DEP)->rwmtx)), \ - (is_nil((DEP)->cid) || ((DEP)->status & ERTS_DE_SFLG_EXITING))) - -#define ERTS_DE_IS_CONNECTED(DEP) \ - (!ERTS_DE_IS_NOT_CONNECTED((DEP))) - #define ERTS_DE_BUSY_LIMIT (1024*1024) extern int erts_dist_buf_busy_limit; extern int erts_is_alive; @@ -181,18 +200,18 @@ erts_dsig_prepare(ErtsDSigData *dsdp, retry: erts_de_rlock(dep); - if (ERTS_DE_IS_CONNECTED(dep)) { + if (dep->state == ERTS_DE_STATE_CONNECTED) { res = ERTS_DSIG_PREP_CONNECTED; } - else if (dep->status & ERTS_DE_SFLG_PENDING) { + else if (dep->state == ERTS_DE_STATE_PENDING) { res = ERTS_DSIG_PREP_PENDING; } - else if (dep->status & ERTS_DE_SFLG_EXITING) { + else if (dep->state == ERTS_DE_STATE_EXITING) { res = ERTS_DSIG_PREP_NOT_CONNECTED; goto fail; } else if (connect) { - ASSERT(dep->status == 0); + ASSERT(dep->state == ERTS_DE_STATE_IDLE); erts_de_runlock(dep); if (!erts_auto_connect(dep, proc, proc_locks)) { return ERTS_DSIG_PREP_NOT_ALIVE; @@ -200,7 +219,7 @@ retry: goto retry; } else { - ASSERT(dep->status == 0); + ASSERT(dep->state == ERTS_DE_STATE_IDLE); res = ERTS_DSIG_PREP_NOT_CONNECTED; goto fail; } @@ -256,58 +275,13 @@ void erts_schedule_dist_command(Port *prt, DistEntry *dist_entry) #endif -typedef struct { - ErtsLink *d_lnk; - ErtsLink *d_sub_lnk; -} ErtsDistLinkData; - -ERTS_GLB_INLINE void erts_remove_dist_link(ErtsDistLinkData *, - Eterm, - Eterm, - DistEntry *); -ERTS_GLB_INLINE int erts_was_dist_link_removed(ErtsDistLinkData *); -ERTS_GLB_INLINE void erts_destroy_dist_link(ErtsDistLinkData *); - -#if ERTS_GLB_INLINE_INCL_FUNC_DEF - -ERTS_GLB_INLINE void -erts_remove_dist_link(ErtsDistLinkData *dldp, - Eterm lid, - Eterm rid, - DistEntry *dep) -{ - erts_de_links_lock(dep); - dldp->d_lnk = erts_lookup_link(dep->nlinks, lid); - if (!dldp->d_lnk) - dldp->d_sub_lnk = NULL; - else { - dldp->d_sub_lnk = erts_remove_link(&ERTS_LINK_ROOT(dldp->d_lnk), rid); - dldp->d_lnk = (ERTS_LINK_ROOT(dldp->d_lnk) - ? NULL - : erts_remove_link(&dep->nlinks, lid)); - } - erts_de_links_unlock(dep); -} - -ERTS_GLB_INLINE int -erts_was_dist_link_removed(ErtsDistLinkData *dldp) -{ - return dldp->d_sub_lnk != NULL; -} - -ERTS_GLB_INLINE void -erts_destroy_dist_link(ErtsDistLinkData *dldp) -{ - if (dldp->d_lnk) - erts_destroy_link(dldp->d_lnk); - if (dldp->d_sub_lnk) - erts_destroy_link(dldp->d_sub_lnk); -} - +#ifdef DEBUG +#define ERTS_DBG_CHK_NO_DIST_LNK(D, R, L) \ + erts_dbg_chk_no_dist_proc_link((D), (R), (L)) +#else +#define ERTS_DBG_CHK_NO_DIST_LNK(D, R, L) #endif - - /* Define for testing */ /* #define EXTREME_TTB_TRAPPING 1 */ @@ -328,6 +302,7 @@ typedef struct TTBSizeContext_ { typedef struct TTBEncodeContext_ { Uint flags; + Uint hopefull_flags; int level; byte* ep; Eterm obj; diff --git a/erts/emulator/beam/erl_afit_alloc.c b/erts/emulator/beam/erl_afit_alloc.c index 4ebe37ee1d..23efe3bba4 100644 --- a/erts/emulator/beam/erl_afit_alloc.c +++ b/erts/emulator/beam/erl_afit_alloc.c @@ -181,7 +181,7 @@ static struct { static void ERTS_INLINE atom_init(Eterm *atom, char *name) { - *atom = am_atom_put(name, strlen(name)); + *atom = am_atom_put(name, sys_strlen(name)); } #define AM_INIT(AM) atom_init(&am.AM, #AM) diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c index 239dda6bcf..061b9df627 100644 --- a/erts/emulator/beam/erl_alloc.c +++ b/erts/emulator/beam/erl_alloc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2017. All Rights Reserved. + * Copyright Ericsson AB 2002-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,7 +40,7 @@ #include "erl_bits.h" #include "erl_instrument.h" #include "erl_mseg.h" -#include "erl_monitors.h" +#include "erl_monitor_link.h" #include "erl_hl_timer.h" #include "erl_cpu_topology.h" #include "erl_thr_queue.h" @@ -366,16 +366,9 @@ set_default_exec_alloc_opts(struct au_init *ip) ip->init.util.rmbcmt = 0; ip->init.util.acul = 0; -# ifdef ERTS_HAVE_EXEC_MMAPPER - ip->init.util.mseg_alloc = &erts_alcu_mmapper_mseg_alloc; - ip->init.util.mseg_realloc = &erts_alcu_mmapper_mseg_realloc; - ip->init.util.mseg_dealloc = &erts_alcu_mmapper_mseg_dealloc; - ip->init.util.mseg_mmapper = &erts_exec_mmapper; -# else ip->init.util.mseg_alloc = &erts_alcu_exec_mseg_alloc; ip->init.util.mseg_realloc = &erts_alcu_exec_mseg_realloc; ip->init.util.mseg_dealloc = &erts_alcu_exec_mseg_dealloc; -# endif } #endif /* ERTS_ALC_A_EXEC */ @@ -643,10 +636,10 @@ erts_alloc_init(int *argc, char **argv, ErtsAllocInitOpts *eaiop) fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_PROC)] = sizeof(Process); - fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MONITOR_SH)] - = ERTS_MONITOR_SH_SIZE * sizeof(Uint); - fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_NLINK_SH)] - = ERTS_LINK_SH_SIZE * sizeof(Uint); + fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_MONITOR)] + = sizeof(ErtsMonitorDataHeap); + fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_LINK)] + = sizeof(ErtsLinkData); fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_DRV_SEL_D_STATE)] = sizeof(ErtsDrvSelectDataState); fix_type_sizes[ERTS_ALC_FIX_TYPE_IX(ERTS_ALC_T_NIF_SEL_D_STATE)] @@ -1219,9 +1212,9 @@ get_bool_value(char *param_end, char** argv, int* ip) { char *param = argv[*ip]+1; char *value = get_value(param_end, argv, ip); - if (strcmp(value, "true") == 0) + if (sys_strcmp(value, "true") == 0) return 1; - else if (strcmp(value, "false") == 0) + else if (sys_strcmp(value, "false") == 0) return 0; else bad_value(param, param_end, value); @@ -1369,46 +1362,46 @@ handle_au_arg(struct au_init *auip, } else if(has_prefix("as", sub_param)) { char *alg = get_value(sub_param + 2, argv, ip); - if (strcmp("bf", alg) == 0) { + if (sys_strcmp("bf", alg) == 0) { auip->atype = BESTFIT; auip->init.bf.ao = 0; } - else if (strcmp("aobf", alg) == 0) { + else if (sys_strcmp("aobf", alg) == 0) { auip->atype = BESTFIT; auip->init.bf.ao = 1; } - else if (strcmp("gf", alg) == 0) { + else if (sys_strcmp("gf", alg) == 0) { auip->atype = GOODFIT; } - else if (strcmp("af", alg) == 0) { + else if (sys_strcmp("af", alg) == 0) { auip->atype = AFIT; } - else if (strcmp("aoff", alg) == 0) { + else if (sys_strcmp("aoff", alg) == 0) { auip->atype = FIRSTFIT; auip->init.aoff.crr_order = FF_AOFF; auip->init.aoff.blk_order = FF_AOFF; } - else if (strcmp("aoffcbf", alg) == 0) { + else if (sys_strcmp("aoffcbf", alg) == 0) { auip->atype = FIRSTFIT; auip->init.aoff.crr_order = FF_AOFF; auip->init.aoff.blk_order = FF_BF; } - else if (strcmp("aoffcaobf", alg) == 0) { + else if (sys_strcmp("aoffcaobf", alg) == 0) { auip->atype = FIRSTFIT; auip->init.aoff.crr_order = FF_AOFF; auip->init.aoff.blk_order = FF_AOBF; } - else if (strcmp("ageffcaoff", alg) == 0) { + else if (sys_strcmp("ageffcaoff", alg) == 0) { auip->atype = FIRSTFIT; auip->init.aoff.crr_order = FF_AGEFF; auip->init.aoff.blk_order = FF_AOFF; } - else if (strcmp("ageffcbf", alg) == 0) { + else if (sys_strcmp("ageffcbf", alg) == 0) { auip->atype = FIRSTFIT; auip->init.aoff.crr_order = FF_AGEFF; auip->init.aoff.blk_order = FF_BF; } - else if (strcmp("ageffcaobf", alg) == 0) { + else if (sys_strcmp("ageffcaobf", alg) == 0) { auip->atype = FIRSTFIT; auip->init.aoff.crr_order = FF_AGEFF; auip->init.aoff.blk_order = FF_AOBF; @@ -1568,10 +1561,8 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) break; case 'X': if (has_prefix("scs", argv[i]+3)) { -#ifdef ERTS_HAVE_EXEC_MMAPPER - init->mseg.exec_mmap.scs = -#endif - get_mb_value(argv[i]+6, argv, &i); + /* Ignore obsolete */ + (void) get_mb_value(argv[i]+6, argv, &i); } else handle_au_arg(&init->exec_alloc, &argv[i][3], argv, &i, 0); @@ -1688,7 +1679,7 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) } else if (has_prefix("e", param+2)) { arg = get_value(param+3, argv, &i); - if (strcmp("true", arg) != 0) + if (sys_strcmp("true", arg) != 0) bad_value(param, param+3, arg); } else @@ -1700,20 +1691,20 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) case 'a': { int a; arg = get_value(argv[i]+4, argv, &i); - if (strcmp("min", arg) == 0) { + if (sys_strcmp("min", arg) == 0) { for (a = 0; a < aui_sz; a++) aui[a]->enable = 0; } - else if (strcmp("max", arg) == 0) { + else if (sys_strcmp("max", arg) == 0) { for (a = 0; a < aui_sz; a++) aui[a]->enable = 1; } - else if (strcmp("config", arg) == 0) { + else if (sys_strcmp("config", arg) == 0) { init->erts_alloc_config = 1; } - else if (strcmp("r9c", arg) == 0 - || strcmp("r10b", arg) == 0 - || strcmp("r11b", arg) == 0) { + else if (sys_strcmp("r9c", arg) == 0 + || sys_strcmp("r10b", arg) == 0 + || sys_strcmp("r11b", arg) == 0) { set_default_sl_alloc_opts(&init->sl_alloc); set_default_std_alloc_opts(&init->std_alloc); set_default_ll_alloc_opts(&init->ll_alloc); @@ -1725,7 +1716,7 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) set_default_driver_alloc_opts(&init->fix_alloc); init->driver_alloc.enable = 0; - if (strcmp("r9c", arg) == 0) { + if (sys_strcmp("r9c", arg) == 0) { init->sl_alloc.enable = 0; init->std_alloc.enable = 0; init->binary_alloc.enable = 0; @@ -1752,18 +1743,18 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) switch (argv[i][3]) { case 's': arg = get_value(argv[i]+4, argv, &i); - if (strcmp("true", arg) == 0) + if (sys_strcmp("true", arg) == 0) init->instr.stat = 1; - else if (strcmp("false", arg) == 0) + else if (sys_strcmp("false", arg) == 0) init->instr.stat = 0; else bad_value(param, param+3, arg); break; case 'm': arg = get_value(argv[i]+4, argv, &i); - if (strcmp("true", arg) == 0) + if (sys_strcmp("true", arg) == 0) init->instr.map = 1; - else if (strcmp("false", arg) == 0) + else if (sys_strcmp("false", arg) == 0) init->instr.map = 0; else bad_value(param, param+3, arg); @@ -1778,9 +1769,9 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) case 'l': if (has_prefix("pm", param+2)) { arg = get_value(argv[i]+5, argv, &i); - if (strcmp("all", arg) == 0) + if (sys_strcmp("all", arg) == 0) lock_all_physical_memory = 1; - else if (strcmp("no", arg) == 0) + else if (sys_strcmp("no", arg) == 0) lock_all_physical_memory = 0; else bad_value(param, param+4, arg); @@ -1830,8 +1821,8 @@ handle_args(int *argc, char **argv, erts_alc_hndl_args_init_t *init) /* || init->instr.stat || init->instr.map */) { while (i < *argc) { - if(strcmp(argv[i], "-sname") == 0 - || strcmp(argv[i], "-name") == 0) { + if(sys_strcmp(argv[i], "-sname") == 0 + || sys_strcmp(argv[i], "-name") == 0) { if (i + 1 <*argc) { init->instr.nodename = argv[i+1]; break; @@ -2379,7 +2370,6 @@ erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg) } tmp += erts_ptab_mem_size(&erts_proc); tmp += erts_bif_timer_memory_size(); - tmp += erts_tot_link_lh_size(); size.processes = size.processes_used = tmp; @@ -2390,12 +2380,11 @@ erts_memory(fmtfn_t *print_to_p, void *print_to_arg, void *proc, Eterm earg) add_fix_values(&size.processes, &size.processes_used, fi, - ERTS_ALC_T_MONITOR_SH); - + ERTS_ALC_T_MONITOR); add_fix_values(&size.processes, &size.processes_used, fi, - ERTS_ALC_T_NLINK_SH); + ERTS_ALC_T_LINK); add_fix_values(&size.processes, &size.processes_used, fi, @@ -2626,11 +2615,6 @@ erts_allocated_areas(fmtfn_t *print_to_p, void *print_to_arg, void *proc) i++; values[i].arity = 2; - values[i].name = "link_lh"; - values[i].ui[0] = erts_tot_link_lh_size(); - i++; - - values[i].arity = 2; values[i].name = "process_table"; values[i].ui[0] = erts_ptab_mem_size(&erts_proc); i++; @@ -2692,7 +2676,7 @@ erts_allocated_areas(fmtfn_t *print_to_p, void *print_to_arg, void *proc) Eterm atom; if (hpp) atom = am_atom_put(values[i].name, - (int) strlen(values[i].name)); + (int) sys_strlen(values[i].name)); else atom = am_true; @@ -2833,10 +2817,6 @@ erts_allocator_info(fmtfn_t to, void *arg) erts_print(to, arg, "=allocator:erts_mmap.literal_mmap\n"); erts_mmap_info(&erts_literal_mmapper, &to, arg, NULL, NULL, &emis); #endif -#ifdef ERTS_HAVE_EXEC_MMAPPER - erts_print(to, arg, "=allocator:erts_mmap.exec_mmap\n"); - erts_mmap_info(&erts_exec_mmapper, &to, arg, NULL, NULL, &emis); -#endif } #endif @@ -2883,7 +2863,7 @@ erts_allocator_options(void *proc) for (a = ERTS_ALC_A_MIN; a <= ERTS_ALC_A_MAX; a++) { Eterm tmp = NIL; atoms[length] = am_atom_put((char *) ERTS_ALC_A2AD(a), - strlen(ERTS_ALC_A2AD(a))); + sys_strlen(ERTS_ALC_A2AD(a))); if (erts_allctrs_info[a].enabled) { if (erts_allctrs_info[a].alloc_util) { Allctr_t *allctr; @@ -2977,7 +2957,7 @@ erts_allocator_options(void *proc) for (a = ERTS_ALC_A_MIN; a <= ERTS_ALC_A_MAX; a++) { if (erts_allctrs_info[a].enabled) { terms[length++] = am_atom_put((char *) ERTS_ALC_A2AD(a), - strlen(ERTS_ALC_A2AD(a))); + sys_strlen(ERTS_ALC_A2AD(a))); } } @@ -2991,9 +2971,6 @@ erts_allocator_options(void *proc) #if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) terms[length++] = ERTS_MAKE_AM("literal_mmap"); #endif -#ifdef ERTS_HAVE_EXEC_MMAPPER - terms[length++] = ERTS_MAKE_AM("exec_mmap"); -#endif features = length ? erts_bld_list(hpp, szp, length, terms) : NIL; #if defined(__GLIBC__) @@ -3083,9 +3060,6 @@ reply_alloc_info(void *vair) # if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) struct erts_mmap_info_struct mmap_info_literal; # endif -# ifdef ERTS_HAVE_EXEC_MMAPPER - struct erts_mmap_info_struct mmap_info_exec; -# endif #endif int i; Eterm (*info_func)(Allctr_t *, @@ -3214,17 +3188,6 @@ reply_alloc_info(void *vair) erts_bld_atom(hpp,szp,"literal_mmap"), ainfo); # endif -# ifdef ERTS_HAVE_EXEC_MMAPPER - ai_list = erts_bld_cons(hpp, szp, - ainfo, ai_list); - ainfo = (air->only_sz ? NIL : - erts_mmap_info(&erts_exec_mmapper, NULL, NULL, - hpp, szp, &mmap_info_exec)); - ainfo = erts_bld_tuple3(hpp, szp, - alloc_atom, - erts_bld_atom(hpp,szp,"exec_mmap"), - ainfo); -# endif #else /* !HAVE_ERTS_MMAP */ ainfo = erts_bld_tuple2(hpp, szp, alloc_atom, am_false); @@ -3961,7 +3924,7 @@ set_memory_fence(void *ptr, Uint sz, ErtsAlcType_t n) *(ui_ptr++) = sz; *(ui_ptr++) = pattern; - memcpy((void *) (((char *) ui_ptr)+sz), (void *) &pattern, sizeof(UWord)); + sys_memcpy((void *) (((char *) ui_ptr)+sz), (void *) &pattern, sizeof(UWord)); #ifdef HARD_DEBUG *mblkpp = hdbg_alloc((void *) ui_ptr, sz, n); @@ -4001,7 +3964,7 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func) (UWord) ptr); } - memcpy((void *) &post_pattern, (void *) (((char *)ptr)+sz), sizeof(UWord)); + sys_memcpy((void *) &post_pattern, (void *) (((char *)ptr)+sz), sizeof(UWord)); if (post_pattern != MK_PATTERN(n) || pre_pattern != post_pattern) { diff --git a/erts/emulator/beam/erl_alloc.h b/erts/emulator/beam/erl_alloc.h index 174bf7a80a..578a3717d9 100644 --- a/erts/emulator/beam/erl_alloc.h +++ b/erts/emulator/beam/erl_alloc.h @@ -434,7 +434,7 @@ NAME##_free(TYPE *p) \ #ifdef DEBUG #define ERTS_PRE_ALLOC_SIZE(SZ) ((SZ) < 1000 ? (SZ)/10 + 10 : 100) -#define ERTS_PRE_ALLOC_CLOBBER(P, T) memset((void *) (P), 0xfd, sizeof(T)) +#define ERTS_PRE_ALLOC_CLOBBER(P, T) sys_memset((void *) (P), 0xfd, sizeof(T)) #else #define ERTS_PRE_ALLOC_SIZE(SZ) ((SZ) > 1 ? (SZ) : 1) #define ERTS_PRE_ALLOC_CLOBBER(P, T) diff --git a/erts/emulator/beam/erl_alloc.types b/erts/emulator/beam/erl_alloc.types index 8107f133aa..4a6a19b210 100644 --- a/erts/emulator/beam/erl_alloc.types +++ b/erts/emulator/beam/erl_alloc.types @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2017. All Rights Reserved. +# Copyright Ericsson AB 2003-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -282,6 +282,11 @@ type THR_PRGR_IDATA LONG_LIVED SYSTEM thr_prgr_internal_data type THR_PRGR_DATA LONG_LIVED SYSTEM thr_prgr_data type T_THR_PRGR_DATA SHORT_LIVED SYSTEM temp_thr_prgr_data type RELEASE_LAREA SHORT_LIVED SYSTEM release_literal_area +type SIG_DATA SHORT_LIVED PROCESSES signal_data +type DIST_DEMONITOR SHORT_LIVED PROCESSES dist_demonitor +type CML_CLEANUP SHORT_LIVED SYSTEM connection_ml_cleanup +type ML_YIELD_STATE SHORT_LIVED SYSTEM monitor_link_yield_state +type ML_DIST STANDARD SYSTEM monitor_link_dist type ENVIRONMENT SYSTEM SYSTEM environment @@ -326,8 +331,8 @@ type LCNT_VECTOR SHORT_LIVED SYSTEM lcnt_sample_vector type DEBUG SHORT_LIVED SYSTEM debugging type DDLL_PROCESS STANDARD SYSTEM ddll_processes -type MONITOR_LH STANDARD PROCESSES monitor_lh -type NLINK_LH STANDARD PROCESSES nlink_lh +type MONITOR_EXT STANDARD PROCESSES monitor_extended +type LINK_EXT STANDARD PROCESSES link_extended type CODE LONG_LIVED CODE code type LITERAL LITERAL CODE literal type LITERAL_REF SHORT_LIVED CODE literal_area_ref @@ -340,8 +345,8 @@ type LL_TEMP_TERM LONG_LIVED SYSTEM ll_temp_term type NIF_TRAP_EXPORT STANDARD PROCESSES nif_trap_export_entry type NIF_EXP_TRACE FIXED_SIZE PROCESSES nif_export_trace type EXPORT LONG_LIVED CODE export_entry -type MONITOR_SH FIXED_SIZE PROCESSES monitor_sh -type NLINK_SH FIXED_SIZE PROCESSES nlink_sh +type MONITOR FIXED_SIZE PROCESSES monitor +type LINK FIXED_SIZE PROCESSES link type AINFO_REQ SHORT_LIVED SYSTEM alloc_info_request type SCHED_WTIME_REQ SHORT_LIVED SYSTEM sched_wall_time_request type GC_INFO_REQ SHORT_LIVED SYSTEM gc_info_request diff --git a/erts/emulator/beam/erl_alloc_util.c b/erts/emulator/beam/erl_alloc_util.c index fa97ead908..e148be7af6 100644 --- a/erts/emulator/beam/erl_alloc_util.c +++ b/erts/emulator/beam/erl_alloc_util.c @@ -874,7 +874,7 @@ erts_alcu_literal_32_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, #elif defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) /* For allocators that have their own mmapper (super carrier), - * like literal_alloc and exec_alloc on amd64 + * like literal_alloc. */ void* erts_alcu_mmapper_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags) @@ -916,10 +916,10 @@ erts_alcu_mmapper_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, } #endif /* ARCH_64 && ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION */ -#if defined(ERTS_ALC_A_EXEC) && !defined(ERTS_HAVE_EXEC_MMAPPER) +#if defined(ERTS_ALC_A_EXEC) /* - * For exec_alloc on non-amd64 that just need memory with PROT_EXEC + * For exec_alloc that need memory with PROT_EXEC */ void* erts_alcu_exec_mseg_alloc(Allctr_t *allctr, Uint *size_p, Uint flags) @@ -958,7 +958,7 @@ erts_alcu_exec_mseg_dealloc(Allctr_t *allctr, void *seg, Uint size, Uint flags) ASSERT(r == 0); (void)r; erts_alcu_mseg_dealloc(allctr, seg, size, flags); } -#endif /* ERTS_ALC_A_EXEC && !ERTS_HAVE_EXEC_MMAPPER */ +#endif /* ERTS_ALC_A_EXEC */ #endif /* HAVE_ERTS_MSEG */ @@ -4315,7 +4315,7 @@ static Eterm fix_type_atoms[ERTS_ALC_NO_FIXED_SIZES]; static ERTS_INLINE void atom_init(Eterm *atom, char *name) { - *atom = am_atom_put(name, strlen(name)); + *atom = am_atom_put(name, sys_strlen(name)); } #define AM_INIT(AM) atom_init(&am.AM, #AM) @@ -4416,7 +4416,7 @@ init_atoms(Allctr_t *allctr) for (ix = 0; ix < ERTS_ALC_NO_FIXED_SIZES; ix++) { ErtsAlcType_t n = ERTS_ALC_N_MIN_A_FIXED_SIZE + ix; char *name = (char *) ERTS_ALC_N2TD(n); - size_t len = strlen(name); + size_t len = sys_strlen(name); fix_type_atoms[ix] = am_atom_put(name, len); } } @@ -4854,20 +4854,20 @@ make_name_atoms(Allctr_t *allctr) char realloc[] = "realloc"; char free[] = "free"; char buf[MAX_ATOM_CHARACTERS]; - size_t prefix_len = strlen(allctr->name_prefix); + size_t prefix_len = sys_strlen(allctr->name_prefix); if (prefix_len > MAX_ATOM_CHARACTERS + sizeof(realloc) - 1) erts_exit(ERTS_ERROR_EXIT,"Too long allocator name: %salloc\n",allctr->name_prefix); - memcpy((void *) buf, (void *) allctr->name_prefix, prefix_len); + sys_memcpy((void *) buf, (void *) allctr->name_prefix, prefix_len); - memcpy((void *) &buf[prefix_len], (void *) alloc, sizeof(alloc) - 1); + sys_memcpy((void *) &buf[prefix_len], (void *) alloc, sizeof(alloc) - 1); allctr->name.alloc = am_atom_put(buf, prefix_len + sizeof(alloc) - 1); - memcpy((void *) &buf[prefix_len], (void *) realloc, sizeof(realloc) - 1); + sys_memcpy((void *) &buf[prefix_len], (void *) realloc, sizeof(realloc) - 1); allctr->name.realloc = am_atom_put(buf, prefix_len + sizeof(realloc) - 1); - memcpy((void *) &buf[prefix_len], (void *) free, sizeof(free) - 1); + sys_memcpy((void *) &buf[prefix_len], (void *) free, sizeof(free) - 1); allctr->name.free = am_atom_put(buf, prefix_len + sizeof(free) - 1); } diff --git a/erts/emulator/beam/erl_alloc_util.h b/erts/emulator/beam/erl_alloc_util.h index 9a6de2bb75..05c8a0db3b 100644 --- a/erts/emulator/beam/erl_alloc_util.h +++ b/erts/emulator/beam/erl_alloc_util.h @@ -205,7 +205,7 @@ void* erts_alcu_mmapper_mseg_realloc(Allctr_t*, void *seg, Uint old_size, Uint * void erts_alcu_mmapper_mseg_dealloc(Allctr_t*, void *seg, Uint size, Uint flags); # endif -# if defined(ERTS_ALC_A_EXEC) && !defined(ERTS_HAVE_EXEC_MMAPPER) +# if defined(ERTS_ALC_A_EXEC) void* erts_alcu_exec_mseg_alloc(Allctr_t*, Uint *size_p, Uint flags); void* erts_alcu_exec_mseg_realloc(Allctr_t*, void *seg, Uint old_size, Uint *new_size_p); void erts_alcu_exec_mseg_dealloc(Allctr_t*, void *seg, Uint size, Uint flags); diff --git a/erts/emulator/beam/erl_ao_firstfit_alloc.c b/erts/emulator/beam/erl_ao_firstfit_alloc.c index f8a6101b95..0c5545401a 100644 --- a/erts/emulator/beam/erl_ao_firstfit_alloc.c +++ b/erts/emulator/beam/erl_ao_firstfit_alloc.c @@ -1054,7 +1054,7 @@ static struct { static void ERTS_INLINE atom_init(Eterm *atom, const char *name) { - *atom = am_atom_put(name, strlen(name)); + *atom = am_atom_put(name, sys_strlen(name)); } #define AM_INIT(AM) atom_init(&am.AM, #AM) diff --git a/erts/emulator/beam/erl_bestfit_alloc.c b/erts/emulator/beam/erl_bestfit_alloc.c index 6173c408e1..85fc4c3a85 100644 --- a/erts/emulator/beam/erl_bestfit_alloc.c +++ b/erts/emulator/beam/erl_bestfit_alloc.c @@ -875,7 +875,7 @@ static struct { static void ERTS_INLINE atom_init(Eterm *atom, char *name) { - *atom = am_atom_put(name, strlen(name)); + *atom = am_atom_put(name, sys_strlen(name)); } #define AM_INIT(AM) atom_init(&am.AM, #AM) diff --git a/erts/emulator/beam/erl_bif_binary.c b/erts/emulator/beam/erl_bif_binary.c index 33bc189182..469f6a1ea8 100644 --- a/erts/emulator/beam/erl_bif_binary.c +++ b/erts/emulator/beam/erl_bif_binary.c @@ -365,7 +365,7 @@ static ACTrie *create_acdata(MyAllocator *my, Uint len, acn->d = 0; acn->final = 0; acn->h = NULL; - memset(acn->g, 0, sizeof(ACNode *) * ALPHABET_SIZE); + sys_memset(acn->g, 0, sizeof(ACNode *) * ALPHABET_SIZE); #ifdef HARDDEBUG act->idc = 0; acn->id = 0; @@ -388,7 +388,7 @@ static BMData *create_bmdata(MyAllocator *my, byte *x, Uint len, init_my_allocator(my, datasize, data); bmd = my_alloc(my, sizeof(BMData)); bmd->x = my_alloc(my,len); - memcpy(bmd->x,x,len); + sys_memcpy(bmd->x,x,len); bmd->len = len; bmd->goodshift = my_alloc(my,sizeof(Uint) * len); *the_bin = mb; @@ -433,7 +433,7 @@ static void ac_add_one_pattern(MyAllocator *my, ACTrie *act, byte *x, Uint len) nn->d = i+1; nn->h = act->root; nn->final = 0; - memset(nn->g, 0, sizeof(ACNode *) * ALPHABET_SIZE); + sys_memset(nn->g, 0, sizeof(ACNode *) * ALPHABET_SIZE); acn->g[x[i]] = nn; ++i; acn = nn; @@ -2264,7 +2264,7 @@ static BIF_RETTYPE do_longest_common(Process *p, Eterm list, int direction) if (cd[i].type == CL_TYPE_HEAP_NOALLOC) { unsigned char *tmp = cd[i].buff; cd[i].buff = erts_alloc(ERTS_ALC_T_BINARY_BUFFER, cd[i].bufflen); - memcpy(cd[i].buff,tmp,cd[i].bufflen); + sys_memcpy(cd[i].buff,tmp,cd[i].bufflen); cd[i].type = CL_TYPE_HEAP; } } @@ -2556,7 +2556,7 @@ static BIF_RETTYPE do_binary_copy(Process *p, Eterm bin, Eterm en) 0); if (!(cbs->temp_alloc)) { /* alignment not needed, need to copy */ byte *tmp = erts_alloc(ERTS_ALC_T_BINARY_BUFFER,size); - memcpy(tmp,cbs->source,size); + sys_memcpy(tmp,cbs->source,size); cbs->source = tmp; cbs->source_type = BC_TYPE_HEAP; } else { @@ -2569,7 +2569,7 @@ static BIF_RETTYPE do_binary_copy(Process *p, Eterm bin, Eterm en) pos = 0; i = 0; while (pos < reds) { - memcpy(t+pos,cbs->source, size); + sys_memcpy(t+pos,cbs->source, size); pos += size; ++i; } @@ -2594,7 +2594,7 @@ static BIF_RETTYPE do_binary_copy(Process *p, Eterm bin, Eterm en) } pos = 0; while (pos < target_size) { - memcpy(t+pos,source, size); + sys_memcpy(t+pos,source, size); pos += size; } erts_free_aligned_binary_bytes(temp_alloc); @@ -2624,7 +2624,7 @@ BIF_RETTYPE binary_copy_trap(BIF_ALIST_2) if ((n-1) * size >= reds) { Uint i = 0; while ((pos - opos) < reds) { - memcpy(t+pos,cbs->source, size); + sys_memcpy(t+pos,cbs->source, size); pos += size; ++i; } @@ -2637,7 +2637,7 @@ BIF_RETTYPE binary_copy_trap(BIF_ALIST_2) Eterm resbin; Uint target_size = cbs->result->orig_size; while (pos < target_size) { - memcpy(t+pos,cbs->source, size); + sys_memcpy(t+pos,cbs->source, size); pos += size; } save = cbs->result; @@ -3029,7 +3029,7 @@ static void dump_bm_data(BMData *bm) static void dump_ac_node(ACNode *node, int indent, int ch) { int i; char *spaces = erts_alloc(ERTS_ALC_T_TMP, 10 * indent + 1); - memset(spaces,' ',10*indent); + sys_memset(spaces,' ',10*indent); spaces[10*indent] = '\0'; erts_printf("%s-> %c\n",spaces,ch); erts_printf("%sId: %u\n",spaces,(unsigned) node->id); diff --git a/erts/emulator/beam/erl_bif_chksum.c b/erts/emulator/beam/erl_bif_chksum.c index 9417803e14..9095bcd380 100644 --- a/erts/emulator/beam/erl_bif_chksum.c +++ b/erts/emulator/beam/erl_bif_chksum.c @@ -516,7 +516,7 @@ md5_2(BIF_ALIST_2) /* No need to check context, this function cannot be called with unaligned or badly sized context as it's always trapped to. */ bytes = binary_bytes(BIF_ARG_1); - memcpy(&context,bytes,sizeof(MD5_CTX)); + sys_memcpy(&context,bytes,sizeof(MD5_CTX)); rest = do_chksum(&md5_wrap,BIF_P,BIF_ARG_2,100,(void *) &context,&res, &err); if (err != 0) { @@ -564,7 +564,7 @@ md5_update_2(BIF_ALIST_2) erts_free_aligned_binary_bytes(temp_alloc); BIF_ERROR(BIF_P, BADARG); } - memcpy(&context,bytes,sizeof(MD5_CTX)); + sys_memcpy(&context,bytes,sizeof(MD5_CTX)); erts_free_aligned_binary_bytes(temp_alloc); rest = do_chksum(&md5_wrap,BIF_P,BIF_ARG_2,100,(void *) &context,&res, &err); @@ -599,7 +599,7 @@ md5_final_1(BIF_ALIST_1) goto error; } bin = erts_new_heap_binary(BIF_P, (byte *)NULL, 16, &result); - memcpy(&ctx_copy, context, sizeof(MD5_CTX)); + sys_memcpy(&ctx_copy, context, sizeof(MD5_CTX)); erts_free_aligned_binary_bytes(temp_alloc); MD5Final(result, &ctx_copy); BIF_RET(bin); diff --git a/erts/emulator/beam/erl_bif_ddll.c b/erts/emulator/beam/erl_bif_ddll.c index f673ef3194..294bce115f 100644 --- a/erts/emulator/beam/erl_bif_ddll.c +++ b/erts/emulator/beam/erl_bif_ddll.c @@ -1412,7 +1412,7 @@ static Eterm copy_ref(Eterm ref, Eterm *hp) ErtsORefThing *ptr; ASSERT(is_internal_ordinary_ref(ref)); ptr = ordinary_ref_thing_ptr(ref); - memcpy(hp, ptr, sizeof(ErtsORefThing)); + sys_memcpy(hp, ptr, sizeof(ErtsORefThing)); return (make_internal_ref(hp)); } @@ -1454,9 +1454,9 @@ static void set_driver_reloading(DE_Handle *dh, Process *proc, char *path, char dh->procs = p; dh->status = ERL_DE_RELOAD; dh->reload_full_path = erts_alloc(ERTS_ALC_T_DDLL_HANDLE, sys_strlen(path) + 1); - strcpy(dh->reload_full_path,path); + sys_strcpy(dh->reload_full_path,path); dh->reload_driver_name = erts_alloc(ERTS_ALC_T_DDLL_HANDLE, sys_strlen(name) + 1); - strcpy(dh->reload_driver_name,name); + sys_strcpy(dh->reload_driver_name,name); dh->reload_flags = flags; } @@ -1501,10 +1501,11 @@ static int do_load_driver_entry(DE_Handle *dh, char *path, char *name) goto error; } - if (strcmp(name, dp->driver_name) != 0) { + if (sys_strcmp(name, dp->driver_name) != 0) { res = ERL_DE_LOAD_ERROR_BAD_NAME; goto error; } + erts_atomic_init_nob(&(dh->refc), (erts_aint_t) 0); erts_atomic32_init_nob(&dh->port_count, 0); dh->full_path = erts_alloc(ERTS_ALC_T_DDLL_HANDLE, sys_strlen(path) + 1); @@ -1512,7 +1513,7 @@ static int do_load_driver_entry(DE_Handle *dh, char *path, char *name) dh->flags = 0; dh->status = ERL_DE_OK; - if (erts_add_driver_entry(dp, dh, 1) != 0 /* io.c */) { + if (erts_add_driver_entry(dp, dh, 1, 1) != 0 /* io.c */) { /* * The init in the driver struct did not return 0 */ @@ -1799,7 +1800,7 @@ static char *pick_list_or_atom(Eterm name_term) goto error; } name = erts_alloc(ERTS_ALC_T_DDLL_TMP_BUF, ap->len + 1); - memcpy(name,ap->name,ap->len); + sys_memcpy(name,ap->name,ap->len); name[ap->len] = '\0'; } else { if (erts_iolist_size(name_term, &name_len)) { @@ -1870,7 +1871,7 @@ static erts_driver_t *lookup_driver(char *name) { erts_driver_t *drv; assert_drv_list_locked(); - for (drv = driver_list; drv != NULL && strcmp(drv->name, name); drv = drv->next) + for (drv = driver_list; drv != NULL && sys_strcmp(drv->name, name); drv = drv->next) ; return drv; } diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c index 903f54e2fb..bdca93428e 100644 --- a/erts/emulator/beam/erl_bif_info.c +++ b/erts/emulator/beam/erl_bif_info.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2017. All Rights Reserved. + * Copyright Ericsson AB 1999-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,6 +50,7 @@ #define ERTS_PTAB_WANT_DEBUG_FUNCS__ #include "erl_ptab.h" #include "erl_time.h" +#include "erl_proc_sig_queue.h" #ifdef HIPE #include "hipe_arch.h" #endif @@ -72,6 +73,9 @@ static Export *gather_msacc_res_trap; static Export *gather_gc_info_res_trap; static Export *gather_system_check_res_trap; +static Export *is_process_alive_trap; + + #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) static char otp_version[] = ERLANG_OTP_VERSION; @@ -211,21 +215,27 @@ bld_magic_ref_bin_list(Uint **hpp, Uint *szp, ErlOffHeap* oh) make_monitor_list: returns a list of records.. -record(erl_monitor, { - type, % MON_ORIGIN or MON_TARGET (1 or 3) - ref, + type, % process | port | time_offset | dist_process | resource + % | node | nodes | suspend + dir, % origin | target + ref, % reference or [] pid, % Process or nodename - name % registered name or [] + extra % registered name, integer or [] }). */ static void do_calc_mon_size(ErtsMonitor *mon, void *vpsz) { + ErtsMonitorData *mdp = erts_monitor_to_data(mon); Uint *psz = vpsz; - *psz += NC_HEAP_SIZE(mon->ref); - *psz += (mon->type == MON_NIF_TARGET ? - erts_resource_ref_size(mon->u.resource) : - (is_immed(mon->u.pid) ? 0 : NC_HEAP_SIZE(mon->u.pid))); - *psz += 8; /* CONS + 5-tuple */ + *psz += is_immed(mdp->ref) ? 0 : NC_HEAP_SIZE(mdp->ref); + + if (mon->type == ERTS_MON_TYPE_RESOURCE && erts_monitor_is_target(mon)) + *psz += erts_resource_ref_size(mon->other.ptr); + else + *psz += is_immed(mon->other.item) ? 0 : NC_HEAP_SIZE(mon->other.item); + + *psz += 9; /* CONS + 6-tuple */ } typedef struct { @@ -237,35 +247,97 @@ typedef struct { static void do_make_one_mon_element(ErtsMonitor *mon, void * vpmlc) { + ErtsMonitorData *mdp = erts_monitor_to_data(mon); MonListContext *pmlc = vpmlc; - Eterm tup; - Eterm r = STORE_NC(&(pmlc->hp), &MSO(pmlc->p), mon->ref); - Eterm p = (mon->type == MON_NIF_TARGET ? - erts_bld_resource_ref(&(pmlc->hp), &MSO(pmlc->p), mon->u.resource) - : (is_immed(mon->u.pid) ? mon->u.pid - : STORE_NC(&(pmlc->hp), &MSO(pmlc->p), mon->u.pid))); - tup = TUPLE5(pmlc->hp, pmlc->tag, make_small(mon->type), r, p, mon->name); - pmlc->hp += 6; + Eterm tup, t, d, r, p, x; + + r = is_immed(mdp->ref) ? mdp->ref : STORE_NC(&(pmlc->hp), &MSO(pmlc->p), mdp->ref); + if (mon->type == ERTS_MON_TYPE_RESOURCE && erts_monitor_is_target(mon)) + p = erts_bld_resource_ref(&(pmlc->hp), &MSO(pmlc->p), mon->other.ptr); + else + p = (is_immed(mon->other.item) + ? mon->other.item + : STORE_NC(&(pmlc->hp), &MSO(pmlc->p), mon->other.item)); + + if (mon->flags & ERTS_ML_FLG_NAME) + x = ((ErtsMonitorDataExtended *) mdp)->u.name; + else if (erts_monitor_is_target(mon)) + x = NIL; + else if (mon->type == ERTS_MON_TYPE_NODE || mon->type == ERTS_MON_TYPE_NODES) + x = make_small(((ErtsMonitorDataExtended *) mdp)->u.refc); + else + x = NIL; + + switch (mon->type) { + case ERTS_MON_TYPE_PROC: + t = am_process; + break; + case ERTS_MON_TYPE_PORT: + t = am_port; + break; + case ERTS_MON_TYPE_TIME_OFFSET: + t = am_time_offset; + break; + case ERTS_MON_TYPE_DIST_PROC: { + ERTS_DECL_AM(dist_process); + t = AM_dist_process; + break; + } + case ERTS_MON_TYPE_RESOURCE: { + ERTS_DECL_AM(resource); + t = AM_resource; + break; + } + case ERTS_MON_TYPE_NODE: + t = am_node; + break; + case ERTS_MON_TYPE_NODES: { + ERTS_DECL_AM(nodes); + t = AM_nodes; + break; + } + case ERTS_MON_TYPE_SUSPEND: + t = am_suspend; + break; + default: + ERTS_INTERNAL_ERROR("Unknown monitor type"); + t = am_error; + break; + } + if (erts_monitor_is_target(mon)) { + ERTS_DECL_AM(target); + d = AM_target; + } + else { + ERTS_DECL_AM(origin); + d = AM_origin; + } + tup = TUPLE6(pmlc->hp, pmlc->tag, t, d, r, p, x); + pmlc->hp += 7; pmlc->res = CONS(pmlc->hp, tup, pmlc->res); pmlc->hp += 2; } static Eterm -make_monitor_list(Process *p, ErtsMonitor *root) +make_monitor_list(Process *p, int tree, ErtsMonitor *root, Eterm tail) { DECL_AM(erl_monitor); Uint sz = 0; MonListContext mlc; + void (*foreach)(ErtsMonitor *, + void (*)(ErtsMonitor *, void *), + void *); - erts_doforall_monitors(root, &do_calc_mon_size, &sz); - if (sz == 0) { - return NIL; - } + foreach = tree ? erts_monitor_tree_foreach : erts_monitor_list_foreach; + + (*foreach)(root, do_calc_mon_size, &sz); + if (sz == 0) + return tail; mlc.p = p; mlc.hp = HAlloc(p,sz); - mlc.res = NIL; + mlc.res = tail; mlc.tag = AM_erl_monitor; - erts_doforall_monitors(root, &do_make_one_mon_element, &mlc); + (*foreach)(root, do_make_one_mon_element, &mlc); return mlc.res; } @@ -273,20 +345,22 @@ make_monitor_list(Process *p, ErtsMonitor *root) make_link_list: returns a list of records.. -record(erl_link, { - type, % LINK_NODE or LINK_PID (1 or 3) - pid, % Process or nodename - targets % List of erl_link's or nil + type, % process | port | dist_process + pid, % Process or port + id % (address) }). */ -static void do_calc_lnk_size(ErtsLink *lnk, void *vpsz) +static void calc_lnk_size(ErtsLink *lnk, void *vpsz) { Uint *psz = vpsz; - *psz += is_immed(lnk->pid) ? 0 : NC_HEAP_SIZE(lnk->pid); - if (lnk->type != LINK_NODE && ERTS_LINK_ROOT(lnk) != NULL) { - /* Node links use this pointer as ref counter... */ - erts_doforall_links(ERTS_LINK_ROOT(lnk),&do_calc_lnk_size,vpsz); - } + Uint sz = 0; + ErtsLinkData *ldp = erts_link_to_data(lnk); + + (void) erts_bld_uword(NULL, &sz, (UWord) ldp); + + *psz += sz; + *psz += is_immed(lnk->other.item) ? 0 : size_object(lnk->other.item); *psz += 7; /* CONS + 4-tuple */ } @@ -297,37 +371,58 @@ typedef struct { Eterm tag; } LnkListContext; -static void do_make_one_lnk_element(ErtsLink *lnk, void * vpllc) +static void make_one_lnk_element(ErtsLink *lnk, void * vpllc) { LnkListContext *pllc = vpllc; - Eterm tup; - Eterm old_res, targets = NIL; - Eterm p = (is_immed(lnk->pid) - ? lnk->pid - : STORE_NC(&(pllc->hp), &MSO(pllc->p), lnk->pid)); - if (lnk->type == LINK_NODE) { - targets = make_small(ERTS_LINK_REFC(lnk)); - } else if (ERTS_LINK_ROOT(lnk) != NULL) { - old_res = pllc->res; - pllc->res = NIL; - erts_doforall_links(ERTS_LINK_ROOT(lnk),&do_make_one_lnk_element, vpllc); - targets = pllc->res; - pllc->res = old_res; - } - tup = TUPLE4(pllc->hp, pllc->tag, make_small(lnk->type), p, targets); + Eterm tup, t, pid, id; + ErtsLinkData *ldp = erts_link_to_data(lnk); + + id = erts_bld_uword(&pllc->hp, NULL, (UWord) ldp); + + if (is_immed(lnk->other.item)) + pid = lnk->other.item; + else { + Uint sz = size_object(lnk->other.item); + pid = copy_struct(lnk->other.item, sz, &(pllc->hp), &MSO(pllc->p)); + } + + switch (lnk->type) { + case ERTS_LNK_TYPE_PROC: + t = am_process; + break; + case ERTS_LNK_TYPE_PORT: + t = am_port; + break; + case ERTS_LNK_TYPE_DIST_PROC: { + ERTS_DECL_AM(dist_process); + t = AM_dist_process; + break; + } + default: + ERTS_INTERNAL_ERROR("Unkown link type"); + t = am_undefined; + break; + } + + tup = TUPLE4(pllc->hp, pllc->tag, t, pid, id); pllc->hp += 5; pllc->res = CONS(pllc->hp, tup, pllc->res); pllc->hp += 2; } static Eterm -make_link_list(Process *p, ErtsLink *root, Eterm tail) +make_link_list(Process *p, int tree, ErtsLink *root, Eterm tail) { DECL_AM(erl_link); Uint sz = 0; LnkListContext llc; + void (*foreach)(ErtsLink *, + void (*)(ErtsLink *, void *), + void *); - erts_doforall_links(root, &do_calc_lnk_size, &sz); + foreach = tree ? erts_link_tree_foreach : erts_link_list_foreach; + + (*foreach)(root, calc_lnk_size, (void *) &sz); if (sz == 0) { return tail; } @@ -335,7 +430,7 @@ make_link_list(Process *p, ErtsLink *root, Eterm tail) llc.hp = HAlloc(p,sz); llc.res = tail; llc.tag = AM_erl_link; - erts_doforall_links(root, &do_make_one_lnk_element, &llc); + (*foreach)(root, make_one_lnk_element, (void *) &llc); return llc.res; } @@ -381,6 +476,8 @@ typedef struct { Eterm term; ErtsResource* resource; }entity; + int named; + Uint16 type; Eterm node; /* pid is actual target being monitored, no matter pid/port or name */ Eterm pid; @@ -423,78 +520,103 @@ static void collect_one_link(ErtsLink *lnk, void *vmicp) { MonitorInfoCollection *micp = vmicp; EXTEND_MONITOR_INFOS(micp); - if (!(lnk->type == LINK_PID)) { - return; - } - micp->mi[micp->mi_i].entity.term = lnk->pid; - micp->sz += 2 + NC_HEAP_SIZE(lnk->pid); + micp->mi[micp->mi_i].entity.term = lnk->other.item; + micp->sz += 2 + NC_HEAP_SIZE(lnk->other.item); micp->mi_i++; } static void collect_one_origin_monitor(ErtsMonitor *mon, void *vmicp) { - MonitorInfoCollection *micp = vmicp; + if (erts_monitor_is_origin(mon)) { + MonitorInfoCollection *micp = vmicp; - if (mon->type != MON_ORIGIN) { - return; - } - EXTEND_MONITOR_INFOS(micp); - if (is_atom(mon->u.pid)) { /* external by name */ - micp->mi[micp->mi_i].entity.term = mon->name; - micp->mi[micp->mi_i].node = mon->u.pid; - micp->sz += 3; /* need one 2-tuple */ - } else if (is_external_pid(mon->u.pid)) { /* external by pid */ - micp->mi[micp->mi_i].entity.term = mon->u.pid; - micp->mi[micp->mi_i].node = NIL; - micp->sz += NC_HEAP_SIZE(mon->u.pid); - } else if (!is_nil(mon->name)) { /* internal by name */ - micp->mi[micp->mi_i].entity.term = mon->name; - 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.term = mon->u.pid; - 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->u.pid; + EXTEND_MONITOR_INFOS(micp); + + micp->mi[micp->mi_i].type = mon->type; + + switch (mon->type) { + case ERTS_MON_TYPE_PROC: + case ERTS_MON_TYPE_PORT: + case ERTS_MON_TYPE_DIST_PROC: + case ERTS_MON_TYPE_TIME_OFFSET: + if (!(mon->flags & ERTS_ML_FLG_NAME)) { + micp->mi[micp->mi_i].named = 0; + micp->mi[micp->mi_i].entity.term = mon->other.item; + micp->mi[micp->mi_i].node = NIL; + if (is_not_atom(mon->other.item)) + micp->sz += NC_HEAP_SIZE(mon->other.item); + } + else { + ErtsMonitorDataExtended *mdep; + micp->mi[micp->mi_i].named = !0; + mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon); + micp->mi[micp->mi_i].entity.term = mdep->u.name; + if (mdep->dist) + micp->mi[micp->mi_i].node = mdep->dist->nodename; + else + micp->mi[micp->mi_i].node = erts_this_dist_entry->sysname; + micp->sz += 3; /* need one 2-tuple */ + } - micp->mi_i++; - micp->sz += 2 + 3; /* For a cons cell and a 2-tuple */ + /* 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->other.item; + + micp->mi_i++; + micp->sz += 2 + 3; /* For a cons cell and a 2-tuple */ + break; + default: + break; + } + } } static void collect_one_target_monitor(ErtsMonitor *mon, void *vmicp) { MonitorInfoCollection *micp = vmicp; - if (mon->type != MON_TARGET && mon->type != MON_NIF_TARGET) { - return; - } + if (erts_monitor_is_target(mon)) { - EXTEND_MONITOR_INFOS(micp); + EXTEND_MONITOR_INFOS(micp); + micp->mi[micp->mi_i].type = mon->type; + micp->mi[micp->mi_i].named = !!(mon->flags & ERTS_ML_FLG_NAME); + switch (mon->type) { + + case ERTS_MON_TYPE_PROC: + case ERTS_MON_TYPE_PORT: + case ERTS_MON_TYPE_DIST_PROC: + + micp->mi[micp->mi_i].entity.term = mon->other.item; + micp->mi[micp->mi_i].node = NIL; + micp->sz += NC_HEAP_SIZE(mon->other.item); + + micp->sz += 2; /* cons */; + micp->mi_i++; + break; + + case ERTS_MON_TYPE_RESOURCE: + + micp->mi[micp->mi_i].entity.resource = mon->other.ptr; + micp->mi[micp->mi_i].node = NIL; + micp->sz += erts_resource_ref_size(mon->other.ptr); + + micp->sz += 2; /* cons */; + micp->mi_i++; + break; + + default: + break; + } - if (mon->type == MON_NIF_TARGET) { - micp->mi[micp->mi_i].entity.resource = mon->u.resource; - micp->mi[micp->mi_i].node = make_small(MON_NIF_TARGET); - micp->sz += erts_resource_ref_size(mon->u.resource); - } - else { - micp->mi[micp->mi_i].entity.term = mon->u.pid; - micp->mi[micp->mi_i].node = NIL; - micp->sz += NC_HEAP_SIZE(mon->u.pid); } - micp->sz += 2; /* cons */; - micp->mi_i++; } typedef struct { Process *c_p; ErtsProcLocks c_p_locks; - ErtsSuspendMonitor **smi; + ErtsMonitorSuspend **smi; Uint smi_i; Uint smi_max; int sz; @@ -517,10 +639,10 @@ do { \ (SMICP)->smi, \ ((SMICP)->smi_max \ + ERTS_SMI_INC) \ - * sizeof(ErtsSuspendMonitor *)) \ + * sizeof(ErtsMonitorSuspend *)) \ : erts_alloc(ERTS_ALC_T_TMP, \ ERTS_SMI_INC \ - * sizeof(ErtsSuspendMonitor *))); \ + * sizeof(ErtsMonitorSuspend *))); \ (SMICP)->smi_max += ERTS_SMI_INC; \ } \ } while (0) @@ -533,12 +655,13 @@ do { \ } while (0) static void -collect_one_suspend_monitor(ErtsSuspendMonitor *smon, void *vsmicp) +collect_one_suspend_monitor(ErtsMonitor *mon, void *vsmicp) { + ErtsMonitorSuspend *smon = erts_monitor_suspend(mon); ErtsSuspendMonitorInfoCollection *smicp = vsmicp; Process *suspendee = erts_pid2proc(smicp->c_p, smicp->c_p_locks, - smon->pid, + mon->other.item, 0); if (suspendee) { /* suspendee is alive */ Sint a, p; @@ -572,29 +695,23 @@ collect_one_suspend_monitor(ErtsSuspendMonitor *smon, void *vsmicp) #define ERTS_PI_FAIL_TYPE_BADARG 0 #define ERTS_PI_FAIL_TYPE_YIELD 1 -#define ERTS_PI_FAIL_TYPE_AWAIT_EXIT 2 +#define ERTS_PI_FAIL_TYPE_EXITED 2 static ERTS_INLINE ErtsProcLocks pi_locks(Eterm info) { switch (info) { - case am_status: case am_priority: - case am_trap_exit: - return ERTS_PROC_LOCK_STATUS; - case am_links: - case am_monitors: - case am_monitored_by: - case am_suspending: - return ERTS_PROC_LOCK_LINK; + case am_status: + return 0; + case am_suspended: + return ERTS_PROC_LOCK_STATUS; case am_messages: case am_message_queue_len: case am_total_heap_size: - return ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_MSGQ; - case am_memory: - return ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_MSGQ; + return ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_MSGQ; default: - return ERTS_PROC_LOCK_MAIN; + return ERTS_PROC_LOCK_MAIN; } } @@ -698,7 +815,6 @@ static Eterm pi_1_keys[] = { am_initial_call, am_status, am_message_queue_len, - am_messages, am_links, am_dictionary, am_trap_exit, @@ -743,7 +859,7 @@ process_info_init(void) } static ERTS_INLINE Process * -pi_pid2proc(Process *c_p, Eterm pid, ErtsProcLocks info_locks) +pi_lookup_proc(Process *c_p, Eterm pid, ErtsProcLocks *locks) { /* * If the main lock is needed, we use erts_pid2proc_not_running() @@ -758,23 +874,79 @@ pi_pid2proc(Process *c_p, Eterm pid, ErtsProcLocks info_locks) * is currently running. */ - if (info_locks & ERTS_PROC_LOCK_MAIN) - return erts_pid2proc_not_running(c_p, ERTS_PROC_LOCK_MAIN, - pid, info_locks); - else - return erts_pid2proc(c_p, ERTS_PROC_LOCK_MAIN, - pid, info_locks); + if ((*locks) & ERTS_PROC_LOCK_MAIN) { + erts_aint32_t state; + int local_only, done; + Process *rp; + ErtsProcLocks more_locks; + + rp = erts_pid2proc_not_running(c_p, ERTS_PROC_LOCK_MAIN, + pid, ERTS_PROC_LOCK_MAIN); + + if (!rp || rp == ERTS_PROC_LOCK_BUSY) + return rp; + + if ((*locks) & ERTS_PROC_LOCK_MSGQ) { + /* + * Move in queue into private queue and + * release msgq lock, enabling others to + * send messages to the process while it + * is being inspected... + */ + erts_proc_lock(rp, ERTS_PROC_LOCK_MSGQ); + erts_proc_sig_fetch(rp); + erts_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ); + (*locks) &= ~ERTS_PROC_LOCK_MSGQ; + } + + /* + * Handle all signals received up to this point + * in order to preserve signal order. + * + * FIX ME: Should be done yielding... + */ + local_only = 0; + do { + int r = CONTEXT_REDS; + done = erts_proc_sig_handle_incoming(rp, &state, &r, + CONTEXT_REDS, + local_only); + local_only = !0; + BUMP_REDS(c_p, r); + } while (!done && !(state & ERTS_PSFLG_EXITING)); + + if (state & ERTS_PSFLG_EXITING) { + if (rp != c_p) + erts_proc_unlock(rp, ERTS_PROC_LOCK_MAIN); + return NULL; + } + more_locks = (*locks) & ~ERTS_PROC_LOCK_MAIN; + if (more_locks) + erts_proc_lock(rp, more_locks); + return rp; + } + + ASSERT(!((*locks) & ERTS_PROC_LOCK_MSGQ)); + + if (*locks) + return erts_pid2proc(c_p, ERTS_PROC_LOCK_MAIN, + pid, *locks); + + return erts_proc_lookup(pid); } + + static BIF_RETTYPE process_info_aux(Process *BIF_P, Process *rp, ErtsProcLocks rp_locks, Eterm rpid, Eterm item, - int always_wrap); + int always_wrap, + int *reds); #define ERTS_PI_RES_ELEM_IX_BUF_INC 1024 #define ERTS_PI_DEF_RES_ELEM_IX_BUF_SZ ERTS_PI_ARGS @@ -794,6 +966,7 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap, ErtsProcLocks locks = (ErtsProcLocks) 0; int res_len, ix; Process *rp = NULL; + int reds = 0; *fail_type = ERTS_PI_FAIL_TYPE_BADARG; @@ -845,9 +1018,14 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap, ASSERT(res_len > 0); - rp = pi_pid2proc(c_p, pid, locks|ERTS_PROC_LOCK_STATUS); + rp = pi_lookup_proc(c_p, pid, &locks); if (!rp) { - res = am_undefined; + if (c_p->common.id != pid) + res = am_undefined; + else { + *fail_type = ERTS_PI_FAIL_TYPE_EXITED; + res = THE_NON_VALUE; + } goto done; } else if (rp == ERTS_PROC_LOCK_BUSY) { @@ -856,38 +1034,9 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap, *fail_type = ERTS_PI_FAIL_TYPE_YIELD; goto done; } - else if (c_p != rp && ERTS_PROC_PENDING_EXIT(rp)) { - locks |= ERTS_PROC_LOCK_STATUS; - res = THE_NON_VALUE; - *fail_type = ERTS_PI_FAIL_TYPE_AWAIT_EXIT; - goto done; - } - else { - ErtsProcLocks unlock_locks = 0; - - if (c_p == rp) - locks |= ERTS_PROC_LOCK_MAIN; - - if (!(locks & ERTS_PROC_LOCK_STATUS)) - unlock_locks |= ERTS_PROC_LOCK_STATUS; - - if (locks & ERTS_PROC_LOCK_MSGQ) { - /* - * Move in queue into private queue and - * release msgq lock, enabling others to - * send messages to the process while it - * is being inspected... - */ - ASSERT(locks & ERTS_PROC_LOCK_MAIN); - ERTS_MSGQ_MV_INQ2PRIVQ(rp); - locks &= ~ERTS_PROC_LOCK_MSGQ; - unlock_locks |= ERTS_PROC_LOCK_MSGQ; - } - if (unlock_locks) - erts_proc_unlock(rp, unlock_locks); - - } + if (c_p == rp) + locks |= ERTS_PROC_LOCK_MAIN; /* * We always handle 'messages' first if it should be part @@ -899,16 +1048,23 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap, if (want_messages) { ix = pi_arg2ix(am_messages); ASSERT(part_res[ix] == THE_NON_VALUE); - part_res[ix] = process_info_aux(c_p, rp, locks, pid, am_messages, always_wrap); - ASSERT(part_res[ix] != THE_NON_VALUE); + res = process_info_aux(c_p, rp, locks, pid, am_messages, + always_wrap, &reds); + ASSERT(res != am_undefined); + ASSERT(res != THE_NON_VALUE); + part_res[ix] = res; } for (; res_elem_ix_ix >= 0; res_elem_ix_ix--) { ix = res_elem_ix[res_elem_ix_ix]; if (part_res[ix] == THE_NON_VALUE) { arg = pi_ix2arg(ix); - part_res[ix] = process_info_aux(c_p, rp, locks, pid, arg, always_wrap); - ASSERT(part_res[ix] != THE_NON_VALUE); + res = process_info_aux(c_p, rp, locks, pid, arg, + always_wrap, &reds); + if (res == am_undefined) + goto done; + ASSERT(res != THE_NON_VALUE); + part_res[ix] = res; } } @@ -948,6 +1104,8 @@ process_info_list(Process *c_p, Eterm pid, Eterm list, int always_wrap, if (res_elem_ix != &def_res_elem_ix_buf[0]) erts_free(ERTS_ALC_T_TMP, res_elem_ix); + BUMP_REDS(c_p, reds); + return res; } @@ -971,8 +1129,8 @@ BIF_RETTYPE process_info_1(BIF_ALIST_1) BIF_ERROR(BIF_P, BADARG); case ERTS_PI_FAIL_TYPE_YIELD: ERTS_BIF_YIELD1(bif_export[BIF_process_info_1], BIF_P, BIF_ARG_1); - case ERTS_PI_FAIL_TYPE_AWAIT_EXIT: - ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_undefined); + case ERTS_PI_FAIL_TYPE_EXITED: + ERTS_BIF_EXITED(BIF_P); default: erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error", __FILE__, __LINE__); } @@ -989,7 +1147,7 @@ BIF_RETTYPE process_info_2(BIF_ALIST_2) Process *rp; Eterm pid = BIF_ARG_1; ErtsProcLocks info_locks; - int fail_type; + int fail_type, reds = 0; if (is_external_pid(pid) && external_pid_dist_entry(pid) == erts_this_dist_entry) @@ -1011,8 +1169,8 @@ BIF_RETTYPE process_info_2(BIF_ALIST_2) case ERTS_PI_FAIL_TYPE_YIELD: ERTS_BIF_YIELD2(bif_export[BIF_process_info_2], BIF_P, BIF_ARG_1, BIF_ARG_2); - case ERTS_PI_FAIL_TYPE_AWAIT_EXIT: - ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_undefined); + case ERTS_PI_FAIL_TYPE_EXITED: + ERTS_BIF_EXITED(BIF_P); default: erts_exit(ERTS_ABORT_EXIT, "%s:%d: Internal error", __FILE__, __LINE__); @@ -1027,44 +1185,23 @@ BIF_RETTYPE process_info_2(BIF_ALIST_2) info_locks = pi_locks(BIF_ARG_2); - rp = pi_pid2proc(BIF_P, pid, info_locks|ERTS_PROC_LOCK_STATUS); - if (!rp) - res = am_undefined; + rp = pi_lookup_proc(BIF_P, pid, &info_locks); + if (!rp) { + if (BIF_P->common.id == pid) + ERTS_BIF_EXITED(BIF_P); + BIF_RET(am_undefined); + } else if (rp == ERTS_PROC_LOCK_BUSY) ERTS_BIF_YIELD2(bif_export[BIF_process_info_2], BIF_P, BIF_ARG_1, BIF_ARG_2); - else if (rp != BIF_P && ERTS_PROC_PENDING_EXIT(rp)) { - erts_proc_unlock(rp, info_locks|ERTS_PROC_LOCK_STATUS); - ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_undefined); - } - else { - ErtsProcLocks unlock_locks = 0; - if (BIF_P == rp) - info_locks |= ERTS_PROC_LOCK_MAIN; - - if (!(info_locks & ERTS_PROC_LOCK_STATUS)) - unlock_locks |= ERTS_PROC_LOCK_STATUS; - - if (info_locks & ERTS_PROC_LOCK_MSGQ) { - /* - * Move in queue into private queue and - * release msgq lock, enabling others to - * send messages to the process while it - * is being inspected... - */ - ASSERT(info_locks & ERTS_PROC_LOCK_MAIN); - ERTS_MSGQ_MV_INQ2PRIVQ(rp); - info_locks &= ~ERTS_PROC_LOCK_MSGQ; - unlock_locks |= ERTS_PROC_LOCK_MSGQ; - } + if (BIF_P == rp) + info_locks |= ERTS_PROC_LOCK_MAIN; - if (unlock_locks) - erts_proc_unlock(rp, unlock_locks); + res = process_info_aux(BIF_P, rp, info_locks, pid, BIF_ARG_2, + 0, &reds); - res = process_info_aux(BIF_P, rp, info_locks, pid, BIF_ARG_2, 0); - } - ASSERT(is_value(res)); + BUMP_REDS(BIF_P, reds); if (BIF_P == rp) info_locks &= ~ERTS_PROC_LOCK_MAIN; @@ -1081,11 +1218,14 @@ process_info_aux(Process *BIF_P, ErtsProcLocks rp_locks, Eterm rpid, Eterm item, - int always_wrap) + int always_wrap, + int *reds) { Eterm *hp; Eterm res = NIL; + (*reds)++; + ASSERT(rp); /* @@ -1144,14 +1284,15 @@ process_info_aux(Process *BIF_P, break; case am_status: - res = erts_process_status(rp, rpid); - ASSERT(res != am_undefined); + res = erts_process_state2status(erts_atomic32_read_nob(&rp->state)); + if (res == am_exiting || res == am_free) + return am_undefined; hp = HAlloc(BIF_P, 3); break; case am_messages: { - if (rp->msg.len == 0 || ERTS_TRACE_FLAGS(rp) & F_SENSITIVE) { + if (rp->sig_qs.len == 0 || ERTS_TRACE_FLAGS(rp) & F_SENSITIVE) { hp = HAlloc(BIF_P, 3); } else { ErtsMessageInfo *mip; @@ -1162,16 +1303,17 @@ process_info_aux(Process *BIF_P, #endif mip = erts_alloc(ERTS_ALC_T_TMP, - rp->msg.len*sizeof(ErtsMessageInfo)); + rp->sig_qs.len*sizeof(ErtsMessageInfo)); /* * Note that message queue may shrink when calling - * erts_prep_msgq_for_inspection() since it removes + * erts_proc_sig_prep_msgq_for_inspection() since it removes * corrupt distribution messages. */ - heap_need = erts_prep_msgq_for_inspection(BIF_P, rp, rp_locks, mip); + heap_need = erts_proc_sig_prep_msgq_for_inspection(BIF_P, rp, + rp_locks, mip); heap_need += 3; /* top 2-tuple */ - heap_need += rp->msg.len*2; /* Cons cells */ + heap_need += rp->sig_qs.len*2; /* Cons cells */ hp = HAlloc(BIF_P, heap_need); /* heap_need is exact */ #ifdef DEBUG @@ -1179,7 +1321,7 @@ process_info_aux(Process *BIF_P, #endif /* Build list of messages... */ - for (i = rp->msg.len - 1, res = NIL; i >= 0; i--) { + for (i = rp->sig_qs.len - 1, res = NIL; i >= 0; i--) { Eterm msg = ERL_MESSAGE_TERM(mip[i].msgp); Uint sz = mip[i].size; @@ -1192,6 +1334,11 @@ process_info_aux(Process *BIF_P, ASSERT(hp_end == hp + 3); + if (rp->sig_qs.len > CONTEXT_REDS*4) + *reds += CONTEXT_REDS*4; + else + *reds += rp->sig_qs.len / 4; + erts_free(ERTS_ALC_T_TMP, mip); } break; @@ -1199,7 +1346,7 @@ process_info_aux(Process *BIF_P, case am_message_queue_len: hp = HAlloc(BIF_P, 3); - res = make_small(rp->msg.len); + res = make_small(rp->sig_qs.len); break; case am_links: { @@ -1209,7 +1356,7 @@ process_info_aux(Process *BIF_P, INIT_MONITOR_INFOS(mic); - erts_doforall_links(ERTS_P_LINKS(rp),&collect_one_link,&mic); + erts_link_tree_foreach(ERTS_P_LINKS(rp), collect_one_link, (void *) &mic); hp = HAlloc(BIF_P, 3 + mic.sz); res = NIL; @@ -1218,6 +1365,12 @@ process_info_aux(Process *BIF_P, res = CONS(hp, item, res); hp += 2; } + + if (mic.mi_i > CONTEXT_REDS*4) + *reds += CONTEXT_REDS*4; + else + *reds += mic.mi_i / 4; + DESTROY_MONITOR_INFOS(mic); break; } @@ -1227,12 +1380,14 @@ process_info_aux(Process *BIF_P, int i; INIT_MONITOR_INFOS(mic); - erts_doforall_monitors(ERTS_P_MONITORS(rp), - &collect_one_origin_monitor, &mic); + erts_monitor_tree_foreach(ERTS_P_MONITORS(rp), + collect_one_origin_monitor, + (void *) &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.term)) { + if (mic.mi[i].named) { /* Monitor by name. * Build {process|port, {Name, Node}} and cons it. */ @@ -1252,11 +1407,28 @@ process_info_aux(Process *BIF_P, hp += 2; } else { - /* Monitor by pid. Build {process|port, Pid} and cons it. */ + /* Build {process|port|time_offset, Pid|clock_service} and cons it. */ Eterm t; - Eterm pid = STORE_NC(&hp, &MSO(BIF_P), mic.mi[i].entity.term); + Eterm pid; + Eterm m_type; + + if (is_atom(mic.mi[i].entity.term)) + pid = mic.mi[i].entity.term; + else + pid = STORE_NC(&hp, &MSO(BIF_P), mic.mi[i].entity.term); + + switch (mic.mi[i].type) { + case ERTS_MON_TYPE_PORT: + m_type = am_port; + break; + case ERTS_MON_TYPE_TIME_OFFSET: + m_type = am_time_offset; + break; + default: + m_type = am_process; + break; + } - 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)); @@ -1266,6 +1438,12 @@ process_info_aux(Process *BIF_P, hp += 2; } } + + if (mic.mi_i > CONTEXT_REDS*4) + *reds += CONTEXT_REDS*4; + else + *reds += mic.mi_i / 4; + DESTROY_MONITOR_INFOS(mic); break; } @@ -1276,20 +1454,34 @@ process_info_aux(Process *BIF_P, Eterm item; INIT_MONITOR_INFOS(mic); - erts_doforall_monitors(ERTS_P_MONITORS(rp),&collect_one_target_monitor,&mic); + erts_monitor_list_foreach(ERTS_P_LT_MONITORS(rp), + collect_one_target_monitor, + (void *) &mic); + erts_monitor_tree_foreach(ERTS_P_MONITORS(rp), + collect_one_target_monitor, + (void *) &mic); + hp = HAlloc(BIF_P, 3 + mic.sz); res = NIL; for (i = 0; i < mic.mi_i; ++i) { - if (mic.mi[i].node == make_small(MON_NIF_TARGET)) { - item = erts_bld_resource_ref(&hp, &MSO(BIF_P), mic.mi[i].entity.resource); - } - else { - item = STORE_NC(&hp, &MSO(BIF_P), mic.mi[i].entity.term); - } + if (mic.mi[i].type == ERTS_MON_TYPE_RESOURCE) + item = erts_bld_resource_ref(&hp, + &MSO(BIF_P), + mic.mi[i].entity.resource); + else + item = STORE_NC(&hp, + &MSO(BIF_P), + mic.mi[i].entity.term); res = CONS(hp, item, res); hp += 2; } + + if (mic.mi_i > CONTEXT_REDS*4) + *reds += CONTEXT_REDS*4; + else + *reds += mic.mi_i / 4; + DESTROY_MONITOR_INFOS(mic); break; } @@ -1306,11 +1498,11 @@ process_info_aux(Process *BIF_P, BIF_P, (BIF_P == rp ? ERTS_PROC_LOCK_MAIN - : 0) | ERTS_PROC_LOCK_LINK); + : 0) | ERTS_PROC_LOCK_STATUS); - erts_doforall_suspend_monitors(rp->suspend_monitors, - &collect_one_suspend_monitor, - &smic); + erts_monitor_tree_foreach(rp->suspend_monitors, + &collect_one_suspend_monitor, + &smic); hp = HAlloc(BIF_P, 3 + smic.sz); #ifdef DEBUG hp_end = hp + smic.sz; @@ -1334,12 +1526,17 @@ process_info_aux(Process *BIF_P, pending = small_to_big(p, hp); hp += BIG_UINT_HEAP_SIZE; } - item = TUPLE3(hp, smic.smi[i]->pid, active, pending); + item = TUPLE3(hp, smic.smi[i]->mon.other.item, active, pending); hp += 4; res = CONS(hp, item, res); hp += 2; } + if (smic.smi_i > CONTEXT_REDS*4) + *reds += CONTEXT_REDS*4; + else + *reds += smic.smi_i / 4; + ERTS_DESTROY_SUSPEND_MONITOR_INFOS(smic); ASSERT(hp == hp_end); @@ -1347,18 +1544,22 @@ process_info_aux(Process *BIF_P, } case am_dictionary: - if (ERTS_TRACE_FLAGS(rp) & F_SENSITIVE) { + if (!rp->dictionary || (ERTS_TRACE_FLAGS(rp) & F_SENSITIVE)) { res = NIL; } else { + Uint num = rp->dictionary->numElements; res = erts_dictionary_copy(BIF_P, rp->dictionary); + if (num > CONTEXT_REDS*4) + *reds += CONTEXT_REDS*4; + else + *reds += (int) num / 4; } hp = HAlloc(BIF_P, 3); break; case am_trap_exit: { - erts_aint32_t state = erts_atomic32_read_nob(&rp->state); hp = HAlloc(BIF_P, 3); - if (state & ERTS_PSFLG_TRAP_EXIT) + if (rp->flags & F_TRAP_EXIT) res = am_true; else res = am_false; @@ -1415,7 +1616,6 @@ process_info_aux(Process *BIF_P, } case am_total_heap_size: { - ErtsMessage *mp; Uint total_heap_size; Uint hsz = 3; @@ -1425,10 +1625,19 @@ process_info_aux(Process *BIF_P, total_heap_size += rp->mbuf_sz; - if (rp->flags & F_ON_HEAP_MSGQ) - for (mp = rp->msg.first; mp; mp = mp->next) - if (mp->data.attached) - total_heap_size += erts_msg_attached_data_size(mp); + if (rp->flags & F_ON_HEAP_MSGQ) { + ERTS_FOREACH_SIG_PRIVQS( + rp, mp, + { + if (ERTS_SIG_IS_MSG(mp) && mp->data.attached) + total_heap_size += erts_msg_attached_data_size(mp); + }); + + if (rp->sig_qs.len > CONTEXT_REDS*4) + *reds += CONTEXT_REDS*4; + else + *reds += rp->sig_qs.len / 4; + } (void) erts_bld_uint(NULL, &hsz, total_heap_size); hp = HAlloc(BIF_P, hsz); @@ -1451,6 +1660,12 @@ process_info_aux(Process *BIF_P, (void) erts_bld_uint(NULL, &hsz, size); hp = HAlloc(BIF_P, hsz); res = erts_bld_uint(&hp, NULL, size); + + if (rp->sig_qs.len > CONTEXT_REDS*4) + *reds += CONTEXT_REDS*4; + else + *reds += rp->sig_qs.len / 4; + break; } @@ -1523,10 +1738,14 @@ process_info_aux(Process *BIF_P, break; } - case am_priority: + case am_priority: { + erts_aint32_t state = erts_atomic32_read_nob(&rp->state); + if (ERTS_PSFLG_EXITING & state) + return am_undefined; hp = HAlloc(BIF_P, 3); - res = erts_get_process_priority(rp); + res = erts_get_process_priority(state); break; + } case am_trace: hp = HAlloc(BIF_P, 3); @@ -1629,6 +1848,8 @@ process_info_aux(Process *BIF_P, (void) bld_magic_ref_bin_list(NULL, &sz, &MSO(rp)); hp = HAlloc(BIF_P, sz); res = bld_magic_ref_bin_list(&hp, NULL, &MSO(rp)); + + *reds += 10; break; } @@ -2378,12 +2599,12 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_RET(os_version_tuple); } else if (BIF_ARG_1 == am_version) { - int n = strlen(ERLANG_VERSION); + int n = sys_strlen(ERLANG_VERSION); hp = HAlloc(BIF_P, ((sizeof ERLANG_VERSION)-1) * 2); BIF_RET(buf_to_intlist(&hp, ERLANG_VERSION, n, NIL)); } else if (BIF_ARG_1 == am_machine) { - int n = strlen(EMULATOR); + int n = sys_strlen(EMULATOR); hp = HAlloc(BIF_P, n*2); BIF_RET(buf_to_intlist(&hp, EMULATOR, n, NIL)); } @@ -2409,7 +2630,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) res = erts_bld_cons(hpp, hszp, erts_bld_tuple(hpp, hszp, 2, erts_atom_put((byte *)opc[i].name, - strlen(opc[i].name), + sys_strlen(opc[i].name), ERTS_ATOM_ENC_LATIN1, 1), erts_bld_uint(hpp, hszp, @@ -2805,21 +3026,21 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) Uint sz; Eterm res = NIL, tup, text; Eterm *hp = HAlloc(BIF_P, 3*(2 + 3) + /* three 2-tuples and three cons */ - 2*(strlen(erts_build_flags_CONFIG_H) + - strlen(erts_build_flags_CFLAGS) + - strlen(erts_build_flags_LDFLAGS))); + 2*(sys_strlen(erts_build_flags_CONFIG_H) + + sys_strlen(erts_build_flags_CFLAGS) + + sys_strlen(erts_build_flags_LDFLAGS))); - sz = strlen(erts_build_flags_CONFIG_H); + sz = sys_strlen(erts_build_flags_CONFIG_H); text = buf_to_intlist(&hp, erts_build_flags_CONFIG_H, sz, NIL); tup = TUPLE2(hp, am_config_h, text); hp += 3; res = CONS(hp, tup, res); hp += 2; - sz = strlen(erts_build_flags_CFLAGS); + sz = sys_strlen(erts_build_flags_CFLAGS); text = buf_to_intlist(&hp, erts_build_flags_CFLAGS, sz, NIL); tup = TUPLE2(hp, am_cflags, text); hp += 3; res = CONS(hp, tup, res); hp += 2; - sz = strlen(erts_build_flags_LDFLAGS); + sz = sys_strlen(erts_build_flags_LDFLAGS); text = buf_to_intlist(&hp, erts_build_flags_LDFLAGS, sz, NIL); tup = TUPLE2(hp, am_ldflags, text); hp += 3; res = CONS(hp, tup, res); hp += 2; @@ -2863,6 +3084,16 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1) BIF_ERROR(BIF_P, BADARG); } +static void monitor_size(ErtsMonitor *mon, void *vsz) +{ + *((Uint *) vsz) = erts_monitor_size(mon); +} + +static void link_size(ErtsMonitor *lnk, void *vsz) +{ + *((Uint *) vsz) = erts_link_size(lnk); +} + /**********************************************************************/ /* Return information on ports */ /* Info: @@ -2898,7 +3129,7 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, INIT_MONITOR_INFOS(mic); - erts_doforall_links(ERTS_P_LINKS(prt), &collect_one_link, &mic); + erts_link_tree_foreach(ERTS_P_LINKS(prt), collect_one_link, (void *) &mic); if (szp) *szp += mic.sz; @@ -2922,11 +3153,11 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, else if (item == am_monitors) { MonitorInfoCollection mic; int i; - Eterm item; INIT_MONITOR_INFOS(mic); - erts_doforall_monitors(ERTS_P_MONITORS(prt), - &collect_one_origin_monitor, &mic); + erts_monitor_tree_foreach(ERTS_P_MONITORS(prt), + collect_one_origin_monitor, + (void *) &mic); if (szp) *szp += mic.sz; @@ -2935,11 +3166,10 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, res = NIL; for (i = 0; i < mic.mi_i; i++) { Eterm t; - Eterm m_type; - item = STORE_NC(hpp, ohp, mic.mi[i].entity.term); - m_type = is_port(item) ? am_port : am_process; - t = TUPLE2(*hpp, m_type, item); + ASSERT(mic.mi[i].type == ERTS_MON_TYPE_PORT); + ASSERT(is_internal_pid(mic.mi[i].entity.term)); + t = TUPLE2(*hpp, am_process, mic.mi[i].entity.term); *hpp += 3; res = CONS(*hpp, t, res); *hpp += 2; @@ -2958,15 +3188,19 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, Eterm item; INIT_MONITOR_INFOS(mic); - erts_doforall_monitors(ERTS_P_MONITORS(prt), - &collect_one_target_monitor, &mic); + erts_monitor_list_foreach(ERTS_P_LT_MONITORS(prt), + collect_one_target_monitor, + (void *) &mic); + erts_monitor_tree_foreach(ERTS_P_MONITORS(prt), + collect_one_target_monitor, + (void *) &mic); if (szp) *szp += mic.sz; if (hpp) { res = NIL; for (i = 0; i < mic.mi_i; ++i) { - ASSERT(mic.mi[i].node == NIL); + ASSERT(mic.mi[i].type != ERTS_MON_TYPE_RESOURCE); item = STORE_NC(hpp, ohp, mic.mi[i].entity.term); res = CONS(*hpp, item, res); *hpp += 2; @@ -3043,7 +3277,12 @@ erts_bld_port_info(Eterm **hpp, ErlOffHeap *ohp, Uint *szp, Port *prt, */ Uint size = 0; - erts_doforall_links(ERTS_P_LINKS(prt), &erts_one_link_size, &size); + erts_link_tree_foreach(ERTS_P_LINKS(prt), + link_size, (void *) &size); + erts_monitor_tree_foreach(ERTS_P_MONITORS(prt), + monitor_size, (void *) &size); + erts_monitor_list_foreach(ERTS_P_LT_MONITORS(prt), + monitor_size, (void *) &size); size += erts_port_data_size(prt); @@ -3265,35 +3504,53 @@ fun_info_mfa_1(BIF_ALIST_1) BIF_ERROR(p, BADARG); } +BIF_RETTYPE erts_internal_is_process_alive_2(BIF_ALIST_2) +{ + if (!is_internal_pid(BIF_ARG_1) || !is_internal_ordinary_ref(BIF_ARG_2)) + BIF_ERROR(BIF_P, BADARG); + erts_proc_sig_send_is_alive_request(BIF_P, BIF_ARG_1, BIF_ARG_2); + BIF_RET(am_ok); +} + BIF_RETTYPE is_process_alive_1(BIF_ALIST_1) { - if(is_internal_pid(BIF_ARG_1)) { - Process *rp; - - if (BIF_ARG_1 == BIF_P->common.id) - BIF_RET(am_true); - - rp = erts_proc_lookup_raw(BIF_ARG_1); - if (!rp) { - BIF_RET(am_false); - } - else { - if (erts_atomic32_read_acqb(&rp->state) - & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING)) - ERTS_BIF_AWAIT_X_DATA_TRAP(BIF_P, BIF_ARG_1, am_false); - else - BIF_RET(am_true); - } - } - else if(is_external_pid(BIF_ARG_1)) { - if(external_pid_dist_entry(BIF_ARG_1) == erts_this_dist_entry) + if (is_internal_pid(BIF_ARG_1)) { + erts_aint32_t state; + Process *rp; + + if (BIF_ARG_1 == BIF_P->common.id) + BIF_RET(am_true); + + rp = erts_proc_lookup_raw(BIF_ARG_1); + if (!rp) + BIF_RET(am_false); + + state = erts_atomic32_read_acqb(&rp->state); + if (state & (ERTS_PSFLG_EXITING + | ERTS_PSFLG_SIG_Q + | ERTS_PSFLG_SIG_IN_Q)) { + /* + * If in exiting state, trap out and send 'is alive' + * request and wait for it to complete termination. + * + * If process has signals enqueued, we need to + * send it an 'is alive' request via its signal + * queue in order to ensure that signal order is + * preserved (we may earlier have sent it an + * exit signal that has not been processed yet). + */ + BIF_TRAP1(is_process_alive_trap, BIF_P, BIF_ARG_1); + } + + BIF_RET(am_true); + } + + if (is_external_pid(BIF_ARG_1)) { + if (external_pid_dist_entry(BIF_ARG_1) == erts_this_dist_entry) BIF_RET(am_false); /* A pid from an old incarnation of this node */ - else - BIF_ERROR(BIF_P, BADARG); - } - else { - BIF_ERROR(BIF_P, BADARG); } + + BIF_ERROR(BIF_P, BADARG); } BIF_RETTYPE process_display_2(BIF_ALIST_2) @@ -3311,16 +3568,6 @@ BIF_RETTYPE process_display_2(BIF_ALIST_2) if (rp == ERTS_PROC_LOCK_BUSY) ERTS_BIF_YIELD2(bif_export[BIF_process_display_2], BIF_P, BIF_ARG_1, BIF_ARG_2); - if (rp != BIF_P && ERTS_PROC_PENDING_EXIT(rp)) { - Eterm args[2] = {BIF_ARG_1, BIF_ARG_2}; - erts_proc_unlock(rp, ERTS_PROC_LOCKS_ALL); - ERTS_BIF_AWAIT_X_APPLY_TRAP(BIF_P, - BIF_ARG_1, - am_erlang, - am_process_display, - args, - 2); - } erts_stack_dump(ERTS_PRINT_STDERR, NULL, rp); erts_proc_unlock(rp, (BIF_P == rp ? ERTS_PROC_LOCKS_ALL_MINOR @@ -3693,22 +3940,59 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) BIF_RET(erts_process_status(NULL, tp[2])); } } + else if (ERTS_IS_ATOM_STR("connection_id", tp[1])) { + DistEntry *dep; + Eterm *hp, res; + Uint con_id, hsz = 0; + if (!is_atom(tp[2])) + BIF_ERROR(BIF_P, BADARG); + dep = erts_sysname_to_connected_dist_entry(tp[2]); + if (!dep) + BIF_ERROR(BIF_P, BADARG); + erts_de_rlock(dep); + con_id = (Uint) dep->connection_id; + erts_de_runlock(dep); + (void) erts_bld_uint(NULL, &hsz, con_id); + hp = hsz ? HAlloc(BIF_P, hsz) : NULL; + res = erts_bld_uint(&hp, NULL, con_id); + BIF_RET(res); + } else if (ERTS_IS_ATOM_STR("link_list", tp[1])) { /* Used by erl_link_SUITE (emulator) */ if(is_internal_pid(tp[2])) { + erts_aint32_t state; Eterm res; Process *p; + int sigs_done, local_only; p = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN, tp[2], - ERTS_PROC_LOCK_LINK); + ERTS_PROC_LOCK_MAIN); if (!p) { ERTS_ASSERT_IS_NOT_EXITING(BIF_P); BIF_RET(am_undefined); } - res = make_link_list(BIF_P, ERTS_P_LINKS(p), NIL); - erts_proc_unlock(p, ERTS_PROC_LOCK_LINK); + + local_only = 0; + do { + int reds = CONTEXT_REDS; + sigs_done = erts_proc_sig_handle_incoming(p, + &state, + &reds, + CONTEXT_REDS, + local_only); + local_only = !0; + } while (!sigs_done && !(state & ERTS_PSFLG_EXITING)); + + if (!(state & ERTS_PSFLG_EXITING)) + res = make_link_list(BIF_P, 1, ERTS_P_LINKS(p), NIL); + else if (BIF_P == p) + ERTS_BIF_EXITED(BIF_P); + else + res = am_undefined; + if (BIF_P != p) + erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN); BIF_RET(res); } else if(is_internal_port(tp[2])) { @@ -3719,19 +4003,20 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) ERTS_PORT_SFLGS_INVALID_LOOKUP); if(!p) BIF_RET(am_undefined); - res = make_link_list(BIF_P, ERTS_P_LINKS(p), NIL); + res = make_link_list(BIF_P, 1, ERTS_P_LINKS(p), NIL); erts_port_release(p); BIF_RET(res); } else if(is_node_name_atom(tp[2])) { DistEntry *dep = erts_find_dist_entry(tp[2]); if(dep) { - Eterm subres; - erts_de_links_lock(dep); - subres = make_link_list(BIF_P, dep->nlinks, NIL); - subres = make_link_list(BIF_P, dep->node_links, subres); - erts_de_links_unlock(dep); - BIF_RET(subres); + Eterm res = NIL; + if (dep->mld) { + erts_mtx_lock(&dep->mld->mtx); + res = make_link_list(BIF_P, 0, dep->mld->links, NIL); + erts_mtx_unlock(&dep->mld->mtx); + } + BIF_RET(res); } else { BIF_RET(am_undefined); } @@ -3740,27 +4025,54 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) else if (ERTS_IS_ATOM_STR("monitor_list", tp[1])) { /* Used by erl_link_SUITE (emulator) */ if(is_internal_pid(tp[2])) { + erts_aint32_t state; Process *p; Eterm res; + int sigs_done, local_only; p = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN, tp[2], - ERTS_PROC_LOCK_LINK); + ERTS_PROC_LOCK_MAIN); if (!p) { ERTS_ASSERT_IS_NOT_EXITING(BIF_P); BIF_RET(am_undefined); } - res = make_monitor_list(BIF_P, ERTS_P_MONITORS(p)); - erts_proc_unlock(p, ERTS_PROC_LOCK_LINK); + + local_only = 0; + do { + int reds = CONTEXT_REDS; + sigs_done = erts_proc_sig_handle_incoming(p, + &state, + &reds, + CONTEXT_REDS, + local_only); + local_only = !0; + } while (!sigs_done && !(state & ERTS_PSFLG_EXITING)); + + if (!(state & ERTS_PSFLG_EXITING)) { + res = make_monitor_list(BIF_P, 1, ERTS_P_MONITORS(p), NIL); + res = make_monitor_list(BIF_P, 0, ERTS_P_LT_MONITORS(p), res); + } + else { + if (BIF_P == p) + ERTS_BIF_EXITED(BIF_P); + else + res = am_undefined; + } + if (BIF_P != p) + erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN); BIF_RET(res); } else if(is_node_name_atom(tp[2])) { DistEntry *dep = erts_find_dist_entry(tp[2]); if(dep) { - Eterm ml; - erts_de_links_lock(dep); - ml = make_monitor_list(BIF_P, dep->monitors); - erts_de_links_unlock(dep); + Eterm ml = NIL; + if (dep->mld) { + erts_mtx_lock(&dep->mld->mtx); + ml = make_monitor_list(BIF_P, 1, dep->mld->orig_name_monitors, NIL); + ml = make_monitor_list(BIF_P, 0, dep->mld->monitors, ml); + erts_mtx_unlock(&dep->mld->mtx); + } BIF_RET(ml); } else { BIF_RET(am_undefined); @@ -3778,18 +4090,6 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) } BIF_RET(res); } - else if (ERTS_IS_ATOM_STR("have_pending_exit", tp[1])) { - Process *rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN, - tp[2], ERTS_PROC_LOCK_STATUS); - if (!rp) { - BIF_RET(am_undefined); - } - else { - Eterm res = ERTS_PROC_PENDING_EXIT(rp) ? am_true : am_false; - erts_proc_unlock(rp, ERTS_PROC_LOCK_STATUS); - BIF_RET(res); - } - } else if (ERTS_IS_ATOM_STR("binary_info", tp[1])) { Eterm bin = tp[2]; if (is_binary(bin)) { @@ -3883,7 +4183,7 @@ BIF_RETTYPE erts_debug_get_internal_state_1(BIF_ALIST_1) while (ix >= atom_table_size()) { char tmp[20]; erts_snprintf(tmp, sizeof(tmp), "am%x", atom_table_size()); - erts_atom_put((byte *) tmp, strlen(tmp), ERTS_ATOM_ENC_LATIN1, 1); + erts_atom_put((byte *) tmp, sys_strlen(tmp), ERTS_ATOM_ENC_LATIN1, 1); } return make_atom(ix); } @@ -4116,57 +4416,6 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2) erts_set_gc_state(BIF_P, enable); BIF_RET(res); } - else if (ERTS_IS_ATOM_STR("send_fake_exit_signal", BIF_ARG_1)) { - /* Used by signal_SUITE (emulator) */ - - /* Testcases depend on the exit being received via - a pending exit when the receiver is the same as - the caller. */ - if (is_tuple(BIF_ARG_2)) { - Eterm* tp = tuple_val(BIF_ARG_2); - if (arityval(tp[0]) == 3 - && (is_pid(tp[1]) || is_port(tp[1])) - && is_internal_pid(tp[2])) { - int xres; - ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND; - Process *rp = erts_pid2proc(BIF_P, ERTS_PROC_LOCK_MAIN, - tp[2], rp_locks); - if (!rp) { - DECL_AM(dead); - BIF_RET(AM_dead); - } - - if (BIF_P == rp) - rp_locks |= ERTS_PROC_LOCK_MAIN; - xres = erts_send_exit_signal(NULL, /* NULL in order to - force a pending exit - when we send to our - selves. */ - tp[1], - rp, - &rp_locks, - tp[3], - NIL, - NULL, - 0); - if (BIF_P == rp) - rp_locks &= ~ERTS_PROC_LOCK_MAIN; - erts_proc_unlock(rp, rp_locks); - if (xres > 1) { - DECL_AM(message); - BIF_RET(AM_message); - } - else if (xres == 0) { - DECL_AM(unaffected); - BIF_RET(AM_unaffected); - } - else { - DECL_AM(exit); - BIF_RET(AM_exit); - } - } - } - } else if (ERTS_IS_ATOM_STR("colliding_names", BIF_ARG_1)) { /* Used by ets_SUITE (stdlib) */ if (is_tuple(BIF_ARG_2)) { @@ -4449,7 +4698,7 @@ static Eterm lcnt_build_lock_stats_term(Eterm **hpp, Uint *szp, erts_lcnt_lock_s file = stats->file ? stats->file : "undefined"; - af = erts_atom_put((byte *)file, strlen(file), ERTS_ATOM_ENC_LATIN1, 1); + af = erts_atom_put((byte *)file, sys_strlen(file), ERTS_ATOM_ENC_LATIN1, 1); uil = erts_bld_uint( hpp, szp, stats->line); tloc = erts_bld_tuple(hpp, szp, 2, af, uil); @@ -4495,7 +4744,7 @@ static Eterm lcnt_pretty_print_lock_id(erts_lcnt_lock_info_t *info) { } else if(info->flags & ERTS_LOCK_FLAGS_CATEGORY_ALLOCATOR) { if(is_small(id) && !sys_strcmp(info->name, "alcu_allocator")) { const char *name = (const char*)ERTS_ALC_A2AD(signed_val(id)); - id = erts_atom_put((byte*)name, strlen(name), ERTS_ATOM_ENC_LATIN1, 1); + id = erts_atom_put((byte*)name, sys_strlen(name), ERTS_ATOM_ENC_LATIN1, 1); } } @@ -4515,8 +4764,8 @@ static Eterm lcnt_build_lock_term(Eterm **hpp, Uint *szp, lcnt_sample_t *sample, lock_desc = erts_lock_flags_get_type_name(info->flags); - type = erts_atom_put((byte*)lock_desc, strlen(lock_desc), ERTS_ATOM_ENC_LATIN1, 1); - name = erts_atom_put((byte*)info->name, strlen(info->name), ERTS_ATOM_ENC_LATIN1, 1); + type = erts_atom_put((byte*)lock_desc, sys_strlen(lock_desc), ERTS_ATOM_ENC_LATIN1, 1); + name = erts_atom_put((byte*)info->name, sys_strlen(info->name), ERTS_ATOM_ENC_LATIN1, 1); /* Only attempt to resolve ids when actually emitting the term. This ought * to be safe since all immediates are the same size. */ @@ -4552,11 +4801,11 @@ static Eterm lcnt_build_result_term(Eterm **hpp, Uint *szp, erts_lcnt_time_t *du dtns = bld_unstable_uint64(hpp, szp, duration->ns); tdt = erts_bld_tuple(hpp, szp, 2, dts, dtns); - adur = erts_atom_put((byte *)str_duration, strlen(str_duration), ERTS_ATOM_ENC_LATIN1, 1); + adur = erts_atom_put((byte *)str_duration, sys_strlen(str_duration), ERTS_ATOM_ENC_LATIN1, 1); tdur = erts_bld_tuple(hpp, szp, 2, adur, tdt); /* lock tuple */ - aloc = erts_atom_put((byte *)str_locks, strlen(str_locks), ERTS_ATOM_ENC_LATIN1, 1); + aloc = erts_atom_put((byte *)str_locks, sys_strlen(str_locks), ERTS_ATOM_ENC_LATIN1, 1); for(i = 0; i < current_locks->size; i++) { lloc = lcnt_build_lock_term(hpp, szp, ¤t_locks->elements[i], lloc); @@ -4610,7 +4859,7 @@ static Eterm lcnt_build_category_list(Eterm **hpp, Uint *szp, erts_lock_flags_t for(i = 0; lcnt_category_map[i].name != NULL; i++) { if(mask & lcnt_category_map[i].flag) { Eterm category = erts_atom_put((byte*)lcnt_category_map[i].name, - strlen(lcnt_category_map[i].name), + sys_strlen(lcnt_category_map[i].name), ERTS_ATOM_ENC_UTF8, 0); res = erts_bld_cons(hpp, szp, category, res); @@ -4740,14 +4989,14 @@ BIF_RETTYPE erts_debug_lcnt_control_2(BIF_ALIST_2) static void os_info_init(void) { - Eterm type = erts_atom_put((byte *) os_type, strlen(os_type), ERTS_ATOM_ENC_LATIN1, 1); + Eterm type = erts_atom_put((byte *) os_type, sys_strlen(os_type), ERTS_ATOM_ENC_LATIN1, 1); Eterm flav; int major, minor, build; char* buf = erts_alloc(ERTS_ALC_T_TMP, 1024); /* More than enough */ Eterm* hp; os_flavor(buf, 1024); - flav = erts_atom_put((byte *) buf, strlen(buf), ERTS_ATOM_ENC_LATIN1, 1); + flav = erts_atom_put((byte *) buf, sys_strlen(buf), ERTS_ATOM_ENC_LATIN1, 1); erts_free(ERTS_ALC_T_TMP, (void *) buf); hp = erts_alloc(ERTS_ALC_T_LITERAL, (3+4)*sizeof(Eterm)); os_type_tuple = TUPLE2(hp, type, flav); @@ -4780,6 +5029,10 @@ erts_bif_info_init(void) = erts_export_put(am_erts_internal, am_gather_microstate_accounting_result, 2); gather_system_check_res_trap = erts_export_put(am_erts_internal, am_gather_system_check_result, 1); + + is_process_alive_trap = erts_export_put(am_erts_internal, am_is_process_alive, 1); + + process_info_init(); os_info_init(); } diff --git a/erts/emulator/beam/erl_bif_port.c b/erts/emulator/beam/erl_bif_port.c index b184adedee..7fe4e02782 100644 --- a/erts/emulator/beam/erl_bif_port.c +++ b/erts/emulator/beam/erl_bif_port.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2017. All Rights Reserved. + * Copyright Ericsson AB 2001-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +43,7 @@ #include "erl_bits.h" #include "erl_bif_unique.h" #include "dtrace-wrapper.h" +#include "erl_proc_sig_queue.h" static Port *open_port(Process* p, Eterm name, Eterm settings, int *err_typep, int *err_nump); static int merge_global_environment(erts_osenv_t *env, Eterm key_value_pairs); @@ -53,10 +54,13 @@ char *erts_default_arg0 = "default"; BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2) { + BIF_RETTYPE ret; Port *port; Eterm res; char *str; int err_type, err_num; + ErtsLinkData *ldp; + ErtsLink *lnk; port = open_port(BIF_P, BIF_ARG_1, BIF_ARG_2, &err_type, &err_num); if (!port) { @@ -71,13 +75,23 @@ BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2) BIF_ERROR(BIF_P, EXC_INTERNAL_ERROR); } else if (err_type == -2) { str = erl_errno_id(err_num); - res = erts_atom_put((byte *) str, strlen(str), ERTS_ATOM_ENC_LATIN1, 1); + res = erts_atom_put((byte *) str, sys_strlen(str), ERTS_ATOM_ENC_LATIN1, 1); } else { res = am_einval; } BIF_RET(res); } + ldp = erts_link_create(ERTS_LNK_TYPE_PORT, BIF_P->common.id, port->common.id); + ASSERT(ldp->a.other.item == port->common.id); + ASSERT(ldp->b.other.item == BIF_P->common.id); + /* + * This link should not already be present, but can potentially + * due to id wrapping... + */ + lnk = erts_link_tree_lookup_insert(&ERTS_P_LINKS(BIF_P), &ldp->a); + erts_link_tree_insert(&ERTS_P_LINKS(port), &ldp->b); + if (port->drv_ptr->flags & ERL_DRV_FLAG_USE_INIT_ACK) { /* Copied from erl_port_task.c */ @@ -86,39 +100,30 @@ BIF_RETTYPE erts_internal_open_port_2(BIF_ALIST_2) erts_make_ref_in_array(port->async_open_port->ref); port->async_open_port->to = BIF_P->common.id; - erts_proc_lock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE | ERTS_PROC_LOCK_LINK); - if (ERTS_PROC_PENDING_EXIT(BIF_P)) { - /* need to exit caller instead */ - erts_proc_unlock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE | ERTS_PROC_LOCK_LINK); - KILL_CATCHES(BIF_P); - BIF_P->freason = EXC_EXIT; - erts_port_release(port); - BIF_RET(am_badarg); - } - - ERTS_MSGQ_MV_INQ2PRIVQ(BIF_P); - BIF_P->msg.save = BIF_P->msg.last; - - erts_proc_unlock(BIF_P, ERTS_PROC_LOCKS_MSG_RECEIVE); + /* + * We unconditionaly *must* do a receive on a message + * containing the reference after this... + */ + ERTS_RECV_MARK_SAVE(BIF_P); + ERTS_RECV_MARK_SET(BIF_P); res = erts_proc_store_ref(BIF_P, port->async_open_port->ref); } else { res = port->common.id; - erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK); } - erts_add_link(&ERTS_P_LINKS(port), LINK_PID, BIF_P->common.id); - erts_add_link(&ERTS_P_LINKS(BIF_P), LINK_PID, port->common.id); - if (IS_TRACED_FL(BIF_P, F_TRACE_PROCS)) - trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, BIF_P, + trace_proc(BIF_P, ERTS_PROC_LOCK_MAIN, BIF_P, am_link, port->common.id); - erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK); + ERTS_BIF_PREP_RET(ret, res); erts_port_release(port); - BIF_RET(res); + if (lnk) + erts_link_release(lnk); + + return ret; } static ERTS_INLINE Port * @@ -195,7 +200,6 @@ BIF_RETTYPE erts_internal_port_command_3(BIF_ALIST_3) #endif switch (erts_port_output(BIF_P, flags, prt, prt->common.id, BIF_ARG_2, &ref)) { - case ERTS_PORT_OP_CALLER_EXIT: case ERTS_PORT_OP_BADARG: case ERTS_PORT_OP_DROPPED: ERTS_BIF_PREP_RET(res, am_badarg); @@ -254,7 +258,6 @@ BIF_RETTYPE erts_internal_port_call_3(BIF_ALIST_3) op = (unsigned int) uint_op; switch (erts_port_call(BIF_P, prt, op, BIF_ARG_3, &retval)) { - case ERTS_PORT_OP_CALLER_EXIT: case ERTS_PORT_OP_DROPPED: case ERTS_PORT_OP_BADARG: retval = am_badarg; @@ -272,11 +275,8 @@ BIF_RETTYPE erts_internal_port_call_3(BIF_ALIST_3) } state = erts_atomic32_read_acqb(&BIF_P->state); - if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) { - if (state & ERTS_PSFLG_PENDING_EXIT) - erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN); + if (state & ERTS_PSFLG_EXITING) ERTS_BIF_EXITED(BIF_P); - } BIF_RET(retval); } @@ -302,7 +302,6 @@ BIF_RETTYPE erts_internal_port_control_3(BIF_ALIST_3) op = (unsigned int) uint_op; switch (erts_port_control(BIF_P, prt, op, BIF_ARG_3, &retval)) { - case ERTS_PORT_OP_CALLER_EXIT: case ERTS_PORT_OP_BADARG: case ERTS_PORT_OP_DROPPED: retval = am_badarg; @@ -320,11 +319,8 @@ BIF_RETTYPE erts_internal_port_control_3(BIF_ALIST_3) } state = erts_atomic32_read_acqb(&BIF_P->state); - if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) { - if (state & ERTS_PSFLG_PENDING_EXIT) - erts_handle_pending_exit(BIF_P, ERTS_PROC_LOCK_MAIN); + if (state & ERTS_PSFLG_EXITING) ERTS_BIF_EXITED(BIF_P); - } BIF_RET(retval); } @@ -347,7 +343,6 @@ BIF_RETTYPE erts_internal_port_close_1(BIF_ALIST_1) BIF_RET(am_badarg); switch (erts_port_exit(BIF_P, 0, prt, BIF_P->common.id, am_normal, &ref)) { - case ERTS_PORT_OP_CALLER_EXIT: case ERTS_PORT_OP_BADARG: case ERTS_PORT_OP_DROPPED: BIF_RET(am_badarg); @@ -380,7 +375,6 @@ BIF_RETTYPE erts_internal_port_connect_2(BIF_ALIST_2) #endif switch (erts_port_connect(BIF_P, 0, prt, BIF_P->common.id, BIF_ARG_2, &ref)) { - case ERTS_PORT_OP_CALLER_EXIT: case ERTS_PORT_OP_BADARG: case ERTS_PORT_OP_DROPPED: BIF_RET(am_badarg); @@ -418,7 +412,6 @@ BIF_RETTYPE erts_internal_port_info_1(BIF_ALIST_1) } switch (erts_port_info(BIF_P, prt, THE_NON_VALUE, &retval)) { - case ERTS_PORT_OP_CALLER_EXIT: case ERTS_PORT_OP_BADARG: BIF_RET(am_badarg); case ERTS_PORT_OP_DROPPED: @@ -457,7 +450,6 @@ BIF_RETTYPE erts_internal_port_info_2(BIF_ALIST_2) } switch (erts_port_info(BIF_P, prt, BIF_ARG_2, &retval)) { - case ERTS_PORT_OP_CALLER_EXIT: case ERTS_PORT_OP_BADARG: BIF_RET(am_badarg); case ERTS_PORT_OP_DROPPED: @@ -1145,7 +1137,7 @@ http_bld_string(struct packet_callback_args* pca, Uint **hpp, Uint *szp, ErlHeapBin* bin = (ErlHeapBin*) *hpp; bin->thing_word = header_heap_bin(len); bin->size = len; - memcpy(bin->data, str, len); + sys_memcpy(bin->data, str, len); *hpp += size; } } @@ -1323,9 +1315,9 @@ int ssl_tls_erl(void* arg, unsigned type, unsigned major, unsigned minor, Eterm bin = new_binary(pca->p, NULL, plen+len); byte* bin_ptr = binary_bytes(bin); - memcpy(bin_ptr+plen, buf, len); + sys_memcpy(bin_ptr+plen, buf, len); if (plen) { - memcpy(bin_ptr, prefix, plen); + sys_memcpy(bin_ptr, prefix, plen); } /* {ssl_tls,NIL,ContentType,{Major,Minor},Bin} */ diff --git a/erts/emulator/beam/erl_bif_re.c b/erts/emulator/beam/erl_bif_re.c index bc819505e7..4d769c2d46 100644 --- a/erts/emulator/beam/erl_bif_re.c +++ b/erts/emulator/beam/erl_bif_re.c @@ -514,7 +514,7 @@ re_version_0(BIF_ALIST_0) Eterm ret; size_t version_size = 0; byte *version = (byte *) erts_pcre_version(); - version_size = strlen((const char *) version); + version_size = sys_strlen((const char *) version); ret = new_binary(BIF_P, version, version_size); BIF_RET(ret); } @@ -998,7 +998,7 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code) has_dupnames = ((options & PCRE_DUPNAMES) != 0); for(i=0;i<top;++i) { - if (last == NULL || !has_dupnames || strcmp((char *) last+2,(char *) nametable+2)) { + if (last == NULL || !has_dupnames || sys_strcmp((char *) last+2,(char *) nametable+2)) { ASSERT(ri->num_spec >= 0); ++(ri->num_spec); if(ri->num_spec > sallocated) { @@ -1048,7 +1048,7 @@ build_capture(Eterm capture_spec[CAPSPEC_SIZE], const pcre *code) (tmpbsiz = ap->len + 1)); } } - memcpy(tmpb,ap->name,ap->len); + sys_memcpy(tmpb,ap->name,ap->len); tmpb[ap->len] = '\0'; } else { ErlDrvSizeT slen; @@ -1210,7 +1210,7 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) erts_pcre_fullinfo(result, NULL, PCRE_INFO_CAPTURECOUNT, &capture_count); ovsize = 3*(capture_count+1); restart.code = erts_alloc(ERTS_ALC_T_RE_SUBJECT, code_size); - memcpy(restart.code, result, code_size); + sys_memcpy(restart.code, result, code_size); erts_pcre_free(result); erts_free(ERTS_ALC_T_RE_TMP_BUF, expr); /*unicode = (pflags & PARSE_FLAG_UNICODE) ? 1 : 0;*/ @@ -1252,7 +1252,7 @@ re_run(Process *p, Eterm arg1, Eterm arg2, Eterm arg3) BIF_ERROR(p, BADARG); } restart.code = erts_alloc(ERTS_ALC_T_RE_SUBJECT, code_size); - memcpy(restart.code, code_tmp, code_size); + sys_memcpy(restart.code, code_tmp, code_size); erts_free_aligned_binary_bytes(temp_alloc); } @@ -1364,7 +1364,7 @@ handle_iolist: RestartContext *restartp = ERTS_MAGIC_BIN_DATA(mbp); Eterm magic_ref; Eterm *hp; - memcpy(restartp,&restart,sizeof(RestartContext)); + sys_memcpy(restartp,&restart,sizeof(RestartContext)); BUMP_ALL_REDS(p); hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE); magic_ref = erts_mk_magic_ref(&hp, &MSO(p), mbp); @@ -1517,7 +1517,7 @@ re_inspect_2(BIF_ALIST_2) last = NULL; name = nametable; for(i=0;i<top;++i) { - if (last == NULL || !has_dupnames || strcmp((char *) last+2, + if (last == NULL || !has_dupnames || sys_strcmp((char *) last+2, (char *) name+2)) { ++num_names; } @@ -1531,9 +1531,9 @@ re_inspect_2(BIF_ALIST_2) name = nametable; j = 0; for(i=0;i<top;++i) { - if (last == NULL || !has_dupnames || strcmp((char *) last+2, + if (last == NULL || !has_dupnames || sys_strcmp((char *) last+2, (char *) name+2)) { - tmp_vec[j++] = new_binary(BIF_P, (byte *) name+2, strlen((char *) name+2)); + tmp_vec[j++] = new_binary(BIF_P, (byte *) name+2, sys_strlen((char *) name+2)); } last = name; name += entrysize; diff --git a/erts/emulator/beam/erl_bif_trace.c b/erts/emulator/beam/erl_bif_trace.c index 22942b40c4..1953f79d79 100644 --- a/erts/emulator/beam/erl_bif_trace.c +++ b/erts/emulator/beam/erl_bif_trace.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2017. All Rights Reserved. + * Copyright Ericsson AB 1999-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,6 +40,7 @@ #include "erl_binary.h" #include "erl_thr_progress.h" #include "erl_bif_unique.h" +#include "erl_proc_sig_queue.h" #define DECL_AM(S) Eterm AM_ ## S = am_atom_put(#S, sizeof(#S) - 1) @@ -1806,9 +1807,6 @@ Eterm erts_seq_trace(Process *p, Eterm arg1, Eterm arg2, return old_value; } else if (arg1 == am_label) { - if (! is_small(arg2)) { - return THE_NON_VALUE; - } new_seq_trace_token(p); if (build_result) { old_value = SEQ_TRACE_TOKEN_LABEL(p); diff --git a/erts/emulator/beam/erl_bif_unique.h b/erts/emulator/beam/erl_bif_unique.h index 9aa631fde9..6af9d4ac8c 100644 --- a/erts/emulator/beam/erl_bif_unique.h +++ b/erts/emulator/beam/erl_bif_unique.h @@ -307,7 +307,7 @@ erts_iref_storage_clean(ErtsIRefStorage *iref) if (iref->is_magic && erts_refc_dectest(&iref->u.mb->intern.refc, 0) == 0) erts_ref_bin_free(iref->u.mb); #ifdef DEBUG - memset((void *) iref, 0xf, sizeof(ErtsIRefStorage)); + sys_memset((void *) iref, 0xf, sizeof(ErtsIRefStorage)); #endif } @@ -342,7 +342,7 @@ erts_iref_storage_make_ref(ErtsIRefStorage *iref, #ifdef DEBUG if (clean_storage) - memset((void *) iref, 0xf, sizeof(ErtsIRefStorage)); + sys_memset((void *) iref, 0xf, sizeof(ErtsIRefStorage)); #endif return make_internal_ref(hp); diff --git a/erts/emulator/beam/erl_bits.h b/erts/emulator/beam/erl_bits.h index b9d141d585..a3816fa820 100644 --- a/erts/emulator/beam/erl_bits.h +++ b/erts/emulator/beam/erl_bits.h @@ -111,7 +111,7 @@ typedef struct erl_bin_match_struct{ #define copy_binary_to_buffer(DstBuffer, DstBufOffset, SrcBuffer, SrcBufferOffset, NumBits) \ do { \ if (BIT_OFFSET(DstBufOffset) == 0 && (SrcBufferOffset == 0) && \ - (BIT_OFFSET(NumBits)==0)) { \ + (BIT_OFFSET(NumBits)==0) && (NumBits != 0)) { \ sys_memcpy(DstBuffer+BYTE_OFFSET(DstBufOffset), \ SrcBuffer, NBYTES(NumBits)); \ } else { \ diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 1ab1c4a363..a76d769283 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -4261,7 +4261,7 @@ erts_ets_colliding_names(Process* p, Eterm name, Uint cnt) while (index >= atom_table_size()) { char tmp[20]; erts_snprintf(tmp, sizeof(tmp), "am%x", atom_table_size()); - erts_atom_put((byte *) tmp, strlen(tmp), ERTS_ATOM_ENC_LATIN1, 1); + erts_atom_put((byte *) tmp, sys_strlen(tmp), ERTS_ATOM_ENC_LATIN1, 1); } list = CONS(hp, make_atom(index), list); hp += 2; diff --git a/erts/emulator/beam/erl_db_util.c b/erts/emulator/beam/erl_db_util.c index e017b9552b..3836f28aa4 100644 --- a/erts/emulator/beam/erl_db_util.c +++ b/erts/emulator/beam/erl_db_util.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2017. All Rights Reserved. + * Copyright Ericsson AB 1998-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,6 +38,7 @@ #include "erl_binary.h" #include "erl_map.h" #include "erl_thr_progress.h" +#include "erl_proc_sig_queue.h" #include "erl_db_util.h" @@ -74,12 +75,35 @@ DBIF_TABLE_GUARD | DBIF_TABLE_BODY | DBIF_TRACE_GUARD | DBIF_TRACE_BODY typedef struct DMC_STACK_TYPE(Type) { \ int pos; \ int siz; \ - Type def[DMC_DEFAULT_SIZE]; \ + int bytes; \ Type *data; \ + Type def[DMC_DEFAULT_SIZE]; \ } DMC_STACK_TYPE(Type) + + +typedef int Dummy; +DMC_DECLARE_STACK_TYPE(Dummy); + +static void dmc_stack_grow(DMC_Dummy_stack* s) +{ + int was_bytes = s->bytes; + s->siz *= 2; + s->bytes *= 2; + if (s->data == s->def) { + s->data = erts_alloc(ERTS_ALC_T_DB_MC_STK, s->bytes); + sys_memcpy(s->data, s->def, was_bytes); + } + else { + s->data = erts_realloc(ERTS_ALC_T_DB_MC_STK, s->data, s->bytes); + } +} -#define DMC_INIT_STACK(Name) \ - (Name).pos = 0; (Name).siz = DMC_DEFAULT_SIZE; (Name).data = (Name).def +#define DMC_INIT_STACK(Name) do { \ + (Name).pos = 0; \ + (Name).siz = DMC_DEFAULT_SIZE; \ + (Name).bytes = sizeof((Name).def); \ + (Name).data = (Name).def; \ +} while (0) #define DMC_STACK_DATA(Name) (Name).data @@ -87,21 +111,19 @@ typedef struct DMC_STACK_TYPE(Type) { \ #define DMC_PUSH(On, What) \ do { \ - if ((On).pos >= (On).siz) { \ - (On).siz *= 2; \ - (On).data \ - = (((On).def == (On).data) \ - ? memcpy(erts_alloc(ERTS_ALC_T_DB_MC_STK, \ - (On).siz*sizeof(*((On).data))), \ - (On).def, \ - DMC_DEFAULT_SIZE*sizeof(*((On).data))) \ - : erts_realloc(ERTS_ALC_T_DB_MC_STK, \ - (void *) (On).data, \ - (On).siz*sizeof(*((On).data)))); \ - } \ + if ((On).pos >= (On).siz) \ + dmc_stack_grow((DMC_Dummy_stack*)&(On)); \ (On).data[(On).pos++] = What; \ } while (0) +#define DMC_PUSH2(On, A, B) \ +do { \ + if ((On).pos+1 >= (On).siz) \ + dmc_stack_grow((DMC_Dummy_stack*)&(On)); \ + (On).data[(On).pos++] = A; \ + (On).data[(On).pos++] = B; \ +} while (0) + #define DMC_POP(From) (From).data[--(From).pos] #define DMC_TOP(From) (From).data[(From).pos - 1] @@ -155,6 +177,7 @@ set_tracee_flags(Process *tracee_p, ErtsTracer tracer, : am_false); erts_tracer_replace(&tracee_p->common, tracer); ERTS_TRACE_FLAGS(tracee_p) = flags; + return ret; } /* @@ -1518,7 +1541,7 @@ restart: context.current_match < num_progs; ++context.current_match) { /* This loop is long, too long */ - memset(heap.vars, 0, heap.size * sizeof(*heap.vars)); + sys_memset(heap.vars, 0, heap.size * sizeof(*heap.vars)); t = context.matchexpr[context.current_match]; context.stack_used = 0; structure_checked = 0; @@ -1537,8 +1560,7 @@ restart: if (is_flatmap(t)) { num_iters = flatmap_get_size(flatmap_val(t)); if (!structure_checked) { - DMC_PUSH(text, matchMap); - DMC_PUSH(text, num_iters); + DMC_PUSH2(text, matchMap, num_iters); } structure_checked = 0; for (i = 0; i < num_iters; ++i) { @@ -1558,8 +1580,7 @@ restart: } goto error; } - DMC_PUSH(text, matchKey); - DMC_PUSH(text, dmc_private_copy(&context, key)); + DMC_PUSH2(text, matchKey, dmc_private_copy(&context, key)); { int old_stack = ++(context.stack_used); Eterm value = flatmap_get_values(flatmap_val(t))[i]; @@ -1587,8 +1608,7 @@ restart: Eterm *kv; num_iters = hashmap_size(t); if (!structure_checked) { - DMC_PUSH(text, matchMap); - DMC_PUSH(text, num_iters); + DMC_PUSH2(text, matchMap, num_iters); } structure_checked = 0; @@ -1614,8 +1634,7 @@ restart: DESTROY_WSTACK(wstack); goto error; } - DMC_PUSH(text, matchKey); - DMC_PUSH(text, dmc_private_copy(&context, key)); + DMC_PUSH2(text, matchKey, dmc_private_copy(&context, key)); { int old_stack = ++(context.stack_used); res = dmc_one_term(&context, &heap, &stack, &text, @@ -1645,8 +1664,7 @@ restart: num_iters = arityval(*tuple_val(t)); if (!structure_checked) { /* i.e. we did not pop it */ - DMC_PUSH(text,matchTuple); - DMC_PUSH(text,num_iters); + DMC_PUSH2(text, matchTuple, num_iters); } structure_checked = 0; for (i = 1; i <= num_iters; ++i) { @@ -2140,7 +2158,7 @@ restart: case matchEqFloat: if (!is_float(*ep)) FAIL(); - if (memcmp(float_val(*ep) + 1, pc, sizeof(double))) + if (sys_memcmp(float_val(*ep) + 1, pc, sizeof(double))) FAIL(); pc += TermWords(2); ++ep; @@ -2490,25 +2508,20 @@ restart: if (have_no_seqtrace(SEQ_TRACE_TOKEN(c_p))) *esp++ = NIL; else { - Eterm sender = SEQ_TRACE_TOKEN_SENDER(c_p); - Uint sender_sz = is_immed(sender) ? 0 : size_object(sender); - ehp = HAllocX(build_proc, 6 + sender_sz, HEAP_XTRA); - if (sender_sz) { - sender = copy_struct(sender, sender_sz, &ehp, &MSO(build_proc)); - } - *esp++ = make_tuple(ehp); - ehp[0] = make_arityval(5); - ehp[1] = SEQ_TRACE_TOKEN_FLAGS(c_p); - ehp[2] = SEQ_TRACE_TOKEN_LABEL(c_p); - ehp[3] = SEQ_TRACE_TOKEN_SERIAL(c_p); - ehp[4] = sender; - ehp[5] = SEQ_TRACE_TOKEN_LASTCNT(c_p); - ASSERT(SEQ_TRACE_TOKEN_ARITY(c_p) == 5); - ASSERT(is_immed(ehp[1])); - ASSERT(is_immed(ehp[2])); - ASSERT(is_immed(ehp[3])); - ASSERT(is_immed(ehp[5])); - } + Eterm token; + Uint token_sz; + + ASSERT(SEQ_TRACE_TOKEN_ARITY(c_p) == 5); + ASSERT(is_immed(SEQ_TRACE_TOKEN_FLAGS(c_p))); + ASSERT(is_immed(SEQ_TRACE_TOKEN_SERIAL(c_p))); + ASSERT(is_immed(SEQ_TRACE_TOKEN_LASTCNT(c_p))); + + token = SEQ_TRACE_TOKEN(c_p); + token_sz = size_object(token); + + ehp = HAllocX(build_proc, token_sz, HEAP_XTRA); + *esp++ = copy_struct(token, token_sz, &ehp, &MSO(build_proc)); + } break; case matchEnableTrace: ASSERT(c_p == self); @@ -2742,8 +2755,8 @@ Eterm db_format_dmc_err_info(Process *p, DMCErrInfo *ei) if (vnum >= 0) erts_snprintf(buff,sizeof(buff)+20,tmp->error_string, vnum); else - strcpy(buff,tmp->error_string); - sl = strlen(buff); + sys_strcpy(buff,tmp->error_string); + sl = sys_strlen(buff); shp = HAlloc(p, sl * 2 + 5); sev = (tmp->severity == dmcWarning) ? am_atom_put("warning",7) : @@ -3535,20 +3548,17 @@ static DMCRet dmc_one_term(DMCContext *context, return retRestart; } if (heap->vars[n].is_bound) { - DMC_PUSH(*text,matchCmp); - DMC_PUSH(*text,n); + DMC_PUSH2(*text, matchCmp, n); } else { /* Not bound, bind! */ if (n >= heap->vars_used) heap->vars_used = n + 1; - DMC_PUSH(*text,matchBind); - DMC_PUSH(*text,n); + DMC_PUSH2(*text, matchBind, n); heap->vars[n].is_bound = 1; } } else if (c == am_Underscore) { DMC_PUSH(*text, matchSkip); } else { /* Any immediate value */ - DMC_PUSH(*text, matchEq); - DMC_PUSH(*text, (Uint) c); + DMC_PUSH2(*text, matchEq, (Uint) c); } break; case TAG_PRIMARY_LIST: @@ -3561,9 +3571,8 @@ static DMCRet dmc_one_term(DMCContext *context, switch ((hdr & _TAG_HEADER_MASK) >> _TAG_PRIMARY_SIZE) { case (_TAG_HEADER_ARITYVAL >> _TAG_PRIMARY_SIZE): n = arityval(*tuple_val(c)); - DMC_PUSH(*text, matchPushT); + DMC_PUSH2(*text, matchPushT, n); ++(context->stack_used); - DMC_PUSH(*text, n); DMC_PUSH(*stack, c); break; case (_TAG_HEADER_MAP >> _TAG_PRIMARY_SIZE): @@ -3571,9 +3580,8 @@ static DMCRet dmc_one_term(DMCContext *context, n = flatmap_get_size(flatmap_val(c)); else n = hashmap_size(c); - DMC_PUSH(*text, matchPushM); + DMC_PUSH2(*text, matchPushM, n); ++(context->stack_used); - DMC_PUSH(*text, n); DMC_PUSH(*stack, c); break; case (_TAG_HEADER_REF >> _TAG_PRIMARY_SIZE): @@ -3598,8 +3606,7 @@ static DMCRet dmc_one_term(DMCContext *context, break; } case (_TAG_HEADER_FLOAT >> _TAG_PRIMARY_SIZE): - DMC_PUSH(*text,matchEqFloat); - DMC_PUSH(*text, (Uint) float_val(c)[1]); + DMC_PUSH2(*text, matchEqFloat, (Uint) float_val(c)[1]); #ifdef ARCH_64 DMC_PUSH(*text, (Uint) 0); #else @@ -3607,8 +3614,7 @@ static DMCRet dmc_one_term(DMCContext *context, #endif break; default: /* BINARY, FUN, VECTOR, or EXTERNAL */ - DMC_PUSH(*text, matchEqBin); - DMC_PUSH(*text, dmc_private_copy(context, c)); + DMC_PUSH2(*text, matchEqBin, dmc_private_copy(context, c)); break; } break; @@ -3658,8 +3664,7 @@ static void do_emit_constant(DMCContext *context, DMC_STACK_TYPE(UWord) *text, emb->next = context->save; context->save = emb; } - DMC_PUSH(*text,matchPushC); - DMC_PUSH(*text,(Uint) tmp); + DMC_PUSH2(*text, matchPushC, (Uint)tmp); if (++context->stack_used > context->stack_need) context->stack_need = context->stack_used; } @@ -3797,8 +3802,7 @@ dmc_tuple(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text, *constant = 1; return retOk; } - DMC_PUSH(*text, matchMkTuple); - DMC_PUSH(*text, nelems); + DMC_PUSH2(*text, matchMkTuple, nelems); context->stack_used -= (nelems - 1); *constant = 0; return retOk; @@ -3825,13 +3829,11 @@ dmc_map(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text, *constant = 1; return retOk; } - DMC_PUSH(*text, matchPushC); - DMC_PUSH(*text, dmc_private_copy(context, m->keys)); + DMC_PUSH2(*text, matchPushC, dmc_private_copy(context, m->keys)); if (++context->stack_used > context->stack_need) { context->stack_need = context->stack_used; } - DMC_PUSH(*text, matchMkFlatMap); - DMC_PUSH(*text, nelems); + DMC_PUSH2(*text, matchMkFlatMap, nelems); context->stack_used -= nelems; *constant = 0; return retOk; @@ -3883,8 +3885,7 @@ dmc_map(DMCContext *context, DMCHeap *heap, DMC_STACK_TYPE(UWord) *text, do_emit_constant(context, text, CDR(kv)); } } - DMC_PUSH(*text, matchMkHashMap); - DMC_PUSH(*text, nelems); + DMC_PUSH2(*text, matchMkHashMap, nelems); context->stack_used -= nelems; DESTROY_WSTACK(wstack); return retOk; @@ -5044,7 +5045,7 @@ static int match_compact(ErlHeapFragment *expr, DMCErrInfo *err_info) ASSERT(j < x); erts_snprintf(buff+1, sizeof(buff) - 1, "%u", (unsigned) j); /* Yes, writing directly into terms, they ARE off heap */ - *p = erts_atom_put((byte *) buff, strlen(buff), + *p = erts_atom_put((byte *) buff, sys_strlen(buff), ERTS_ATOM_ENC_LATIN1, 1); } ++p; @@ -5505,7 +5506,7 @@ void db_match_dis(Binary *bp) ++t; { double num; - memcpy(&num,t,sizeof(double)); + sys_memcpy(&num,t,sizeof(double)); t += TermWords(2); erts_printf("EqFloat\t%f\n", num); } diff --git a/erts/emulator/beam/erl_debug.c b/erts/emulator/beam/erl_debug.c index bf8244564a..db78378257 100644 --- a/erts/emulator/beam/erl_debug.c +++ b/erts/emulator/beam/erl_debug.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1998-2017. All Rights Reserved. + * Copyright Ericsson AB 1998-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -404,15 +404,16 @@ void verify_process(Process *p) erts_exit(ERTS_ERROR_EXIT,"Wild pointer found in " name " of %T!\n",p->common.id); } - ErtsMessage* mp = p->msg.first; - VERBOSE(DEBUG_MEMORY,("Verify process: %T...\n",p->common.id)); - while (mp != NULL) { - VERIFY_ETERM("message term",ERL_MESSAGE_TERM(mp)); - VERIFY_ETERM("message token",ERL_MESSAGE_TOKEN(mp)); - mp = mp->next; - } + ERTS_FOREACH_SIG_PRIVQS( + p, mp, + { + if (ERTS_SIG_IS_MSG(mp)) { + VERIFY_ETERM("message term",ERL_MESSAGE_TERM(mp)); + VERIFY_ETERM("message token",ERL_MESSAGE_TOKEN(mp)); + } + }); erts_check_stack(p); erts_check_heap(p); @@ -532,16 +533,15 @@ static void print_process_memory(Process *p) erts_printf("-- %-*s ---%s-%s-%s-%s--\n", PTR_SIZE, "PCB", dashes, dashes, dashes, dashes); - if (p->msg.first != NULL) { - ErtsMessage* mp; - erts_printf(" Message Queue:\n"); - mp = p->msg.first; - while (mp != NULL) { - erts_printf("| 0x%0*lx | 0x%0*lx |\n",PTR_SIZE, - ERL_MESSAGE_TERM(mp),PTR_SIZE,ERL_MESSAGE_TOKEN(mp)); - mp = mp->next; - } - } + + erts_printf(" Message Queue:\n"); + ERTS_FOREACH_SIG_PRIVQS( + p, mp, + { + if (ERTS_SIG_IS_MSG(mp)) + erts_printf("| 0x%0*lx | 0x%0*lx |\n",PTR_SIZE, + ERL_MESSAGE_TERM(mp),PTR_SIZE,ERL_MESSAGE_TOKEN(mp)); + }); if (p->dictionary != NULL) { int n = ERTS_PD_SIZE(p->dictionary); diff --git a/erts/emulator/beam/erl_drv_thread.c b/erts/emulator/beam/erl_drv_thread.c index 71d4534ef9..4cf42fce57 100644 --- a/erts/emulator/beam/erl_drv_thread.c +++ b/erts/emulator/beam/erl_drv_thread.c @@ -439,7 +439,7 @@ erl_drv_tsd_key_create(char *name, ErlDrvTSDKey *key) name_copy = no_name; else { name_copy = erts_alloc_fnf(ERTS_ALC_T_DRV_TSD, - sizeof(char)*(strlen(name) + 1)); + sizeof(char)*(sys_strlen(name) + 1)); if (!name_copy) { *key = -1; return ENOMEM; @@ -750,7 +750,7 @@ erl_drv_steal_main_thread(char *name, + (name ? sys_strlen(name) + 1 : 0))); if (!dtid) return ENOMEM; - memset(dtid,0,sizeof(ErlDrvTid_)); + sys_memset(dtid,0,sizeof(ErlDrvTid_)); dtid->tid = (void * ) -1; dtid->drv_thr = 1; dtid->func = func; @@ -763,8 +763,8 @@ erl_drv_steal_main_thread(char *name, *tid = NULL; /* Ignore options and name... */ - memcpy(buff,&func,sizeof(void* (*)(void*))); - memcpy(buff + sizeof(void* (*)(void*)),&arg,sizeof(void *)); + sys_memcpy(buff,&func,sizeof(void* (*)(void*))); + sys_memcpy(buff + sizeof(void* (*)(void*)),&arg,sizeof(void *)); write(erts_darwin_main_thread_pipe[1],buff,buff_sz); return 0; } diff --git a/erts/emulator/beam/erl_gc.c b/erts/emulator/beam/erl_gc.c index 1c64644efc..5da8e3eda5 100644 --- a/erts/emulator/beam/erl_gc.c +++ b/erts/emulator/beam/erl_gc.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2002-2017. All Rights Reserved. + * Copyright Ericsson AB 2002-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +43,7 @@ #include "erl_bif_unique.h" #include "dist.h" #include "erl_nfunc_sched.h" +#include "erl_proc_sig_queue.h" #define ERTS_INACT_WR_PB_LEAVE_MUCH_LIMIT 1 #define ERTS_INACT_WR_PB_LEAVE_MUCH_PERCENTAGE 20 @@ -154,7 +155,6 @@ static void offset_rootset(Process *p, Sint offs, char* area, Uint area_size, Eterm* objv, int nobj); static void offset_off_heap(Process* p, Sint offs, char* area, Uint area_size); static void offset_mqueue(Process *p, Sint offs, char* area, Uint area_size); -static void move_msgq_to_heap(Process *p); static int reached_max_heap_size(Process *p, Uint total_heap_size, Uint extra_heap_size, Uint extra_old_heap_size); static void init_gc_info(ErtsGCInfo *gcip); @@ -189,6 +189,21 @@ static struct { ErtsGCInfo info; } dirty_gc; +static void move_msgs_to_heap(Process *c_p) +{ + Uint64 pre_oh, post_oh; + + pre_oh = c_p->off_heap.overhead; + erts_proc_sig_move_msgs_to_heap(c_p); + post_oh = c_p->off_heap.overhead; + + if (pre_oh != post_oh) { + /* Got new binaries; update bin vheap size... */ + c_p->bin_vheap_sz = next_vheap_size(c_p, post_oh, + c_p->bin_vheap_sz); + } +} + static ERTS_INLINE int gc_cost(Uint gc_moved_live_words, Uint resize_moved_words) { @@ -565,20 +580,26 @@ young_gen_usage(Process *p) hsz = p->mbuf_sz; if (p->flags & F_ON_HEAP_MSGQ) { - ErtsMessage *mp; - for (mp = p->msg.first; mp; mp = mp->next) { - /* - * We leave not yet decoded distribution messages - * as they are in the queue since it is not - * possible to determine a maximum size until - * actual decoding. However, we use their estimated - * size when calculating need, and by this making - * it more likely that they will fit on the heap - * when actually decoded. - */ - if (mp->data.attached) - hsz += erts_msg_attached_data_size(mp); - } + ERTS_FOREACH_SIG_PRIVQS( + p, mp, + { + /* + * We leave not yet decoded distribution messages + * as they are in the queue since it is not + * possible to determine a maximum size until + * actual decoding. However, we use their estimated + * size when calculating need, and by this making + * it more likely that they will fit on the heap + * when actually decoded. + * + * We however ignore off heap messages... + */ + if (ERTS_SIG_IS_MSG(mp) + && mp->data.attached + && mp->data.attached != ERTS_MSG_COMBINED_HFRAG) { + hsz += erts_msg_attached_data_size(mp); + } + }); } hsz += p->htop - p->heap; @@ -621,7 +642,7 @@ check_for_possibly_long_gc(Process *p, Uint ygen_usage) sz = ygen_usage; sz += p->hend - p->stop; if (p->flags & F_ON_HEAP_MSGQ) - sz += p->msg.len; + sz += p->sig_qs.len; if (major) sz += p->old_htop - p->old_heap; @@ -761,13 +782,9 @@ do_major_collection: long_gc/large_gc triggers when this happens as process was killed before a GC could be done. */ if (reds == -2) { - ErtsProcLocks locks = ERTS_PROC_LOCKS_ALL; int res; - erts_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR); - erts_send_exit_signal(p, p->common.id, p, &locks, - am_kill, NIL, NULL, 0); - erts_proc_unlock(p, locks & ERTS_PROC_LOCKS_ALL_MINOR); + erts_set_self_exiting(p, am_killed); delay_gc_after_start: /* erts_send_exit_signal looks for ERTS_PSFLG_GC, so @@ -1375,7 +1392,7 @@ minor_collection(Process* p, ErlHeapFragment *live_hf_end, new_sz, objv, nobj); if (p->flags & F_ON_HEAP_MSGQ) - move_msgq_to_heap(p); + move_msgs_to_heap(p); new_mature = p->old_htop - prev_old_htop; @@ -1760,7 +1777,7 @@ major_collection(Process* p, ErlHeapFragment *live_hf_end, HIGH_WATER(p) = HEAP_TOP(p); if (p->flags & F_ON_HEAP_MSGQ) - move_msgq_to_heap(p); + move_msgs_to_heap(p); ErtsGcQuickSanityCheck(p); @@ -2332,9 +2349,9 @@ collect_live_heap_frags(Process* p, ErlHeapFragment *live_hf_end, Eterm* n_htop) return n_htop; } -static ERTS_INLINE void -copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap, - ErlHeapFragment *bp, Eterm *refs, int nrefs) +void +erts_copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap, + ErlHeapFragment *bp, Eterm *refs, int nrefs) { Uint sz; int i; @@ -2440,61 +2457,6 @@ copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap, bp->off_heap.first = NULL; } -static void -move_msgq_to_heap(Process *p) -{ - - ErtsMessage **mpp = &p->msg.first; - Uint64 pre_oh = MSO(p).overhead; - - while (*mpp) { - ErtsMessage *mp = *mpp; - - if (mp->data.attached) { - ErlHeapFragment *bp; - - /* - * We leave not yet decoded distribution messages - * as they are in the queue since it is not - * possible to determine a maximum size until - * actual decoding... - */ - if (is_value(ERL_MESSAGE_TERM(mp))) { - - bp = erts_message_to_heap_frag(mp); - - if (bp->next) - erts_move_multi_frags(&p->htop, &p->off_heap, bp, - mp->m, ERL_MESSAGE_REF_ARRAY_SZ, 0); - else - copy_one_frag(&p->htop, &p->off_heap, bp, - mp->m, ERL_MESSAGE_REF_ARRAY_SZ); - - if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) { - mp->data.heap_frag = NULL; - free_message_buffer(bp); - } - else { - ErtsMessage *new_mp = erts_alloc_message(0, NULL); - sys_memcpy((void *) new_mp->m, (void *) mp->m, - sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ); - erts_msgq_replace_msg_ref(&p->msg, new_mp, mpp); - mp->next = NULL; - erts_cleanup_messages(mp); - mp = new_mp; - } - } - } - - mpp = &(*mpp)->next; - } - - if (pre_oh != MSO(p).overhead) { - /* Got new binaries; update vheap size... */ - BIN_VHEAP_SZ(p) = next_vheap_size(p, MSO(p).overhead, BIN_VHEAP_SZ(p)); - } -} - static Uint setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset) { @@ -2583,11 +2545,10 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset) * We do not have off heap message queue enabled, i.e. we * need to add message queue to rootset... */ - ErtsMessage *mp; /* Ensure large enough rootset... */ - if (n + p->msg.len > rootset->size) { - Uint new_size = n + p->msg.len; + if (n + p->sig_qs.len > rootset->size) { + Uint new_size = n + p->sig_qs.len; ERTS_GC_ASSERT(roots == rootset->def); roots = erts_alloc(ERTS_ALC_T_ROOTSET, new_size*sizeof(Roots)); @@ -2595,18 +2556,19 @@ setup_rootset(Process *p, Eterm *objv, int nobj, Rootset *rootset) rootset->size = new_size; } - for (mp = p->msg.first; mp; mp = mp->next) { - - if (!mp->data.attached) { - /* - * Message may refer data on heap; - * add it to rootset... - */ - roots[n].v = mp->m; - roots[n].sz = ERL_MESSAGE_REF_ARRAY_SZ; - n++; - } - } + ERTS_FOREACH_SIG_PRIVQS( + p, mp, + { + if (ERTS_SIG_IS_INTERNAL_MSG(mp) && !mp->data.attached) { + /* + * Message may refer data on heap; + * add it to rootset... + */ + roots[n].v = mp->m; + roots[n].sz = ERL_MESSAGE_REF_ARRAY_SZ; + n++; + } + }); break; } } @@ -3117,51 +3079,59 @@ offset_off_heap(Process* p, Sint offs, char* area, Uint area_size) } } +#ifndef USE_VM_PROBES +#define ERTS_OFFSET_DT_UTAG(MP, A, ASZ, O) +#else +#define ERTS_OFFSET_DT_UTAG(MP, A, ASZ, O) \ + do { \ + Eterm utag__ = ERL_MESSAGE_DT_UTAG((MP)); \ + if (is_boxed(utag__) && ErtsInArea(ptr_val(utag__), (A), (ASZ))) { \ + ERL_MESSAGE_DT_UTAG((MP)) = offset_ptr(utag__, (O)); \ + } \ + } while (0) +#endif + +static ERTS_INLINE void +offset_message(ErtsMessage *mp, Sint offs, char* area, Uint area_size) +{ + Eterm mesg = ERL_MESSAGE_TERM(mp); + if (ERTS_SIG_IS_MSG_TAG(mesg)) { + if (ERTS_SIG_IS_INTERNAL_MSG_TAG(mesg)) { + switch (primary_tag(mesg)) { + case TAG_PRIMARY_LIST: + case TAG_PRIMARY_BOXED: + if (ErtsInArea(ptr_val(mesg), area, area_size)) { + ERL_MESSAGE_TERM(mp) = offset_ptr(mesg, offs); + } + break; + } + } + mesg = ERL_MESSAGE_TOKEN(mp); + if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) { + ERL_MESSAGE_TOKEN(mp) = offset_ptr(mesg, offs); + } + + ERTS_OFFSET_DT_UTAG(mp, area, area_size, offs); + + ASSERT((is_nil(ERL_MESSAGE_TOKEN(mp)) || + is_tuple(ERL_MESSAGE_TOKEN(mp)) || + is_atom(ERL_MESSAGE_TOKEN(mp)))); + } +} + /* * Offset pointers in message queue. */ static void offset_mqueue(Process *p, Sint offs, char* area, Uint area_size) { - ErtsMessage* mp = p->msg.first; - - if ((p->flags & (F_OFF_HEAP_MSGQ|F_OFF_HEAP_MSGQ_CHNG)) != F_OFF_HEAP_MSGQ) { - - while (mp != NULL) { - Eterm mesg = ERL_MESSAGE_TERM(mp); - if (is_value(mesg)) { - switch (primary_tag(mesg)) { - case TAG_PRIMARY_LIST: - case TAG_PRIMARY_BOXED: - if (ErtsInArea(ptr_val(mesg), area, area_size)) { - ERL_MESSAGE_TERM(mp) = offset_ptr(mesg, offs); - } - break; - } - } - mesg = ERL_MESSAGE_TOKEN(mp); - if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) { - ERL_MESSAGE_TOKEN(mp) = offset_ptr(mesg, offs); - } -#ifdef USE_VM_PROBES - mesg = ERL_MESSAGE_DT_UTAG(mp); - if (is_boxed(mesg) && ErtsInArea(ptr_val(mesg), area, area_size)) { - ERL_MESSAGE_DT_UTAG(mp) = offset_ptr(mesg, offs); - } -#endif - - ASSERT((is_nil(ERL_MESSAGE_TOKEN(mp)) || - is_tuple(ERL_MESSAGE_TOKEN(mp)) || - is_atom(ERL_MESSAGE_TOKEN(mp)))); - mp = mp->next; - } - - } + if ((p->flags & (F_OFF_HEAP_MSGQ|F_OFF_HEAP_MSGQ_CHNG)) != F_OFF_HEAP_MSGQ) + ERTS_FOREACH_SIG_PRIVQS(p, mp, offset_message(mp, offs, area, area_size)); } static void ERTS_INLINE offset_one_rootset(Process *p, Sint offs, char* area, Uint area_size, - Eterm* objv, int nobj) + Eterm* objv, int nobj) { Eterm *v; Uint sz; @@ -3378,7 +3348,6 @@ erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp, }; Eterm res = THE_NON_VALUE; - ErtsMessage *mp; ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == sizeof(tags)/sizeof(*tags)); ERTS_CT_ASSERT(sizeof(values)/sizeof(*values) == ERTS_PROCESS_GC_INFO_MAX_TERMS); @@ -3397,9 +3366,15 @@ erts_process_gc_info(Process *p, Uint *sizep, Eterm **hpp, be the same as adding old_heap_block_size + heap_block_size + mbuf_size. */ - for (mp = p->msg.first; mp; mp = mp->next) - if (mp->data.attached) - values[2] += erts_msg_attached_data_size(mp); + ERTS_FOREACH_SIG_PRIVQS( + p, mp, + { + if (ERTS_SIG_IS_MSG(mp) + && mp->data.attached + && mp->data.attached != ERTS_MSG_COMBINED_HFRAG) { + values[2] += erts_msg_attached_data_size(mp); + } + }); } res = erts_bld_atom_uword_2tup_list(hpp, diff --git a/erts/emulator/beam/erl_gc.h b/erts/emulator/beam/erl_gc.h index dec0ab1143..63d03cdf8b 100644 --- a/erts/emulator/beam/erl_gc.h +++ b/erts/emulator/beam/erl_gc.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2007-2017. All Rights Reserved. + * Copyright Ericsson AB 2007-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -181,6 +181,8 @@ void erts_free_heap_frags(struct process* p); Eterm erts_max_heap_size_map(Sint, Uint, Eterm **, Uint *); int erts_max_heap_size(Eterm, Uint *, Uint *); void erts_deallocate_young_generation(Process *c_p); +void erts_copy_one_frag(Eterm** hpp, ErlOffHeap* off_heap, + ErlHeapFragment *bp, Eterm *refs, int nrefs); #if defined(DEBUG) || defined(ERTS_OFFHEAP_DEBUG) int erts_dbg_within_proc(Eterm *ptr, Process *p, Eterm* real_htop); #endif diff --git a/erts/emulator/beam/erl_goodfit_alloc.c b/erts/emulator/beam/erl_goodfit_alloc.c index a38f6c7daf..e3ba67f0af 100644 --- a/erts/emulator/beam/erl_goodfit_alloc.c +++ b/erts/emulator/beam/erl_goodfit_alloc.c @@ -508,7 +508,7 @@ static struct { static void ERTS_INLINE atom_init(Eterm *atom, char *name) { - *atom = am_atom_put(name, strlen(name)); + *atom = am_atom_put(name, sys_strlen(name)); } #define AM_INIT(AM) atom_init(&am.AM, #AM) diff --git a/erts/emulator/beam/erl_hl_timer.c b/erts/emulator/beam/erl_hl_timer.c index bda2c9b94d..6ec6f8065e 100644 --- a/erts/emulator/beam/erl_hl_timer.c +++ b/erts/emulator/beam/erl_hl_timer.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2015-2017. All Rights Reserved. + * Copyright Ericsson AB 2015-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,6 +38,7 @@ #define ERTS_WANT_TIMER_WHEEL_API #include "erl_time.h" #include "erl_hl_timer.h" +#include "erl_proc_sig_queue.h" #ifdef ERTS_MAGIC_REF_BIF_TIMERS #include "erl_binary.h" #endif @@ -2470,28 +2471,21 @@ access_bif_timer(Process *c_p, Eterm tref, int cancel, int async, int info) req->rrefn[1] = rrefn[1]; req->rrefn[2] = rrefn[2]; - erts_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); - - if (ERTS_PROC_PENDING_EXIT(c_p)) - ERTS_VBUMP_ALL_REDS(c_p); - else { - /* - * Caller needs to wait for a message containing - * the ref that we just created. No such message - * can exist in callers message queue at this time. - * We therefore move the save pointer of the - * callers message queue to the end of the queue. - * - * NOTE: It is of vital importance that the caller - * immediately do a receive unconditionaly - * waiting for the message with the reference; - * otherwise, next receive will *not* work - * as expected! - */ - ERTS_MSGQ_MV_INQ2PRIVQ(c_p); - c_p->msg.save = c_p->msg.last; - } - erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); + /* + * Caller needs to wait for a message containing + * the ref that we just created. No such message + * can exist in callers message queue at this time. + * We therefore move the save pointer of the + * callers message queue to the end of the queue. + * + * NOTE: It is of vital importance that the caller + * immediately do a receive unconditionaly + * waiting for the message with the reference; + * otherwise, next receive will *not* work + * as expected! + */ + ERTS_RECV_MARK_SAVE(c_p); + ERTS_RECV_MARK_SET(c_p); ERTS_BIF_PREP_TRAP1(ret, erts_await_result, c_p, rref); } diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c index e8048cfdfc..179822cc0b 100644 --- a/erts/emulator/beam/erl_init.c +++ b/erts/emulator/beam/erl_init.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2017. All Rights Reserved. + * Copyright Ericsson AB 1997-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,6 +51,8 @@ #include "erl_time.h" #include "erl_check_io.h" #include "erl_osenv.h" +#include "erl_proc_sig_queue.h" + #ifdef HIPE #include "hipe_mode_switch.h" /* for hipe_mode_switch_init() */ #include "hipe_signal.h" /* for hipe_signal_init() */ @@ -60,7 +62,7 @@ # include <sys/resource.h> #endif -#define ERTS_DEFAULT_NO_ASYNC_THREADS 10 +#define ERTS_DEFAULT_NO_ASYNC_THREADS 1 #define ERTS_DEFAULT_SCHED_STACK_SIZE 128 #define ERTS_DEFAULT_DCPU_SCHED_STACK_SIZE 40 @@ -126,6 +128,8 @@ const Eterm etp_hole_marker = 0; static int modified_sched_thread_suggested_stack_size = 0; +Eterm erts_init_process_id; + /* * Note about VxWorks: All variables must be initialized by executable code, * not by an initializer. Otherwise a new instance of the emulator will @@ -238,7 +242,7 @@ progname(char *fullname) { int i; - i = strlen(fullname); + i = sys_strlen(fullname); while (i >= 0) { if ((fullname[i] != '/') && (fullname[i] != '\\')) i--; @@ -304,8 +308,9 @@ erl_init(int ncpu, int node_tab_delete_delay, ErtsDbSpinCount db_spin_count) { + erts_monitor_link_init(); + erts_proc_sig_queue_init(); erts_bif_unique_init(); - erts_init_monitors(); erts_init_time(time_correction, time_warp_mode); erts_init_sys_common_misc(); erts_init_process(ncpu, proc_tab_sz, legacy_proc_tab); @@ -413,7 +418,7 @@ erl_first_process_otp(char* modname, void* code, unsigned size, int argc, char** } static Eterm -erl_system_process_otp(Eterm parent_pid, char* modname, int off_heap_msgq) +erl_system_process_otp(Eterm parent_pid, char* modname, int off_heap_msgq, int prio) { Eterm start_mod; Process* parent; @@ -428,9 +433,16 @@ erl_system_process_otp(Eterm parent_pid, char* modname, int off_heap_msgq) parent = erts_pid2proc(NULL, 0, parent_pid, ERTS_PROC_LOCK_MAIN); - so.flags = erts_default_spo_flags|SPO_SYSTEM_PROC; + so.flags = erts_default_spo_flags|SPO_SYSTEM_PROC|SPO_USE_ARGS; if (off_heap_msgq) so.flags |= SPO_OFF_HEAP_MSGQ; + so.min_heap_size = H_MIN_SIZE; + so.min_vheap_size = BIN_VH_MIN_SIZE; + so.max_heap_size = H_MAX_SIZE; + so.max_heap_flags = H_MAX_FLAGS; + so.priority = prio; + so.max_gen_gcs = (Uint16) erts_atomic32_read_nob(&erts_max_gen_gcs); + so.scheduler = 0; res = erl_create_process(parent, start_mod, am_start, NIL, &so); erts_proc_unlock(parent, ERTS_PROC_LOCK_MAIN); return res; @@ -593,6 +605,10 @@ void erts_usage(void) erts_fprintf(stderr, "-stbt type u|ns|ts|ps|s|nnts|nnps|tnnps|db\n"); erts_fprintf(stderr, "-sbwt val set scheduler busy wait threshold, valid values are:\n"); erts_fprintf(stderr, " none|very_short|short|medium|long|very_long.\n"); + erts_fprintf(stderr, "-sbwtdcpu val set dirty CPU scheduler busy wait threshold, valid values are:\n"); + erts_fprintf(stderr, " none|very_short|short|medium|long|very_long.\n"); + erts_fprintf(stderr, "-sbwtdio val set dirty IO scheduler busy wait threshold, valid values are:\n"); + erts_fprintf(stderr, " none|very_short|short|medium|long|very_long.\n"); erts_fprintf(stderr, "-scl bool enable/disable compaction of scheduler load,\n"); erts_fprintf(stderr, " see the erl(1) documentation for more info.\n"); erts_fprintf(stderr, "-sct cput set cpu topology,\n"); @@ -611,6 +627,10 @@ void erts_usage(void) erts_fprintf(stderr, " very_lazy|lazy|medium|eager|very_eager.\n"); erts_fprintf(stderr, "-swt val set scheduler wakeup threshold, valid values are:\n"); erts_fprintf(stderr, " very_low|low|medium|high|very_high.\n"); + erts_fprintf(stderr, "-swtdcpu val set dirty CPU scheduler wakeup threshold, valid values are:\n"); + erts_fprintf(stderr, " very_low|low|medium|high|very_high.\n"); + erts_fprintf(stderr, "-swtdio val set dirty IO scheduler wakeup threshold, valid values are:\n"); + erts_fprintf(stderr, " very_low|low|medium|high|very_high.\n"); erts_fprintf(stderr, "-sss size suggested stack size in kilo words for scheduler threads,\n"); erts_fprintf(stderr, " valid range is [%d-%d] (default %d)\n", ERTS_SCHED_THREAD_MIN_STACK_SIZE, @@ -810,7 +830,7 @@ early_init(int *argc, char **argv) /* if (argc && argv) { int i = 1; while (i < *argc) { - if (strcmp(argv[i], "--") == 0) { /* end of emulator options */ + if (sys_strcmp(argv[i], "--") == 0) { /* end of emulator options */ i++; break; } @@ -892,7 +912,7 @@ early_init(int *argc, char **argv) /* else if (argv[i][2] == 'D') { char *arg; char *type = argv[i]+3; - if (strncmp(type, "Pcpu", 4) == 0) { + if (sys_strncmp(type, "Pcpu", 4) == 0) { int ptot, ponln; arg = get_arg(argv[i]+7, argv[i+1], &i); switch (sscanf(arg, "%d:%d", &ptot, &ponln)) { @@ -929,7 +949,7 @@ early_init(int *argc, char **argv) /* VERBOSE(DEBUG_SYSTEM, ("using %d:%d dirty CPU scheduler percentages\n", dirty_cpu_scheds_pctg, dirty_cpu_scheds_onln_pctg)); - } else if (strncmp(type, "cpu", 3) == 0) { + } else if (sys_strncmp(type, "cpu", 3) == 0) { int tot, onln; arg = get_arg(argv[i]+6, argv[i+1], &i); switch (sscanf(arg, "%d:%d", &tot, &onln)) { @@ -980,7 +1000,7 @@ early_init(int *argc, char **argv) /* } VERBOSE(DEBUG_SYSTEM, ("using %d:%d dirty CPU scheduler(s)\n", tot, onln)); - } else if (strncmp(type, "io", 2) == 0) { + } else if (sys_strncmp(type, "io", 2) == 0) { arg = get_arg(argv[i]+5, argv[i+1], &i); dirty_io_scheds = atoi(arg); if (dirty_io_scheds < 0 || @@ -1200,7 +1220,6 @@ erl_start(int argc, char **argv) ErtsTimeWarpMode time_warp_mode; int node_tab_delete_delay = ERTS_NODE_TAB_DELAY_GC_DEFAULT; ErtsDbSpinCount db_spin_count = ERTS_DB_SPNCNT_NORMAL; - Eterm otp_ring0_pid; set_default_time_adj(&time_correction, &time_warp_mode); @@ -1242,7 +1261,7 @@ erl_start(int argc, char **argv) if (argv[i][0] != '-') { erts_usage(); } - if (strcmp(argv[i], "--") == 0) { /* end of emulator options */ + if (sys_strcmp(argv[i], "--") == 0) { /* end of emulator options */ i++; break; } @@ -1270,12 +1289,12 @@ erl_start(int argc, char **argv) ("using display items %d\n",display_items)); break; case 'p': - if (!strncmp(argv[i],"-pc",3)) { + if (!sys_strncmp(argv[i],"-pc",3)) { int printable_chars = ERL_PRINTABLE_CHARACTERS_LATIN1; arg = get_arg(argv[i]+3, argv[i+1], &i); - if (!strcmp(arg,"unicode")) { + if (!sys_strcmp(arg,"unicode")) { printable_chars = ERL_PRINTABLE_CHARACTERS_UNICODE; - } else if (strcmp(arg,"latin1")) { + } else if (sys_strcmp(arg,"latin1")) { erts_fprintf(stderr, "bad range of printable " "characters: %s\n", arg); erts_usage(); @@ -1287,7 +1306,7 @@ erl_start(int argc, char **argv) erts_usage(); } case 'f': - if (!strncmp(argv[i],"-fn",3)) { + if (!sys_strncmp(argv[i],"-fn",3)) { int warning_type = ERL_FILENAME_WARNING_WARNING; arg = get_arg(argv[i]+3, argv[i+1], &i); switch (*arg) { @@ -1470,9 +1489,9 @@ erl_start(int argc, char **argv) } } else if (has_prefix("maxk", sub_param)) { arg = get_arg(sub_param+4, argv[i+1], &i); - if (strcmp(arg,"true") == 0) { + if (sys_strcmp(arg,"true") == 0) { H_MAX_FLAGS |= MAX_HEAP_SIZE_KILL; - } else if (strcmp(arg,"false") == 0) { + } else if (sys_strcmp(arg,"false") == 0) { H_MAX_FLAGS &= ~MAX_HEAP_SIZE_KILL; } else { erts_fprintf(stderr, "bad max heap kill %s\n", arg); @@ -1481,9 +1500,9 @@ erl_start(int argc, char **argv) VERBOSE(DEBUG_SYSTEM, ("using max heap kill %d\n", H_MAX_FLAGS)); } else if (has_prefix("maxel", sub_param)) { arg = get_arg(sub_param+5, argv[i+1], &i); - if (strcmp(arg,"true") == 0) { + if (sys_strcmp(arg,"true") == 0) { H_MAX_FLAGS |= MAX_HEAP_SIZE_LOG; - } else if (strcmp(arg,"false") == 0) { + } else if (sys_strcmp(arg,"false") == 0) { H_MAX_FLAGS &= ~MAX_HEAP_SIZE_LOG; } else { erts_fprintf(stderr, "bad max heap error logger %s\n", arg); @@ -1601,7 +1620,7 @@ erl_start(int argc, char **argv) case 'P': /* set maximum number of processes */ arg = get_arg(argv[i]+2, argv[i+1], &i); - if (strcmp(arg, "legacy") == 0) + if (sys_strcmp(arg, "legacy") == 0) legacy_proc_tab = 1; else { errno = 0; @@ -1617,7 +1636,7 @@ erl_start(int argc, char **argv) case 'Q': /* set maximum number of ports */ arg = get_arg(argv[i]+2, argv[i+1], &i); - if (strcmp(arg, "legacy") == 0) + if (sys_strcmp(arg, "legacy") == 0) legacy_port_tab = 1; else { errno = 0; @@ -1635,11 +1654,11 @@ erl_start(int argc, char **argv) case 'S' : /* Was handled in early_init() just read past it */ if (argv[i][2] == 'D') { char* type = argv[i]+3; - if (strcmp(type, "Pcpu") == 0) + if (sys_strcmp(type, "Pcpu") == 0) (void) get_arg(argv[i]+7, argv[i+1], &i); - if (strcmp(type, "cpu") == 0) + if (sys_strcmp(type, "cpu") == 0) (void) get_arg(argv[i]+6, argv[i+1], &i); - else if (strcmp(type, "io") == 0) + else if (sys_strcmp(type, "io") == 0) (void) get_arg(argv[i]+5, argv[i+1], &i); } else if (argv[i][2] == 'P') (void) get_arg(argv[i]+3, argv[i+1], &i); @@ -1676,15 +1695,41 @@ erl_start(int argc, char **argv) erts_usage(); } } + else if (has_prefix("bwtdcpu", sub_param)) { + arg = get_arg(sub_param + 7, argv[i+1], &i); + + if (erts_sched_set_busy_wait_threshold(ERTS_SCHED_DIRTY_CPU, arg) != 0) { + erts_fprintf(stderr, "bad dirty CPU scheduler busy wait threshold: %s\n", + arg); + erts_usage(); + } + + VERBOSE(DEBUG_SYSTEM, + ("dirty CPU scheduler wakeup threshold: %s\n", arg)); + } + else if (has_prefix("bwtdio", sub_param)) { + arg = get_arg(sub_param + 6, argv[i+1], &i); + + if (erts_sched_set_busy_wait_threshold(ERTS_SCHED_DIRTY_IO, arg) != 0) { + erts_fprintf(stderr, "bad dirty IO scheduler busy wait threshold: %s\n", + arg); + erts_usage(); + } + + VERBOSE(DEBUG_SYSTEM, + ("dirty IO scheduler wakeup threshold: %s\n", arg)); + } else if (has_prefix("bwt", sub_param)) { - arg = get_arg(sub_param+3, argv[i+1], &i); - if (erts_sched_set_busy_wait_threshold(arg) != 0) { + arg = get_arg(sub_param + 3, argv[i+1], &i); + + if (erts_sched_set_busy_wait_threshold(ERTS_SCHED_NORMAL, arg) != 0) { erts_fprintf(stderr, "bad scheduler busy wait threshold: %s\n", arg); erts_usage(); } + VERBOSE(DEBUG_SYSTEM, - ("scheduler wakup threshold: %s\n", arg)); + ("scheduler wakeup threshold: %s\n", arg)); } else if (has_prefix("cl", sub_param)) { arg = get_arg(sub_param+2, argv[i+1], &i); @@ -1801,9 +1846,29 @@ erl_start(int argc, char **argv) VERBOSE(DEBUG_SYSTEM, ("scheduler wake cleanup threshold: %s\n", arg)); } + else if (has_prefix("wtdcpu", sub_param)) { + arg = get_arg(sub_param+6, argv[i+1], &i); + if (erts_sched_set_wakeup_other_threshold(ERTS_SCHED_DIRTY_CPU, arg) != 0) { + erts_fprintf(stderr, "dirty CPU scheduler wakeup threshold: %s\n", + arg); + erts_usage(); + } + VERBOSE(DEBUG_SYSTEM, + ("dirty CPU scheduler wakeup threshold: %s\n", arg)); + } + else if (has_prefix("wtdio", sub_param)) { + arg = get_arg(sub_param+5, argv[i+1], &i); + if (erts_sched_set_wakeup_other_threshold(ERTS_SCHED_DIRTY_IO, arg) != 0) { + erts_fprintf(stderr, "dirty IO scheduler wakeup threshold: %s\n", + arg); + erts_usage(); + } + VERBOSE(DEBUG_SYSTEM, + ("dirty IO scheduler wakeup threshold: %s\n", arg)); + } else if (has_prefix("wt", sub_param)) { arg = get_arg(sub_param+2, argv[i+1], &i); - if (erts_sched_set_wakeup_other_thresold(arg) != 0) { + if (erts_sched_set_wakeup_other_threshold(ERTS_SCHED_NORMAL, arg) != 0) { erts_fprintf(stderr, "scheduler wakeup threshold: %s\n", arg); erts_usage(); @@ -1813,7 +1878,7 @@ erl_start(int argc, char **argv) } else if (has_prefix("ws", sub_param)) { arg = get_arg(sub_param+2, argv[i+1], &i); - if (erts_sched_set_wakeup_other_type(arg) != 0) { + if (erts_sched_set_wakeup_other_type(ERTS_SCHED_NORMAL, arg) != 0) { erts_fprintf(stderr, "scheduler wakeup strategy: %s\n", arg); erts_usage(); @@ -2191,8 +2256,8 @@ erl_start(int argc, char **argv) erts_initialized = 1; - otp_ring0_pid = erl_first_process_otp("otp_ring0", NULL, 0, - boot_argc, boot_argv); + erts_init_process_id = erl_first_process_otp("otp_ring0", NULL, 0, + boot_argc, boot_argv); { /* @@ -2202,14 +2267,18 @@ erl_start(int argc, char **argv) */ Eterm pid; - pid = erl_system_process_otp(otp_ring0_pid, "erts_code_purger", !0); + pid = erl_system_process_otp(erts_init_process_id, + "erts_code_purger", !0, + PRIORITY_NORMAL); erts_code_purger = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc, internal_pid_index(pid)); ASSERT(erts_code_purger && erts_code_purger->common.id == pid); erts_proc_inc_refc(erts_code_purger); - pid = erl_system_process_otp(otp_ring0_pid, "erts_literal_area_collector", !0); + pid = erl_system_process_otp(erts_init_process_id, + "erts_literal_area_collector", + !0, PRIORITY_NORMAL); erts_literal_area_collector = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc, internal_pid_index(pid)); @@ -2217,14 +2286,35 @@ erl_start(int argc, char **argv) && erts_literal_area_collector->common.id == pid); erts_proc_inc_refc(erts_literal_area_collector); - pid = erl_system_process_otp(otp_ring0_pid, "erts_dirty_process_code_checker", !0); - erts_dirty_process_code_checker + pid = erl_system_process_otp(erts_init_process_id, + "erts_dirty_process_signal_handler", + !0, PRIORITY_NORMAL); + erts_dirty_process_signal_handler = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc, internal_pid_index(pid)); - ASSERT(erts_dirty_process_code_checker - && erts_dirty_process_code_checker->common.id == pid); - erts_proc_inc_refc(erts_dirty_process_code_checker); - + ASSERT(erts_dirty_process_signal_handler + && erts_dirty_process_signal_handler->common.id == pid); + erts_proc_inc_refc(erts_dirty_process_signal_handler); + + pid = erl_system_process_otp(erts_init_process_id, + "erts_dirty_process_signal_handler", + !0, PRIORITY_HIGH); + erts_dirty_process_signal_handler_high + = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc, + internal_pid_index(pid)); + ASSERT(erts_dirty_process_signal_handler_high + && erts_dirty_process_signal_handler_high->common.id == pid); + erts_proc_inc_refc(erts_dirty_process_signal_handler_high); + + pid = erl_system_process_otp(erts_init_process_id, + "erts_dirty_process_signal_handler", + !0, PRIORITY_MAX); + erts_dirty_process_signal_handler_max + = (Process *) erts_ptab_pix2intptr_ddrb(&erts_proc, + internal_pid_index(pid)); + ASSERT(erts_dirty_process_signal_handler_max + && erts_dirty_process_signal_handler_max->common.id == pid); + erts_proc_inc_refc(erts_dirty_process_signal_handler_max); } erts_start_schedulers(); diff --git a/erts/emulator/beam/erl_instrument.c b/erts/emulator/beam/erl_instrument.c index 634509f880..2f70e7996e 100644 --- a/erts/emulator/beam/erl_instrument.c +++ b/erts/emulator/beam/erl_instrument.c @@ -103,7 +103,7 @@ static struct { static void ERTS_INLINE atom_init(Eterm *atom, const char *name) { - *atom = am_atom_put((char *) name, strlen(name)); + *atom = am_atom_put((char *) name, sys_strlen(name)); } #define AM_INIT(AM) atom_init(&am.AM, #AM) diff --git a/erts/emulator/beam/erl_lock_check.c b/erts/emulator/beam/erl_lock_check.c index 64950fc252..0ced5ec310 100644 --- a/erts/emulator/beam/erl_lock_check.c +++ b/erts/emulator/beam/erl_lock_check.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2017. All Rights Reserved. + * Copyright Ericsson AB 2005-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -92,7 +92,6 @@ static erts_lc_lock_order_t erts_lock_order[] = { { "db_hash_slot", "address" }, { "resource_monitors", "address" }, { "driver_list", NULL }, - { "proc_link", "pid" }, { "proc_msgq", "pid" }, { "proc_btm", "pid" }, { "dist_entry", "address" }, @@ -259,7 +258,7 @@ static ERTS_INLINE void lc_free(void *p) { erts_lc_free_block_t *fb = (erts_lc_free_block_t *) p; #ifdef DEBUG - memset((void *) p, 0xdf, sizeof(erts_lc_free_block_t)); + sys_memset((void *) p, 0xdf, sizeof(erts_lc_free_block_t)); #endif lc_lock(); fb->next = free_blocks; @@ -289,12 +288,12 @@ static void *lc_core_alloc(void) } for (i = 1; i < ERTS_LC_FB_CHUNK_SIZE - 1; i++) { #ifdef DEBUG - memset((void *) &fbs[i], 0xdf, sizeof(erts_lc_free_block_t)); + sys_memset((void *) &fbs[i], 0xdf, sizeof(erts_lc_free_block_t)); #endif fbs[i].next = &fbs[i+1]; } #ifdef DEBUG - memset((void *) &fbs[ERTS_LC_FB_CHUNK_SIZE-1], + sys_memset((void *) &fbs[ERTS_LC_FB_CHUNK_SIZE-1], 0xdf, sizeof(erts_lc_free_block_t)); #endif lc_lock(); @@ -695,7 +694,7 @@ erts_lc_get_lock_order_id(char *name) erts_fprintf(stderr, "Missing lock name\n"); else { for (i = 0; i < ERTS_LOCK_ORDER_SIZE; i++) - if (strcmp(erts_lock_order[i].name, name) == 0) + if (sys_strcmp(erts_lock_order[i].name, name) == 0) return i; erts_fprintf(stderr, "Lock name '%s' missing in lock order " @@ -1322,12 +1321,12 @@ erts_lc_init(void) static erts_lc_free_block_t fbs[ERTS_LC_FB_CHUNK_SIZE]; for (i = 0; i < ERTS_LC_FB_CHUNK_SIZE - 1; i++) { #ifdef DEBUG - memset((void *) &fbs[i], 0xdf, sizeof(erts_lc_free_block_t)); + sys_memset((void *) &fbs[i], 0xdf, sizeof(erts_lc_free_block_t)); #endif fbs[i].next = &fbs[i+1]; } #ifdef DEBUG - memset((void *) &fbs[ERTS_LC_FB_CHUNK_SIZE-1], + sys_memset((void *) &fbs[ERTS_LC_FB_CHUNK_SIZE-1], 0xdf, sizeof(erts_lc_free_block_t)); #endif fbs[ERTS_LC_FB_CHUNK_SIZE-1].next = NULL; diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c index 8047a9567f..4ec6960997 100644 --- a/erts/emulator/beam/erl_map.c +++ b/erts/emulator/beam/erl_map.c @@ -490,7 +490,9 @@ Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks0, Eterm *vs0, sys_memcpy(ks, ks0, n * sizeof(Eterm)); sys_memcpy(vs, vs0, n * sizeof(Eterm)); - erts_validate_and_sort_flatmap(mp); + if (!erts_validate_and_sort_flatmap(mp)) { + return THE_NON_VALUE; + } return make_flatmap(mp); } else { diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c index 6f7c71ef98..51b7865c0b 100644 --- a/erts/emulator/beam/erl_message.c +++ b/erts/emulator/beam/erl_message.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2017. All Rights Reserved. + * Copyright Ericsson AB 1997-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,6 +33,7 @@ #include "erl_binary.h" #include "dtrace-wrapper.h" #include "beam_bp.h" +#include "erl_proc_sig_queue.h" ERTS_SCHED_PREF_QUICK_ALLOC_IMPL(message_ref, ErtsMessageRef, @@ -207,7 +208,7 @@ erts_cleanup_messages(ErtsMessage *msgp) while (mp) { ErtsMessage *fmp; ErlHeapFragment *bp; - if (is_non_value(ERL_MESSAGE_TERM(mp))) { + if (ERTS_SIG_IS_EXTERNAL_MSG(mp)) { if (is_not_immed(ERL_MESSAGE_TOKEN(mp))) { bp = (ErlHeapFragment *) mp->data.dist_ext->ext_endp; erts_cleanup_offheap(&bp->off_heap); @@ -215,10 +216,13 @@ erts_cleanup_messages(ErtsMessage *msgp) if (mp->data.dist_ext) erts_free_dist_ext_copy(mp->data.dist_ext); } - else { - if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) + else { + if (ERTS_SIG_IS_INTERNAL_MSG(mp) + && mp->data.attached != ERTS_MSG_COMBINED_HFRAG) { bp = mp->data.heap_frag; + } else { + mp->data.attached = ERTS_MSG_COMBINED_HFRAG; bp = mp->hfrag.next; erts_cleanup_offheap(&mp->hfrag.off_heap); } @@ -272,6 +276,7 @@ erts_queue_dist_message(Process *rcvr, mp = erts_alloc_message(0, NULL); mp->data.dist_ext = dist_ext; + ERL_MESSAGE_FROM(mp) = dist_ext->dep->sysname; ERL_MESSAGE_TERM(mp) = THE_NON_VALUE; #ifdef USE_VM_PROBES ERL_MESSAGE_DT_UTAG(mp) = NIL; @@ -294,46 +299,15 @@ erts_queue_dist_message(Process *rcvr, } } + state = erts_atomic32_read_acqb(&rcvr->state); - if (state & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING)) { + if (state & ERTS_PSFLG_EXITING) { if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ)) erts_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ); /* Drop message if receiver is exiting or has a pending exit ... */ erts_cleanup_messages(mp); } - else - if (IS_TRACED_FL(rcvr, F_TRACE_RECEIVE)) { - if (from == am_Empty) - from = dist_ext->dep->sysname; - - /* Ahh... need to decode it in order to trace it... */ - if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ)) - erts_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ); - if (!erts_decode_dist_message(rcvr, rcvr_locks, mp, 0)) - erts_free_message(mp); - else { - Eterm msg = ERL_MESSAGE_TERM(mp); - token = ERL_MESSAGE_TOKEN(mp); -#ifdef USE_VM_PROBES - if (DTRACE_ENABLED(message_queued)) { - DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); - - dtrace_proc_str(rcvr, receiver_name); - if (have_seqtrace(token)) { - tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); - tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); - tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); - } - DTRACE6(message_queued, - receiver_name, size_object(msg), rcvr->msg.len, - tok_label, tok_lastcnt, tok_serial); - } -#endif - erts_queue_message(rcvr, rcvr_locks, mp, msg, from); - } - } else { - /* Enqueue message on external format */ #ifdef USE_VM_PROBES if (DTRACE_ENABLED(message_queued)) { @@ -341,7 +315,7 @@ erts_queue_dist_message(Process *rcvr, dtrace_proc_str(rcvr, receiver_name); if (have_seqtrace(token)) { - tok_label = signed_val(SEQ_TRACE_T_LABEL(token)); + tok_label = SEQ_TRACE_T_DTRACE_LABEL(token); tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token)); tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token)); } @@ -349,7 +323,7 @@ erts_queue_dist_message(Process *rcvr, * TODO: We don't know the real size of the external message here. * -1 will appear to a D script as 4294967295. */ - DTRACE6(message_queued, receiver_name, -1, rcvr->msg.len + 1, + DTRACE6(message_queued, receiver_name, -1, rcvr->sig_qs.len + 1, tok_label, tok_lastcnt, tok_serial); } #endif @@ -359,9 +333,7 @@ erts_queue_dist_message(Process *rcvr, if (!(rcvr_locks & ERTS_PROC_LOCK_MSGQ)) erts_proc_unlock(rcvr, ERTS_PROC_LOCK_MSGQ); - erts_proc_notify_new_message(rcvr, - rcvr_locks - ); + erts_proc_notify_new_message(rcvr, rcvr_locks); } } @@ -372,15 +344,14 @@ queue_messages(Process* receiver, ErtsProcLocks receiver_locks, ErtsMessage* first, ErtsMessage** last, - Uint len, - Eterm from) + Uint len) { - ErtsTracingEvent* te; Sint res; int locked_msgq = 0; erts_aint32_t state; ASSERT(is_value(ERL_MESSAGE_TERM(first))); + ASSERT(is_value(ERL_MESSAGE_FROM(first))); ASSERT(ERL_MESSAGE_TOKEN(first) == am_undefined || ERL_MESSAGE_TOKEN(first) == NIL || is_tuple(ERL_MESSAGE_TOKEN(first))); @@ -398,7 +369,7 @@ queue_messages(Process* receiver, state = *receiver_state; else state = erts_atomic32_read_nob(&receiver->state); - if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) + if (state & ERTS_PSFLG_EXITING) goto exiting; need_locks = receiver_locks & ERTS_PROC_LOCKS_HIGHER_THAN(ERTS_PROC_LOCK_MSGQ); @@ -414,7 +385,7 @@ queue_messages(Process* receiver, state = erts_atomic32_read_nob(&receiver->state); - if (state & (ERTS_PSFLG_PENDING_EXIT|ERTS_PSFLG_EXITING)) { + if (state & ERTS_PSFLG_EXITING) { exiting: /* Drop message if receiver is exiting or has a pending exit... */ if (locked_msgq) @@ -423,7 +394,7 @@ queue_messages(Process* receiver, return 0; } - res = receiver->msg.len; + res = receiver->sig_qs.len; if (receiver_locks & ERTS_PROC_LOCK_MAIN) { /* * We move 'in queue' to 'private queue' and place @@ -433,8 +404,8 @@ queue_messages(Process* receiver, * we don't need to include the 'in queue' in * the root set when garbage collecting. */ - res += receiver->msg_inq.len; - ERTS_MSGQ_MV_INQ2PRIVQ(receiver); + res += receiver->sig_inq.len; + erts_proc_sig_fetch(receiver); LINK_MESSAGE_PRIVQ(receiver, first, last, len); } else @@ -442,38 +413,6 @@ queue_messages(Process* receiver, LINK_MESSAGE(receiver, first, last, len); } - if (IS_TRACED_FL(receiver, F_TRACE_RECEIVE) - && (te = &erts_receive_tracing[erts_active_bp_ix()], - te->on)) { - - ErtsMessage *msg = first; - -#ifdef USE_VM_PROBES - if (DTRACE_ENABLED(message_queued)) { - DTRACE_CHARBUF(receiver_name, DTRACE_TERM_BUF_SIZE); - Sint tok_label = 0; - Sint tok_lastcnt = 0; - Sint tok_serial = 0; - Eterm seq_trace_token = ERL_MESSAGE_TOKEN(msg); - - dtrace_proc_str(receiver, receiver_name); - if (seq_trace_token != NIL && is_tuple(seq_trace_token)) { - tok_label = signed_val(SEQ_TRACE_T_LABEL(seq_trace_token)); - tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(seq_trace_token)); - tok_serial = signed_val(SEQ_TRACE_T_SERIAL(seq_trace_token)); - } - DTRACE6(message_queued, - receiver_name, size_object(ERL_MESSAGE_TERM(msg)), - receiver->msg.len, - tok_label, tok_lastcnt, tok_serial); - } -#endif - while (msg) { - trace_receive(receiver, from, ERL_MESSAGE_TERM(msg), te); - msg = msg->next; - } - - } if (locked_msgq) { erts_proc_unlock(receiver, ERTS_PROC_LOCK_MSGQ); } @@ -489,8 +428,9 @@ queue_message(Process* receiver, ErtsMessage* mp, Eterm msg, Eterm from) { ERL_MESSAGE_TERM(mp) = msg; + ERL_MESSAGE_FROM(mp) = from; return queue_messages(receiver, receiver_state, receiver_locks, - mp, &mp->next, 1, from); + mp, &mp->next, 1); } Sint @@ -503,11 +443,10 @@ erts_queue_message(Process* receiver, ErtsProcLocks receiver_locks, Sint erts_queue_messages(Process* receiver, ErtsProcLocks receiver_locks, - ErtsMessage* first, ErtsMessage** last, Uint len, - Eterm from) + ErtsMessage* first, ErtsMessage** last, Uint len) { return queue_messages(receiver, NULL, receiver_locks, - first, last, len, from); + first, last, len); } void @@ -701,7 +640,8 @@ erts_send_message(Process* sender, seq_trace_update_send(sender); seq_trace_output(stoken, message, SEQ_TRACE_SEND, receiver->common.id, sender); - seq_trace_size = 6; /* TUPLE5 */ + + seq_trace_size = size_object(stoken); } #ifdef USE_VM_PROBES if (DT_UTAG_FLAGS(sender) & DT_UTAG_SPREADING) { @@ -750,7 +690,7 @@ erts_send_message(Process* sender, } if (DTRACE_ENABLED(message_send)) { if (have_seqtrace(stoken)) { - tok_label = signed_val(SEQ_TRACE_T_LABEL(stoken)); + tok_label = SEQ_TRACE_T_DTRACE_LABEL(stoken); tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(stoken)); tok_serial = signed_val(SEQ_TRACE_T_SERIAL(stoken)); } @@ -922,70 +862,77 @@ Sint erts_move_messages_off_heap(Process *c_p) { int reds = 1; + int i; + ErtsMessage *msgq[] = {c_p->sig_qs.first, c_p->sig_qs.cont}; /* * Move all messages off heap. This *only* occurs when the * process had off heap message disabled and just enabled * it... */ - ErtsMessage *mp; - reds += c_p->msg.len / 10; + reds += c_p->sig_qs.len / 10; ASSERT(erts_atomic32_read_nob(&c_p->state) & ERTS_PSFLG_OFF_HEAP_MSGQ); ASSERT(c_p->flags & F_OFF_HEAP_MSGQ_CHNG); - for (mp = c_p->msg.first; mp; mp = mp->next) { - Uint msg_sz, token_sz; + for (i = 0; i < sizeof(msgq)/sizeof(msgq[0]); i++) { + ErtsMessage *mp; + for (mp = msgq[i]; mp; mp = mp->next) { + Uint msg_sz, token_sz; #ifdef USE_VM_PROBES - Uint utag_sz; + Uint utag_sz; #endif - Eterm *hp; - ErlHeapFragment *hfrag; + Eterm *hp; + ErlHeapFragment *hfrag; - if (mp->data.attached) - continue; + if (!ERTS_SIG_IS_INTERNAL_MSG(mp)) + continue; - if (is_immed(ERL_MESSAGE_TERM(mp)) + if (mp->data.attached) + continue; + + if (is_immed(ERL_MESSAGE_TERM(mp)) #ifdef USE_VM_PROBES - && is_immed(ERL_MESSAGE_DT_UTAG(mp)) + && is_immed(ERL_MESSAGE_DT_UTAG(mp)) #endif - && is_not_immed(ERL_MESSAGE_TOKEN(mp))) - continue; + && is_not_immed(ERL_MESSAGE_TOKEN(mp))) + continue; - /* - * The message refers into the heap. Copy the message - * from the heap into a heap fragment and attach - * it to the message... - */ - msg_sz = size_object(ERL_MESSAGE_TERM(mp)); + /* + * The message refers into the heap. Copy the message + * from the heap into a heap fragment and attach + * it to the message... + */ + msg_sz = size_object(ERL_MESSAGE_TERM(mp)); #ifdef USE_VM_PROBES - utag_sz = size_object(ERL_MESSAGE_DT_UTAG(mp)); + utag_sz = size_object(ERL_MESSAGE_DT_UTAG(mp)); #endif - token_sz = size_object(ERL_MESSAGE_TOKEN(mp)); + token_sz = size_object(ERL_MESSAGE_TOKEN(mp)); - hfrag = new_message_buffer(msg_sz + hfrag = new_message_buffer(msg_sz #ifdef USE_VM_PROBES - + utag_sz + + utag_sz #endif - + token_sz); - hp = hfrag->mem; - if (is_not_immed(ERL_MESSAGE_TERM(mp))) - ERL_MESSAGE_TERM(mp) = copy_struct(ERL_MESSAGE_TERM(mp), - msg_sz, &hp, - &hfrag->off_heap); - if (is_not_immed(ERL_MESSAGE_TOKEN(mp))) - ERL_MESSAGE_TOKEN(mp) = copy_struct(ERL_MESSAGE_TOKEN(mp), - token_sz, &hp, - &hfrag->off_heap); + + token_sz); + hp = hfrag->mem; + if (is_not_immed(ERL_MESSAGE_TERM(mp))) + ERL_MESSAGE_TERM(mp) = copy_struct(ERL_MESSAGE_TERM(mp), + msg_sz, &hp, + &hfrag->off_heap); + if (is_not_immed(ERL_MESSAGE_TOKEN(mp))) + ERL_MESSAGE_TOKEN(mp) = copy_struct(ERL_MESSAGE_TOKEN(mp), + token_sz, &hp, + &hfrag->off_heap); #ifdef USE_VM_PROBES - if (is_not_immed(ERL_MESSAGE_DT_UTAG(mp))) - ERL_MESSAGE_DT_UTAG(mp) = copy_struct(ERL_MESSAGE_DT_UTAG(mp), - utag_sz, &hp, - &hfrag->off_heap); + if (is_not_immed(ERL_MESSAGE_DT_UTAG(mp))) + ERL_MESSAGE_DT_UTAG(mp) = copy_struct(ERL_MESSAGE_DT_UTAG(mp), + utag_sz, &hp, + &hfrag->off_heap); #endif - mp->data.heap_frag = hfrag; - reds += 1; + mp->data.heap_frag = hfrag; + reds += 1; + } } return reds; @@ -1015,7 +962,7 @@ erts_complete_off_heap_message_queue_change(Process *c_p) else { reds += 2; erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ); - ERTS_MSGQ_MV_INQ2PRIVQ(c_p); + erts_proc_sig_fetch(c_p); erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ); reds += erts_move_messages_off_heap(c_p); } @@ -1225,139 +1172,6 @@ erts_decode_dist_message(Process *proc, ErtsProcLocks proc_locks, return 1; } -/* - * ERTS_INSPECT_MSGQ_KEEP_OH_MSGS == 0 will move off heap messages - * into the heap of the inspected process if off_heap_message_queue - * is false when process_info(_, messages) is called. That is, the - * following GC will have more data in the rootset compared to the - * scenario when process_info(_, messages) had not been called. - * - * ERTS_INSPECT_MSGQ_KEEP_OH_MSGS != 0 will keep off heap messages - * off heap when process_info(_, messages) is called regardless of - * the off_heap_message_queue setting of the process. That is, it - * will change the following execution of the process as little as - * possible. - */ -#define ERTS_INSPECT_MSGQ_KEEP_OH_MSGS 1 - -Uint -erts_prep_msgq_for_inspection(Process *c_p, Process *rp, - ErtsProcLocks rp_locks, ErtsMessageInfo *mip) -{ - Uint tot_heap_size; - ErtsMessage* mp; - Sint i; - int self_on_heap; - - /* - * Prepare the message queue for inspection - * by process_info(). - * - * - * - Decode all messages on external format - * - Remove all corrupt dist messages from queue - * - Save pointer to, and heap size need of each - * message in the mip array. - * - Return total heap size need for all messages - * that needs to be copied. - * - * If ERTS_INSPECT_MSGQ_KEEP_OH_MSGS == 0: - * - In case off heap messages is disabled and - * we are inspecting our own queue, move all - * off heap data into the heap. - */ - - self_on_heap = c_p == rp && !(c_p->flags & F_OFF_HEAP_MSGQ); - - tot_heap_size = 0; - i = 0; - mp = rp->msg.first; - while (mp) { - Eterm msg = ERL_MESSAGE_TERM(mp); - - mip[i].size = 0; - - if (is_non_value(msg)) { - /* Dist message on external format; decode it... */ - if (mp->data.attached) - erts_decode_dist_message(rp, rp_locks, mp, - ERTS_INSPECT_MSGQ_KEEP_OH_MSGS); - - msg = ERL_MESSAGE_TERM(mp); - - if (is_non_value(msg)) { - ErtsMessage **mpp; - ErtsMessage *bad_mp = mp; - /* - * Bad distribution message; remove - * it from the queue... - */ - ASSERT(!mp->data.attached); - - mpp = i == 0 ? &rp->msg.first : &mip[i-1].msgp->next; - - ASSERT(*mpp == bad_mp); - - erts_msgq_update_internal_pointers(&rp->msg, mpp, &bad_mp->next); - - mp = mp->next; - *mpp = mp; - rp->msg.len--; - bad_mp->next = NULL; - erts_cleanup_messages(bad_mp); - continue; - } - } - - ASSERT(is_value(msg)); - -#if ERTS_INSPECT_MSGQ_KEEP_OH_MSGS - if (is_not_immed(msg) && (!self_on_heap || mp->data.attached)) { - Uint sz = size_object(msg); - mip[i].size = sz; - tot_heap_size += sz; - } -#else - if (self_on_heap) { - if (mp->data.attached) { - ErtsMessage *tmp = NULL; - if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) { - erts_link_mbuf_to_proc(rp, mp->data.heap_frag); - mp->data.attached = NULL; - } - else { - /* - * Need to replace the message reference since - * we will get references to the message data - * from the heap... - */ - ErtsMessage **mpp; - tmp = erts_alloc_message(0, NULL); - sys_memcpy((void *) tmp->m, (void *) mp->m, - sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ); - mpp = i == 0 ? &rp->msg.first : &mip[i-1].msgp->next; - erts_msgq_replace_msg_ref(&rp->msg, tmp, mpp); - erts_save_message_in_proc(rp, mp); - mp = tmp; - } - } - } - else if (is_not_immed(msg)) { - Uint sz = size_object(msg); - mip[i].size = sz; - tot_heap_size += sz; - } - -#endif - - mip[i].msgp = mp; - i++; - mp = mp->next; - } - - return tot_heap_size; -} - void erts_factory_proc_init(ErtsHeapFactory* factory, Process* p) { diff --git a/erts/emulator/beam/erl_message.h b/erts/emulator/beam/erl_message.h index a14f4f51d8..f56a252aef 100644 --- a/erts/emulator/beam/erl_message.h +++ b/erts/emulator/beam/erl_message.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1997-2016. All Rights Reserved. + * Copyright Ericsson AB 1997-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,11 @@ #ifndef __ERL_MESSAGE_H__ #define __ERL_MESSAGE_H__ +#include "sys.h" +#define ERTS_PROC_SIG_QUEUE_TYPE_ONLY +#include "erl_proc_sig_queue.h" +#undef ERTS_PROC_SIG_QUEUE_TYPE_ONLY + struct proc_bin; struct external_thing_; @@ -118,15 +123,16 @@ struct erl_heap_fragment { }; /* m[0] = message, m[1] = seq trace token */ -#define ERL_MESSAGE_REF_ARRAY_SZ 2 +#define ERL_MESSAGE_REF_ARRAY_SZ 3 #define ERL_MESSAGE_TERM(mp) ((mp)->m[0]) #define ERL_MESSAGE_TOKEN(mp) ((mp)->m[1]) +#define ERL_MESSAGE_FROM(mp) ((mp)->m[2]) #ifdef USE_VM_PROBES /* m[2] = dynamic trace user tag */ #undef ERL_MESSAGE_REF_ARRAY_SZ -#define ERL_MESSAGE_REF_ARRAY_SZ 3 -#define ERL_MESSAGE_DT_UTAG(mp) ((mp)->m[2]) +#define ERL_MESSAGE_REF_ARRAY_SZ 4 +#define ERL_MESSAGE_DT_UTAG(mp) ((mp)->m[3]) #else #endif @@ -157,28 +163,79 @@ struct erl_mesg { ErlHeapFragment hfrag; }; +/* + * The ErtsMessage struct is only one special type + * of signal. All signal structs have a common + * begining and can be differentiated by looking + * at the ErtsSignal 'common.tag' field. + * + * - An ordinary message will have a value + * - A distribution message that has not been + * decoded yet will have the non-value. + * - Other signals will have an external pid + * header tag. In order to differentiate + * between those signals one needs to look + * at the arity part of the header (see + * erts_proc_sig_queue.h). + */ + +#define ERTS_SIG_IS_NON_MSG_TAG(Tag) \ + (is_external_pid_header((Tag))) + +#define ERTS_SIG_IS_NON_MSG(Sig) \ + ERTS_SIG_IS_NON_MSG_TAG(((ErtsSignal *) (Sig))->common.tag) + +#define ERTS_SIG_IS_INTERNAL_MSG_TAG(Tag) \ + (!is_header((Tag))) +#define ERTS_SIG_IS_INTERNAL_MSG(Sig) \ + ERTS_SIG_IS_INTERNAL_MSG_TAG(((ErtsSignal *) (Sig))->common.tag) + +#define ERTS_SIG_IS_EXTERNAL_MSG_TAG(Tag) \ + ((Tag) == THE_NON_VALUE) +#define ERTS_SIG_IS_EXTERNAL_MSG(Sig) \ + ERTS_SIG_IS_EXTERNAL_MSG_TAG(((ErtsSignal *) (Sig))->common.tag) + +#define ERTS_SIG_IS_MSG_TAG(Tag) \ + (!ERTS_SIG_IS_NON_MSG_TAG(Tag)) +#define ERTS_SIG_IS_MSG(Sig) \ + ERTS_SIG_IS_MSG_TAG(((ErtsSignal *) (Sig))->common.tag) + +typedef union { + ErtsSignalCommon common; + ErtsMessageRef msg; +} ErtsSignal; + +typedef struct { + /* pointers to next pointers pointing to... */ + ErtsMessage **next; /* ... next (non-message) signal */ + ErtsMessage **last; /* ... next (non-message) signal */ +} ErtsMsgQNMSigs; + /* Size of default message buffer (erl_message.c) */ #define ERL_MESSAGE_BUF_SZ 500 typedef struct { - ErtsMessage* first; - ErtsMessage** last; /* point to the last next pointer */ - ErtsMessage** save; - Sint len; /* queue length */ + /* inner queue */ + ErtsMessage *first; + ErtsMessage **last; /* point to the last next pointer */ + ErtsMessage **save; - /* - * The following field is used by the recv_mark/1 and - * recv_set/1 instructions. - */ - ErtsMessage** saved_last; /* saved last pointer */ -} ErlMessageQueue; + /* middle queue */ + ErtsMessage *cont; + ErtsMessage **cont_last; + ErtsMsgQNMSigs nmsigs; + /* Common for inner and middle queue */ + ErtsMessage **saved_last; /* saved last pointer */ + Sint len; /* message queue length (inner+middle) */ +} ErtsSignalPrivQueues; typedef struct { ErtsMessage* first; ErtsMessage** last; /* point to the last next pointer */ Sint len; /* queue length */ -} ErlMessageInQueue; + ErtsMsgQNMSigs nmsigs; +} ErtsSignalInQueue; typedef struct erl_trace_message_queue__ { struct erl_trace_message_queue__ *next; /* point to the next receiver */ @@ -188,9 +245,46 @@ typedef struct erl_trace_message_queue__ { Sint len; /* queue length */ } ErlTraceMessageQueue; +#define ERTS_RECV_MARK_SAVE(P) \ + do { \ + erts_proc_lock((P), ERTS_PROC_LOCK_MSGQ); \ + if ((P)->sig_inq.first) \ + erts_proc_sig_fetch((P)); \ + erts_proc_unlock((P), ERTS_PROC_LOCK_MSGQ); \ + if ((P)->sig_qs.cont) { \ + (P)->sig_qs.saved_last = (P)->sig_qs.cont_last; \ + (P)->flags |= F_DEFERRED_SAVED_LAST; \ + } \ + else { \ + (P)->sig_qs.saved_last = (P)->sig_qs.last; \ + (P)->flags &= ~F_DEFERRED_SAVED_LAST; \ + } \ + } while (0) + +#define ERTS_RECV_MARK_SET(P) \ + do { \ + if ((P)->sig_qs.saved_last) { \ + if ((P)->flags & F_DEFERRED_SAVED_LAST) { \ + /* Points to middle queue; use end of inner */ \ + (P)->sig_qs.save = (P)->sig_qs.last; \ + ASSERT(!PEEK_MESSAGE((P))); \ + } \ + else { \ + /* Points to inner queue; safe to use */ \ + (P)->sig_qs.save = (P)->sig_qs.saved_last; \ + } \ + } \ + } while (0) + +#define ERTS_RECV_MARK_CLEAR(P) \ + do { \ + (P)->sig_qs.saved_last = NULL; \ + (P)->flags &= ~F_DEFERRED_SAVED_LAST; \ + } while (0) + /* Get "current" message */ -#define PEEK_MESSAGE(p) (*(p)->msg.save) +#define PEEK_MESSAGE(p) (*(p)->sig_qs.save) #ifdef USE_VM_PROBES #define LINK_MESSAGE_DTAG(mp, dt) ERL_MESSAGE_DT_UTAG(mp) = dt @@ -198,58 +292,69 @@ typedef struct erl_trace_message_queue__ { #define LINK_MESSAGE_DTAG(mp, dt) #endif -#define LINK_MESSAGE_IMPL(p, first_msg, last_msg, num_msgs, where) do { \ - *(p)->where.last = (first_msg); \ - (p)->where.last = (last_msg); \ - (p)->where.len += (num_msgs); \ - } while(0) +#ifdef USE_VM_PROBES +# define ERTS_MSG_RECV_TRACED(P) \ + ((ERTS_TRACE_FLAGS((P)) & F_TRACE_RECEIVE) \ + || DTRACE_ENABLED(message_queued)) +#else +# define ERTS_MSG_RECV_TRACED(P) \ + (ERTS_TRACE_FLAGS((P)) & F_TRACE_RECEIVE) + +#endif /* Add message last in private message queue */ -#define LINK_MESSAGE_PRIVQ(p, first_msg, last_msg, len) \ +#define LINK_MESSAGE_PRIVQ(p, first_msg, last_msg, num_msgs) \ do { \ - LINK_MESSAGE_IMPL(p, first_msg, last_msg, len, msg); \ + ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__((p), "before"); \ + if ((p)->sig_qs.cont || ERTS_MSG_RECV_TRACED((p))) { \ + *(p)->sig_qs.cont_last = (first_msg); \ + (p)->sig_qs.cont_last = (last_msg); \ + } \ + else { \ + *(p)->sig_qs.last = (first_msg); \ + (p)->sig_qs.last = (last_msg); \ + } \ + (p)->sig_qs.len += (num_msgs); \ + ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__((p), "after"); \ } while (0) /* Add message last_msg in message queue */ -#define LINK_MESSAGE(p, first_msg, last_msg, len) \ - LINK_MESSAGE_IMPL(p, first_msg, last_msg, len, msg_inq) - -#define ERTS_MSGQ_MV_INQ2PRIVQ(p) \ - do { \ - if (p->msg_inq.first) { \ - *p->msg.last = p->msg_inq.first; \ - p->msg.last = p->msg_inq.last; \ - p->msg.len += p->msg_inq.len; \ - p->msg_inq.first = NULL; \ - p->msg_inq.last = &p->msg_inq.first; \ - p->msg_inq.len = 0; \ - } \ - } while (0) - +#define LINK_MESSAGE(p, first_msg, last_msg, num_msgs) \ + do { \ + ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__((p), "before"); \ + *(p)->sig_inq.last = (first_msg); \ + (p)->sig_inq.last = (last_msg); \ + (p)->sig_inq.len += (num_msgs); \ + ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__((p), "before"); \ + } while(0) /* Unlink current message */ -#define UNLINK_MESSAGE(p,msgp) do { \ - ErtsMessage* __mp = (msgp)->next; \ - *(p)->msg.save = __mp; \ - (p)->msg.len--; \ - if (__mp == NULL) \ - (p)->msg.last = (p)->msg.save; \ -} while(0) +#define UNLINK_MESSAGE(p,msgp) \ + do { \ + ErtsMessage *mp__ = (msgp)->next; \ + ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__((p), "before"); \ + *(p)->sig_qs.save = mp__; \ + (p)->sig_qs.len--; \ + if (mp__ == NULL) \ + (p)->sig_qs.last = (p)->sig_qs.save; \ + ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__((p), "after"); \ + } while(0) /* * Reset message save point (after receive match). * Also invalidate the saved position since it may no * longer be safe to use. */ -#define JOIN_MESSAGE(p) do { \ - (p)->msg.save = &(p)->msg.first; \ - (p)->msg.saved_last = 0; \ -} while(0) +#define JOIN_MESSAGE(p) \ + do { \ + (p)->sig_qs.save = &(p)->sig_qs.first; \ + ERTS_RECV_MARK_CLEAR((p)); \ + } while(0) /* Save current message */ #define SAVE_MESSAGE(p) \ - (p)->msg.save = &(*(p)->msg.save)->next + (p)->sig_qs.save = &(*(p)->sig_qs.save)->next #define ERTS_SND_FLG_NO_SEQ_TRACE (((unsigned) 1) << 0) @@ -276,6 +381,7 @@ typedef struct erl_trace_message_queue__ { (MP)->next = NULL; \ ERL_MESSAGE_TERM(MP) = THE_NON_VALUE; \ ERL_MESSAGE_TOKEN(MP) = NIL; \ + ERL_MESSAGE_FROM(MP) = NIL; \ ERL_MESSAGE_DT_UTAG_INIT(MP); \ MP->data.attached = NULL; \ } while (0) @@ -288,7 +394,7 @@ void free_message_buffer(ErlHeapFragment *); void erts_queue_dist_message(Process*, ErtsProcLocks, ErtsDistExternal *, Eterm, Eterm); Sint erts_queue_message(Process*, ErtsProcLocks,ErtsMessage*, Eterm, Eterm); Sint erts_queue_messages(Process*, ErtsProcLocks, - ErtsMessage*, ErtsMessage**, Uint, Eterm); + ErtsMessage*, ErtsMessage**, Uint); void erts_deliver_exit_message(Eterm, Process*, ErtsProcLocks *, Eterm, Eterm); Sint erts_send_message(Process*, Process*, ErtsProcLocks*, Eterm, unsigned); void erts_link_mbuf_to_proc(Process *proc, ErlHeapFragment *bp); @@ -305,16 +411,6 @@ int erts_decode_dist_message(Process *, ErtsProcLocks, ErtsMessage *, int); void erts_cleanup_messages(ErtsMessage *mp); -typedef struct { - Uint size; - ErtsMessage *msgp; -} ErtsMessageInfo; - -Uint erts_prep_msgq_for_inspection(Process *c_p, - Process *rp, - ErtsProcLocks rp_locks, - ErtsMessageInfo *mip); - void *erts_alloc_message_ref(void); void erts_free_message_ref(void *); @@ -356,12 +452,6 @@ ERTS_GLB_FORCE_INLINE ErtsMessage *erts_shrink_message(ErtsMessage *mp, Uint sz, ERTS_GLB_FORCE_INLINE void erts_free_message(ErtsMessage *mp); ERTS_GLB_INLINE Uint erts_used_frag_sz(const ErlHeapFragment*); ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg); -ERTS_GLB_INLINE void erts_msgq_update_internal_pointers(ErlMessageQueue *msgq, - ErtsMessage **newpp, - ErtsMessage **oldpp); -ERTS_GLB_INLINE void erts_msgq_replace_msg_ref(ErlMessageQueue *msgq, - ErtsMessage *newp, - ErtsMessage **oldpp); #define ERTS_MSG_COMBINED_HFRAG ((void *) 0x1) @@ -465,28 +555,6 @@ ERTS_GLB_INLINE Uint erts_msg_attached_data_size(ErtsMessage *msg) } } -ERTS_GLB_INLINE void -erts_msgq_update_internal_pointers(ErlMessageQueue *msgq, - ErtsMessage **newpp, - ErtsMessage **oldpp) -{ - if (msgq->save == oldpp) - msgq->save = newpp; - if (msgq->last == oldpp) - msgq->last = newpp; - if (msgq->saved_last == oldpp) - msgq->saved_last = newpp; -} - -ERTS_GLB_INLINE void -erts_msgq_replace_msg_ref(ErlMessageQueue *msgq, ErtsMessage *newp, ErtsMessage **oldpp) -{ - ErtsMessage *oldp = *oldpp; - newp->next = oldp->next; - erts_msgq_update_internal_pointers(msgq, &newp->next, &oldp->next); - *oldpp = newp; -} - #endif Uint erts_mbuf_size(Process *p); @@ -500,4 +568,17 @@ Uint erts_mbuf_size(Process *p); # define ERTS_CHK_MBUF_SZ(P) ((void) 1) #endif +#define ERTS_FOREACH_SIG_PRIVQS(PROC, MVAR, CODE) \ + do { \ + int i__; \ + ErtsMessage *msgs__[] = {(PROC)->sig_qs.first, \ + (PROC)->sig_qs.cont}; \ + for (i__ = 0; i__ < sizeof(msgs__)/sizeof(msgs__[0]); i__++) { \ + ErtsMessage *MVAR; \ + for (MVAR = msgs__[i__]; MVAR; MVAR = MVAR->next) { \ + CODE; \ + } \ + } \ + } while (0) + #endif diff --git a/erts/emulator/beam/erl_monitor_link.c b/erts/emulator/beam/erl_monitor_link.c new file mode 100644 index 0000000000..70f36fb6b7 --- /dev/null +++ b/erts/emulator/beam/erl_monitor_link.c @@ -0,0 +1,1341 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +/* + * Description: Monitor and link implementation. + * + * Author: Rickard Green + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#include <stddef.h> +#include "global.h" +#include "erl_node_tables.h" +#include "erl_monitor_link.h" + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Red-black tree implementation used for monitors and links. * +\* */ + +static ERTS_INLINE Eterm +ml_get_key(ErtsMonLnkNode *mln) +{ + char *ptr = (char *) mln; + ptr += mln->key_offset; + +#ifdef ERTS_ML_DEBUG + switch (mln->type) { + case ERTS_MON_TYPE_PROC: + case ERTS_MON_TYPE_PORT: + case ERTS_MON_TYPE_DIST_PROC: + case ERTS_MON_TYPE_TIME_OFFSET: + case ERTS_MON_TYPE_RESOURCE: { + ErtsMonitorData *mdp = erts_monitor_to_data(mln); + ERTS_ML_ASSERT(&mdp->ref == (Eterm *) ptr); + break; + } + case ERTS_LNK_TYPE_PROC: + case ERTS_LNK_TYPE_DIST_PROC: + case ERTS_LNK_TYPE_PORT: + case ERTS_MON_TYPE_NODE: + case ERTS_MON_TYPE_NODES: + case ERTS_MON_TYPE_SUSPEND: + ERTS_ML_ASSERT(&mln->other.item == (Eterm *) ptr); + break; + default: + ERTS_INTERNAL_ERROR("Invalid type"); + break; + } +#endif + + return *((Eterm *) ptr); +} + +/* + * Comparison functions for valid *and only valid* keys. That + * is, if the set of valid keys is changed, this function needs + * to be updated... + * + * Note that this function does *not* order terms according to + * term order, and that the order may vary between emulator + * restarts... + */ +static int +ml_cmp_keys(Eterm key1, Eterm key2) +{ + /* + * A monitor key is either an internal pid, a (nodename) atom, + * a small (monitor nodes bitmask), an ordinary internal ref, + * or an external ref. + * + * Order between monitors with keys of different types: + * internal pid < atom < small < internal ref < external ref + * + * A link key is either a pid, an internal port + * + * Order between links with keys of different types: + * internal pid < internal port < external pid + * + */ + ERTS_ML_ASSERT(is_internal_pid(key1) + || is_internal_port(key1) + || is_atom(key1) + || is_small(key1) + || is_external_pid(key1) + || is_internal_ordinary_ref(key1) + || is_external_ref(key1)); + + ERTS_ML_ASSERT(is_internal_pid(key2) + || is_internal_port(key2) + || is_atom(key2) + || is_small(key2) + || is_external_pid(key2) + || is_internal_ordinary_ref(key2) + || is_external_ref(key2)); + + if (is_immed(key1)) { + int key1_tag, key2_tag; + + ERTS_ML_ASSERT(is_internal_pid(key1) + || is_internal_port(key1) + || is_atom(key1) + || is_small(key1)); + + if (key1 == key2) + return 0; + + if (is_boxed(key2)) + return -1; + + ERTS_ML_ASSERT(is_internal_pid(key2) + || is_internal_port(key2) + || is_atom(key2) + || is_small(key2)); + + key1_tag = (int) (key1 & _TAG_IMMED1_MASK); + key2_tag = (int) (key2 & _TAG_IMMED1_MASK); + + if (key1_tag != key2_tag) + return key1_tag - key2_tag; + + ASSERT((is_atom(key1) && is_atom(key2)) + || (is_small(key1) && is_small(key2)) + || (is_internal_pid(key1) && is_internal_pid(key2)) + || (is_internal_port(key1) && is_internal_port(key2))); + + return key1 < key2 ? -1 : 1; + } + else { + Eterm *w1, hdr1; + + ERTS_ML_ASSERT(is_boxed(key1)); + + w1 = boxed_val(key1); + hdr1 = *w1; + + if ((hdr1 & _TAG_HEADER_MASK) == _TAG_HEADER_REF) { + Eterm *w2; + + if (!is_internal_ref(key2)) + return is_immed(key2) ? 1 : -1; + + w2 = internal_ref_val(key2); + + ERTS_ML_ASSERT(w1[0] == ERTS_REF_THING_HEADER); + ERTS_ML_ASSERT(w2[0] == ERTS_REF_THING_HEADER); + + return sys_memcmp((void *) &w1[1], (void *) &w2[1], + (ERTS_REF_THING_SIZE - 1)*sizeof(Eterm)); + } + + ERTS_ML_ASSERT(is_external(key1)); + + if (is_not_external(key2)) + return 1; + else { + Uint ndw1, ndw2; + ExternalThing *et1, *et2; + ErlNode *n1, *n2; + + ERTS_ML_ASSERT((is_external_ref(key1) && is_external_ref(key2)) + || (is_external_pid(key1) && is_external_pid(key2))); + + et1 = (ExternalThing *) w1; + et2 = (ExternalThing *) external_val(key2); + + n1 = et1->node; + n2 = et2->node; + + if (n1 != n2) { + if (n1->sysname != n2->sysname) + return n1->sysname < n2->sysname ? -1 : 1; + ASSERT(n1->creation != n2->creation); + return n1->creation < n2->creation ? -1 : 1; + } + + ndw1 = external_thing_data_words(et1); + ndw2 = external_thing_data_words(et1); + if (ndw1 != ndw2) + return ndw1 < ndw2 ? -1 : 1; + + return sys_memcmp((void *) &et1->data.ui[0], + (void *) &et2->data.ui[0], + ndw1*sizeof(Eterm)); + } + } +} + +#define ERTS_ML_TPFLG_RED (((UWord) 1) << 0) + +#define ERTS_ML_TPFLGS_MASK (ERTS_ML_TPFLG_RED) + +#define ERTS_RBT_PREFIX mon_lnk +#define ERTS_RBT_T ErtsMonLnkNode +#define ERTS_RBT_KEY_T Eterm +#define ERTS_RBT_FLAGS_T UWord +#define ERTS_RBT_INIT_EMPTY_TNODE(T) \ + do { \ + (T)->node.tree.parent = (UWord) NULL; \ + (T)->node.tree.right = NULL; \ + (T)->node.tree.left = NULL; \ + } while (0) +#define ERTS_RBT_IS_RED(T) \ + (!!((T)->node.tree.parent & ERTS_ML_TPFLG_RED)) +#define ERTS_RBT_SET_RED(T) \ + ((T)->node.tree.parent |= ERTS_ML_TPFLG_RED) +#define ERTS_RBT_IS_BLACK(T) \ + (!ERTS_RBT_IS_RED((T))) +#define ERTS_RBT_SET_BLACK(T) \ + ((T)->node.tree.parent &= ~ERTS_ML_TPFLG_RED) +#define ERTS_RBT_GET_FLAGS(T) \ + ((T)->node.tree.parent & ERTS_ML_TPFLGS_MASK) +#define ERTS_RBT_SET_FLAGS(T, F) \ + do { \ + ERTS_ML_ASSERT((((UWord) (F)) & ~ERTS_ML_TPFLGS_MASK) == 0); \ + (T)->node.tree.parent &= ~ERTS_ML_TPFLGS_MASK; \ + (T)->node.tree.parent |= (F); \ + } while (0) +#define ERTS_RBT_GET_PARENT(T) \ + ((ERTS_RBT_T *) ((T)->node.tree.parent & ~ERTS_ML_TPFLGS_MASK)) +#define ERTS_RBT_SET_PARENT(T, P) \ + do { \ + ERTS_ML_ASSERT((((UWord) (P)) & ERTS_ML_TPFLGS_MASK) == 0); \ + (T)->node.tree.parent &= ERTS_ML_TPFLGS_MASK; \ + (T)->node.tree.parent |= (UWord) (P); \ + } while (0) +#define ERTS_RBT_GET_RIGHT(T) ((T)->node.tree.right) +#define ERTS_RBT_SET_RIGHT(T, R) ((T)->node.tree.right = (R)) +#define ERTS_RBT_GET_LEFT(T) ((T)->node.tree.left) +#define ERTS_RBT_SET_LEFT(T, L) ((T)->node.tree.left = (L)) +#define ERTS_RBT_GET_KEY(T) (ml_get_key((T))) +#define ERTS_RBT_CMP_KEYS(KX, KY) (ml_cmp_keys((KX), (KY))) +#define ERTS_RBT_WANT_DELETE +#define ERTS_RBT_WANT_LOOKUP +#define ERTS_RBT_WANT_LOOKUP_INSERT +#define ERTS_RBT_WANT_LOOKUP_CREATE +#define ERTS_RBT_WANT_INSERT +#define ERTS_RBT_WANT_REPLACE +#define ERTS_RBT_WANT_FOREACH +#define ERTS_RBT_WANT_FOREACH_YIELDING +#define ERTS_RBT_WANT_FOREACH_DESTROY +#define ERTS_RBT_WANT_FOREACH_DESTROY_YIELDING +#define ERTS_RBT_UNDEF + +#include "erl_rbtree.h" + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Tree Operations * +\* */ + +static ErtsMonLnkNode * +ml_rbt_lookup(ErtsMonLnkNode *root, Eterm ref) +{ + ErtsMonLnkNode *ml = mon_lnk_rbt_lookup(root, ref); + ASSERT(!ml || (ml->flags & ERTS_ML_FLG_IN_TABLE)); + return ml; +} + +static ErtsMonLnkNode * +ml_rbt_lookup_insert(ErtsMonLnkNode **root, ErtsMonLnkNode *ml) +{ + ErtsMonLnkNode *res; + ERTS_ML_ASSERT(!(ml->flags & ERTS_ML_FLG_IN_TABLE)); + res = mon_lnk_rbt_lookup_insert(root, ml); + if (!res) + ml->flags |= ERTS_ML_FLG_IN_TABLE; + return res; +} + +static ErtsMonLnkNode * +ml_rbt_lookup_create(ErtsMonLnkNode ** root, Eterm key, + ErtsMonLnkNode *(*create)(Eterm, void *), + void *carg, int *created) +{ + ErtsMonLnkNode *ml; + ml = mon_lnk_rbt_lookup_create(root, key, create, carg, created); + if (*created) + ml->flags |= ERTS_ML_FLG_IN_TABLE; + return ml; +} + +static void +ml_rbt_insert(ErtsMonLnkNode **root, ErtsMonLnkNode *ml) +{ +#ifdef ERTS_ML_DEBUG + ErtsMonLnkNode *res; + ERTS_ML_ASSERT(!(ml->flags & ERTS_ML_FLG_IN_TABLE)); + res = ml_rbt_lookup(*root, ml_get_key(ml)); + ERTS_ML_ASSERT(res == NULL); +#endif + + mon_lnk_rbt_insert(root, ml); + ml->flags |= ERTS_ML_FLG_IN_TABLE; +} + +static void +ml_rbt_replace(ErtsMonLnkNode **root, ErtsMonLnkNode *old, ErtsMonLnkNode *new) +{ + ERTS_ML_ASSERT(old->flags & ERTS_ML_FLG_IN_TABLE); + ERTS_ML_ASSERT(!(new->flags & ERTS_ML_FLG_IN_TABLE)); + + mon_lnk_rbt_replace(root, old, new); + old->flags &= ~ERTS_ML_FLG_IN_TABLE; + new->flags |= ERTS_ML_FLG_IN_TABLE; +} + +static void +ml_rbt_delete(ErtsMonLnkNode **root, ErtsMonLnkNode *ml) +{ + ERTS_ML_ASSERT(ml->flags & ERTS_ML_FLG_IN_TABLE); + mon_lnk_rbt_delete(root, ml); + ml->flags &= ~ERTS_ML_FLG_IN_TABLE; +} + + +static void +ml_rbt_foreach(ErtsMonLnkNode *root, + void (*func)(ErtsMonLnkNode *, void *), + void *arg) +{ + mon_lnk_rbt_foreach(root, func, arg); +} + +typedef struct { + ErtsMonLnkNode *root; + mon_lnk_rbt_yield_state_t rbt_ystate; +} ErtsMonLnkYieldState; + +static int +ml_rbt_foreach_yielding(ErtsMonLnkNode *root, + void (*func)(ErtsMonLnkNode *, void *), + void *arg, + void **vyspp, + Sint limit) +{ + int res; + ErtsMonLnkYieldState ys = {root, ERTS_RBT_YIELD_STAT_INITER}; + ErtsMonLnkYieldState *ysp; + + ysp = (ErtsMonLnkYieldState *) *vyspp; + if (!ysp) + ysp = &ys; + res = mon_lnk_rbt_foreach_yielding(ysp->root, func, arg, + &ysp->rbt_ystate, limit); + if (res == 0) { + if (ysp != &ys) + erts_free(ERTS_ALC_T_ML_YIELD_STATE, ysp); + *vyspp = NULL; + } + else { + + if (ysp == &ys) { + ysp = erts_alloc(ERTS_ALC_T_ML_YIELD_STATE, + sizeof(ErtsMonLnkYieldState)); + sys_memcpy((void *) ysp, (void *) &ys, + sizeof(ErtsMonLnkYieldState)); + } + + *vyspp = (void *) ysp; + } + + return res; +} + +typedef struct { + void (*func)(ErtsMonLnkNode *, void *); + void *arg; +} ErtsMonLnkForeachDeleteContext; + +static void +rbt_wrap_foreach_delete(ErtsMonLnkNode *ml, void *vctxt) +{ + ErtsMonLnkForeachDeleteContext *ctxt = vctxt; + ERTS_ML_ASSERT(ml->flags & ERTS_ML_FLG_IN_TABLE); + ml->flags &= ~ERTS_ML_FLG_IN_TABLE; + ctxt->func(ml, ctxt->arg); +} + +static void +ml_rbt_foreach_delete(ErtsMonLnkNode **root, + void (*func)(ErtsMonLnkNode *, void *), + void *arg) +{ + ErtsMonLnkForeachDeleteContext ctxt; + ctxt.func = func; + ctxt.arg = arg; + mon_lnk_rbt_foreach_destroy(root, + rbt_wrap_foreach_delete, + (void *) &ctxt); +} + +static int +ml_rbt_foreach_delete_yielding(ErtsMonLnkNode **root, + void (*func)(ErtsMonLnkNode *, void *), + void *arg, + void **vyspp, + Sint limit) +{ + int res; + ErtsMonLnkYieldState ys = {*root, ERTS_RBT_YIELD_STAT_INITER}; + ErtsMonLnkYieldState *ysp; + ErtsMonLnkForeachDeleteContext ctxt; + ctxt.func = func; + ctxt.arg = arg; + + ysp = (ErtsMonLnkYieldState *) *vyspp; + if (!ysp) { + *root = NULL; + ysp = &ys; + } + res = mon_lnk_rbt_foreach_destroy_yielding(&ysp->root, + rbt_wrap_foreach_delete, + (void *) &ctxt, + &ysp->rbt_ystate, + limit); + if (res == 0) { + if (ysp != &ys) + erts_free(ERTS_ALC_T_ML_YIELD_STATE, ysp); + *vyspp = NULL; + } + else { + + if (ysp == &ys) { + ysp = erts_alloc(ERTS_ALC_T_ML_YIELD_STATE, + sizeof(ErtsMonLnkYieldState)); + sys_memcpy((void *) ysp, (void *) &ys, + sizeof(ErtsMonLnkYieldState)); + } + + *vyspp = (void *) ysp; + } + + return res; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * List Operations * +\* */ + +static int +ml_dl_list_foreach_yielding(ErtsMonLnkNode *list, + void (*func)(ErtsMonLnkNode *, void *), + void *arg, + void **vyspp, + Sint limit) +{ + Sint cnt = 0; + ErtsMonLnkNode *ml = (ErtsMonLnkNode *) *vyspp; + + ERTS_ML_ASSERT(!ml || list); + + if (!ml) + ml = list; + + if (ml) { + do { + ERTS_ML_ASSERT(ml->flags & ERTS_ML_FLG_IN_TABLE); + func(ml, arg); + ml = ml->node.list.next; + cnt++; + } while (ml != list && cnt < limit); + if (ml != list) { + *vyspp = (void *) ml; + return 1; /* yield */ + } + } + + *vyspp = NULL; + return 0; /* done */ +} + +static int +ml_dl_list_foreach_delete_yielding(ErtsMonLnkNode **list, + void (*func)(ErtsMonLnkNode *, void *), + void *arg, + void **vyspp, + Sint limit) +{ + Sint cnt = 0; + ErtsMonLnkNode *first = *list; + ErtsMonLnkNode *ml = (ErtsMonLnkNode *) *vyspp; + + ERTS_ML_ASSERT(!ml || first); + + if (!ml) + ml = first; + + if (ml) { + do { + ErtsMonLnkNode *next = ml->node.list.next; + ERTS_ML_ASSERT(ml->flags & ERTS_ML_FLG_IN_TABLE); + ml->flags &= ~ERTS_ML_FLG_IN_TABLE; + func(ml, arg); + ml = next; + cnt++; + } while (ml != first && cnt < limit); + if (ml != first) { + *vyspp = (void *) ml; + return 1; /* yield */ + } + } + + *vyspp = NULL; + *list = NULL; + return 0; /* done */ +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Misc * +\* */ + +ErtsMonLnkDist * +erts_mon_link_dist_create(Eterm nodename) +{ + ErtsMonLnkDist *mld = erts_alloc(ERTS_ALC_T_ML_DIST, + sizeof(ErtsMonLnkDist)); + mld->nodename = nodename; + mld->connection_id = ~((Uint32) 0); + erts_atomic_init_nob(&mld->refc, 1); + erts_mtx_init(&mld->mtx, "dist_entry_links", nodename, + ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION); + mld->alive = !0; + mld->links = NULL; + mld->monitors = NULL; + mld->orig_name_monitors = NULL; + return mld; +} + +void +erts_mon_link_dist_destroy__(ErtsMonLnkDist *mld) +{ + ERTS_ML_ASSERT(erts_atomic_read_nob(&mld->refc) == 0); + ERTS_ML_ASSERT(!mld->alive); + ERTS_ML_ASSERT(!mld->links); + ERTS_ML_ASSERT(!mld->monitors); + ERTS_ML_ASSERT(!mld->orig_name_monitors); + + erts_mtx_destroy(&mld->mtx); + erts_free(ERTS_ALC_T_ML_DIST, mld); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Monitor Operations * +\* */ + +#ifdef ERTS_ML_DEBUG +size_t erts_monitor_origin_offset; +size_t erts_monitor_origin_key_offset; +size_t erts_monitor_target_offset; +size_t erts_monitor_target_key_offset; +size_t erts_monitor_node_key_offset; +#endif + +static ERTS_INLINE void +monitor_init(void) +{ +#ifdef ERTS_ML_DEBUG + erts_monitor_origin_offset = offsetof(ErtsMonitorData, origin); + erts_monitor_origin_key_offset = offsetof(ErtsMonitorData, ref); + ASSERT(erts_monitor_origin_key_offset >= erts_monitor_origin_offset); + erts_monitor_origin_key_offset -= erts_monitor_origin_offset; + erts_monitor_target_offset = offsetof(ErtsMonitorData, target); + erts_monitor_target_key_offset = offsetof(ErtsMonitorData, ref); + ASSERT(erts_monitor_target_key_offset >= erts_monitor_target_offset); + erts_monitor_target_key_offset -= erts_monitor_target_offset; + erts_monitor_node_key_offset = offsetof(ErtsMonitor, other.item); +#endif +} + +ErtsMonitor * +erts_monitor_tree_lookup(ErtsMonitor *root, Eterm key) +{ + ERTS_ML_ASSERT(is_internal_ordinary_ref(key) + || is_external_ref(key) + || is_atom(key) + || is_small(key) + || is_internal_pid(key)); + return (ErtsMonitor *) ml_rbt_lookup((ErtsMonLnkNode *) root, key); +} + +ErtsMonitor * +erts_monotor_tree_lookup_insert(ErtsMonitor **root, ErtsMonitor *mon) +{ + return (ErtsMonitor *) ml_rbt_lookup_insert((ErtsMonLnkNode **) root, + (ErtsMonLnkNode *) mon); +} + +typedef struct { + Uint16 type; + Eterm origin; +} ErtsMonitorCreateCtxt; + +static ErtsMonLnkNode * +create_monitor(Eterm target, void *vcctxt) +{ + ErtsMonitorCreateCtxt *cctxt = vcctxt; + ErtsMonitorData *mdp = erts_monitor_create(cctxt->type, + NIL, + cctxt->origin, + target, + NIL); + ERTS_ML_ASSERT(ml_cmp_keys(ml_get_key(&mdp->origin), target) == 0); + return (ErtsMonLnkNode *) &mdp->origin; +} + +ErtsMonitor * +erts_monitor_tree_lookup_create(ErtsMonitor **root, int *created, Uint16 type, + Eterm origin, Eterm target) +{ + ErtsMonitor *res; + ErtsMonitorCreateCtxt cctxt = {type, origin}; + + ERTS_ML_ASSERT(type == ERTS_MON_TYPE_NODE || type == ERTS_MON_TYPE_NODES); + + res = (ErtsMonitor *) ml_rbt_lookup_create((ErtsMonLnkNode **) root, + target, create_monitor, + (void *) &cctxt, + created); + + ERTS_ML_ASSERT(res && erts_monitor_is_origin(res)); + + return res; +} + +void +erts_monitor_tree_insert(ErtsMonitor **root, ErtsMonitor *mon) +{ + ml_rbt_insert((ErtsMonLnkNode **) root, (ErtsMonLnkNode *) mon); +} + +void +erts_monitor_tree_replace(ErtsMonitor **root, ErtsMonitor *old, ErtsMonitor *new) +{ + ml_rbt_replace((ErtsMonLnkNode **) root, + (ErtsMonLnkNode *) old, + (ErtsMonLnkNode *) new); +} + +void +erts_monitor_tree_delete(ErtsMonitor **root, ErtsMonitor *mon) +{ + ml_rbt_delete((ErtsMonLnkNode **) root, (ErtsMonLnkNode *) mon); +} + +void +erts_monitor_tree_foreach(ErtsMonitor *root, + void (*func)(ErtsMonitor *, void *), + void *arg) +{ + ml_rbt_foreach((ErtsMonLnkNode *) root, + (void (*)(ErtsMonLnkNode*, void*)) func, + arg); +} + +int +erts_monitor_tree_foreach_yielding(ErtsMonitor *root, + void (*func)(ErtsMonitor *, void *), + void *arg, + void **vyspp, + Sint limit) +{ + return ml_rbt_foreach_yielding((ErtsMonLnkNode *) root, + (void (*)(ErtsMonLnkNode*, void*)) func, + arg, vyspp, limit); +} + +void +erts_monitor_tree_foreach_delete(ErtsMonitor **root, + void (*func)(ErtsMonitor *, void *), + void *arg) +{ + ml_rbt_foreach_delete((ErtsMonLnkNode **) root, + (void (*)(ErtsMonLnkNode*, void*)) func, + arg); +} + +int +erts_monitor_tree_foreach_delete_yielding(ErtsMonitor **root, + void (*func)(ErtsMonitor *, void *), + void *arg, + void **vyspp, + Sint limit) +{ + return ml_rbt_foreach_delete_yielding((ErtsMonLnkNode **) root, + (void (*)(ErtsMonLnkNode*, void*)) func, + arg, vyspp, limit); +} + +void +erts_monitor_list_foreach(ErtsMonitor *list, + void (*func)(ErtsMonitor *, void *), + void *arg) +{ + void *ystate = NULL; + while (ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list, + (void (*)(ErtsMonLnkNode *, void *)) func, + arg, &ystate, (Sint) INT_MAX)); +} + +int +erts_monitor_list_foreach_yielding(ErtsMonitor *list, + void (*func)(ErtsMonitor *, void *), + void *arg, + void **vyspp, + Sint limit) +{ + return ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list, + (void (*)(ErtsMonLnkNode *, void *)) func, + arg, vyspp, limit); +} + +void +erts_monitor_list_foreach_delete(ErtsMonitor **list, + void (*func)(ErtsMonitor *, void *), + void *arg) +{ + void *ystate = NULL; + while (ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list, + (void (*)(ErtsMonLnkNode*, void*)) func, + arg, &ystate, (Sint) INT_MAX)); +} + +int +erts_monitor_list_foreach_delete_yielding(ErtsMonitor **list, + void (*func)(ErtsMonitor *, void *), + void *arg, + void **vyspp, + Sint limit) +{ + return ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list, + (void (*)(ErtsMonLnkNode*, void*)) func, + arg, vyspp, limit); +} + +ErtsMonitorData * +erts_monitor_create(Uint16 type, Eterm ref, Eterm orgn, Eterm trgt, Eterm name) +{ + ErtsMonitorData *mdp; + + switch (type) { + case ERTS_MON_TYPE_PROC: + case ERTS_MON_TYPE_PORT: + case ERTS_MON_TYPE_TIME_OFFSET: + if (is_nil(name)) { + ErtsMonitorDataHeap *mdhp; + ErtsORefThing *ortp; + + ERTS_ML_ASSERT(is_immed(orgn) && is_immed(trgt)); + ERTS_ML_ASSERT(is_internal_ordinary_ref(ref)); + + mdhp = erts_alloc(ERTS_ALC_T_MONITOR, sizeof(ErtsMonitorDataHeap)); + mdp = &mdhp->md; + ERTS_ML_ASSERT(((void *) mdp) == ((void *) mdhp)); + + ortp = (ErtsORefThing *) (char *) internal_ref_val(ref); + mdhp->oref_thing = *ortp; + mdp->ref = make_internal_ref(&mdhp->oref_thing.header); + + mdp->origin.other.item = trgt; + mdp->origin.offset = (Uint16) offsetof(ErtsMonitorData, origin); + mdp->origin.key_offset = (Uint16) offsetof(ErtsMonitorData, ref); + ERTS_ML_ASSERT(mdp->origin.key_offset >= mdp->origin.offset); + mdp->origin.key_offset -= mdp->origin.offset; + mdp->origin.flags = (Uint16) 0; + mdp->origin.type = type; + + mdp->target.other.item = orgn; + mdp->target.offset = (Uint16) offsetof(ErtsMonitorData, target); + mdp->target.key_offset = (Uint16) offsetof(ErtsMonitorData, ref); + ERTS_ML_ASSERT(mdp->target.key_offset >= mdp->target.offset); + mdp->target.key_offset -= mdp->target.offset; + mdp->target.flags = ERTS_ML_FLG_TARGET; + mdp->target.type = type; + break; + } + case ERTS_MON_TYPE_DIST_PROC: + case ERTS_MON_TYPE_RESOURCE: + case ERTS_MON_TYPE_NODE: + case ERTS_MON_TYPE_NODES: { + ErtsMonitorDataExtended *mdep; + Uint size = sizeof(ErtsMonitorDataExtended) - sizeof(Eterm); + Uint rsz, osz, tsz; + Eterm *hp; + ErlOffHeap oh; + Uint16 name_flag = is_nil(name) ? ((Uint16) 0) : ERTS_ML_FLG_NAME; + + rsz = is_immed(ref) ? 0 : size_object(ref); + tsz = is_immed(trgt) ? 0 : size_object(trgt); + if (type == ERTS_MON_TYPE_RESOURCE) + osz = 0; + else + osz = is_immed(orgn) ? 0 : size_object(orgn); + + size += (rsz + osz + tsz) * sizeof(Eterm); + + mdep = erts_alloc(ERTS_ALC_T_MONITOR_EXT, size); + + ERTS_INIT_OFF_HEAP(&oh); + + hp = &mdep->heap[0]; + + mdp = &mdep->md; + ERTS_ML_ASSERT(((void *) mdp) == ((void *) mdep)); + + mdp->ref = rsz ? copy_struct(ref, rsz, &hp, &oh) : ref; + + mdp->origin.other.item = tsz ? copy_struct(trgt, tsz, &hp, &oh) : trgt; + mdp->origin.offset = (Uint16) offsetof(ErtsMonitorData, origin); + mdp->origin.flags = ERTS_ML_FLG_EXTENDED|name_flag; + mdp->origin.type = type; + + if (type == ERTS_MON_TYPE_RESOURCE) + mdp->target.other.ptr = (void *) orgn; + else + mdp->target.other.item = osz ? copy_struct(orgn, osz, &hp, &oh) : orgn; + mdp->target.offset = (Uint16) offsetof(ErtsMonitorData, target); + mdp->target.flags = ERTS_ML_FLG_TARGET|ERTS_ML_FLG_EXTENDED|name_flag; + mdp->target.type = type; + + if (type == ERTS_MON_TYPE_NODE || type == ERTS_MON_TYPE_NODES) { + mdep->u.refc = 0; + mdp->origin.key_offset = (Uint16) offsetof(ErtsMonitor, other.item); + mdp->target.key_offset = (Uint16) offsetof(ErtsMonitor, other.item); + ERTS_ML_ASSERT(!oh.first); + mdep->uptr.node_monitors = NULL; + } + else { + mdep->u.name = name; + + mdp->origin.key_offset = (Uint16) offsetof(ErtsMonitorData, ref); + ERTS_ML_ASSERT(mdp->origin.key_offset >= mdp->origin.offset); + mdp->origin.key_offset -= mdp->origin.offset; + + mdp->target.key_offset = (Uint16) offsetof(ErtsMonitorData, ref); + ERTS_ML_ASSERT(mdp->target.key_offset >= mdp->target.offset); + mdp->target.key_offset -= mdp->target.offset; + + mdep->uptr.ohhp = oh.first; + } + mdep->dist = NULL; + break; + } + case ERTS_MON_TYPE_SUSPEND: + ERTS_INTERNAL_ERROR("Use erts_monitor_suspend_create() instead..."); + mdp = NULL; + break; + default: + ERTS_INTERNAL_ERROR("Invalid monitor type"); + mdp = NULL; + break; + } + + erts_atomic32_init_nob(&mdp->refc, 2); + + return mdp; +} + +/* + * erts_monitor_destroy__() should only be called from + * erts_monitor_release() or erts_monitor_release_both(). + */ +void +erts_monitor_destroy__(ErtsMonitorData *mdp) +{ + ERTS_ML_ASSERT(erts_atomic32_read_nob(&mdp->refc) == 0); + ERTS_ML_ASSERT(!(mdp->origin.flags & ERTS_ML_FLG_IN_TABLE)); + ERTS_ML_ASSERT(!(mdp->target.flags & ERTS_ML_FLG_IN_TABLE)); + ERTS_ML_ASSERT((mdp->origin.flags & ERTS_ML_FLGS_SAME) + == (mdp->target.flags & ERTS_ML_FLGS_SAME)); + ERTS_ML_ASSERT(mdp->origin.type != ERTS_MON_TYPE_SUSPEND); + + if (!(mdp->origin.flags & ERTS_ML_FLG_EXTENDED)) + erts_free(ERTS_ALC_T_MONITOR, mdp); + else { + ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp; + ErlOffHeap oh; + if (mdp->origin.type == ERTS_MON_TYPE_NODE) + ERTS_ML_ASSERT(!mdep->uptr.node_monitors); + else if (mdep->uptr.ohhp) { + ERTS_INIT_OFF_HEAP(&oh); + oh.first = mdep->uptr.ohhp; + erts_cleanup_offheap(&oh); + } + if (mdep->dist) + erts_mon_link_dist_dec_refc(mdep->dist); + erts_free(ERTS_ALC_T_MONITOR_EXT, mdp); + } +} + +void +erts_monitor_set_dead_dist(ErtsMonitor *mon, Eterm nodename) +{ + ErtsMonitorDataExtended *mdep; + mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon); + + ERTS_ML_ASSERT(mon->flags & ERTS_ML_FLG_EXTENDED); + ERTS_ML_ASSERT(mon->type == ERTS_MON_TYPE_DIST_PROC); + ERTS_ML_ASSERT(!mdep->dist); + + mdep->dist = erts_mon_link_dist_create(nodename); + mdep->dist->alive = 0; +} + +Uint +erts_monitor_size(ErtsMonitor *mon) +{ + Uint size, refc; + ErtsMonitorData *mdp = erts_monitor_to_data(mon); + + ERTS_ML_ASSERT(mon->type != ERTS_MON_TYPE_SUSPEND); + + if (!(mon->flags & ERTS_ML_FLG_EXTENDED)) + size = sizeof(ErtsMonitorDataHeap); + else { + ErtsMonitorDataExtended *mdep; + Uint hsz = 0; + + mdep = (ErtsMonitorDataExtended *) mdp; + + if (mon->type != ERTS_MON_TYPE_NODE + && mon->type != ERTS_MON_TYPE_NODES) { + if (!is_immed(mdep->md.ref)) + hsz += NC_HEAP_SIZE(mdep->md.ref); + if (mon->type == ERTS_MON_TYPE_DIST_PROC) { + if (!is_immed(mdep->md.origin.other.item)) + hsz += NC_HEAP_SIZE(mdep->md.origin.other.item); + if (!is_immed(mdep->md.target.other.item)) + hsz += NC_HEAP_SIZE(mdep->md.target.other.item); + } + } + size = sizeof(ErtsMonitorDataExtended) + (hsz - 1)*sizeof(Eterm); + } + + refc = (Uint) erts_atomic32_read_nob(&mdp->refc); + ASSERT(refc > 0); + + return size / refc; +} + + +/* suspend monitors... */ + +ErtsMonitorSuspend * +erts_monitor_suspend_create(Eterm pid) +{ + ErtsMonitorSuspend *msp; + + ERTS_ML_ASSERT(is_internal_pid(pid)); + + msp = erts_alloc(ERTS_ALC_T_SUSPEND_MON, + sizeof(ErtsMonitorSuspend)); + msp->mon.offset = (Uint16) offsetof(ErtsMonitorSuspend, mon); + msp->mon.key_offset = (Uint16) offsetof(ErtsMonitor, other.item); + msp->mon.other.item = pid; + msp->mon.flags = 0; + msp->mon.type = ERTS_MON_TYPE_SUSPEND; + msp->pending = 0; + msp->active = 0; + return msp; +} + +static ErtsMonLnkNode * +create_monitor_suspend(Eterm pid, void *unused) +{ + ErtsMonitorSuspend *msp = erts_monitor_suspend_create(pid); + return (ErtsMonLnkNode *) &msp->mon; +} + +ErtsMonitorSuspend * +erts_monitor_suspend_tree_lookup_create(ErtsMonitor **root, int *created, + Eterm pid) +{ + ErtsMonitor *mon; + mon = (ErtsMonitor *) ml_rbt_lookup_create((ErtsMonLnkNode **) root, + pid, create_monitor_suspend, + NULL, + created); + return erts_monitor_suspend(mon); +} + +void +erts_monitor_suspend_destroy(ErtsMonitorSuspend *msp) +{ + ERTS_ML_ASSERT(!(msp->mon.flags & ERTS_ML_FLG_IN_TABLE)); + erts_free(ERTS_ALC_T_SUSPEND_MON, msp); +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Link Operations * + * * +\* */ + +#ifdef ERTS_ML_DEBUG +size_t erts_link_a_offset; +size_t erts_link_b_offset; +size_t erts_link_key_offset; +#endif + +static ERTS_INLINE void +link_init(void) +{ +#ifdef ERTS_ML_DEBUG + erts_link_a_offset = offsetof(ErtsLinkData, a); + erts_link_b_offset = offsetof(ErtsLinkData, b); + erts_link_key_offset = offsetof(ErtsLink, other.item); +#endif +} + +ErtsLink * +erts_link_tree_lookup(ErtsLink *root, Eterm key) +{ + ASSERT(is_pid(key) || is_internal_port(key)); + return (ErtsLink *) ml_rbt_lookup((ErtsMonLnkNode *) root, key); +} + +ErtsLink * +erts_link_tree_lookup_insert(ErtsLink **root, ErtsLink *lnk) +{ + return (ErtsLink *) ml_rbt_lookup_insert((ErtsMonLnkNode **) root, + (ErtsMonLnkNode *) lnk); +} + +typedef struct { + Uint16 type; + Eterm a; +} ErtsLinkCreateCtxt; + +static ErtsMonLnkNode * +create_link(Eterm b, void *vcctxt) +{ + ErtsLinkCreateCtxt *cctxt = vcctxt; + ErtsLinkData *ldp = erts_link_create(cctxt->type, cctxt->a, b); + ERTS_ML_ASSERT(ml_cmp_keys(ldp->a.other.item, b) == 0); + return (ErtsMonLnkNode *) &ldp->a; +} + +ErtsLink *erts_link_tree_lookup_create(ErtsLink **root, int *created, + Uint16 type, Eterm this, + Eterm other) +{ + ErtsLinkCreateCtxt cctxt; + cctxt.type = type; + cctxt.a = this; + return (ErtsLink *) ml_rbt_lookup_create((ErtsMonLnkNode **) root, + other, create_link, + (void *) &cctxt, + created); +} + +void +erts_link_tree_insert(ErtsLink **root, ErtsLink *lnk) +{ + ml_rbt_insert((ErtsMonLnkNode **) root, (ErtsMonLnkNode *) lnk); +} + +void +erts_link_tree_replace(ErtsLink **root, ErtsLink *old, ErtsLink *new) +{ + ml_rbt_replace((ErtsMonLnkNode **) root, + (ErtsMonLnkNode *) old, + (ErtsMonLnkNode *) new); +} + +void +erts_link_tree_delete(ErtsLink **root, ErtsLink *lnk) +{ + ml_rbt_delete((ErtsMonLnkNode **) root, (ErtsMonLnkNode *) lnk); +} + +void +erts_link_tree_foreach(ErtsLink *root, + void (*func)(ErtsLink *, void *), + void *arg) +{ + ml_rbt_foreach((ErtsMonLnkNode *) root, + (void (*)(ErtsMonLnkNode*, void*)) func, + arg); + +} + +int +erts_link_tree_foreach_yielding(ErtsLink *root, + void (*func)(ErtsLink *, void *), + void *arg, + void **vyspp, + Sint limit) +{ + return ml_rbt_foreach_yielding((ErtsMonLnkNode *) root, + (void (*)(ErtsMonLnkNode*, void*)) func, + arg, vyspp, limit); +} + +void +erts_link_tree_foreach_delete(ErtsLink **root, + void (*func)(ErtsLink *, void *), + void *arg) +{ + ml_rbt_foreach_delete((ErtsMonLnkNode **) root, + (void (*)(ErtsMonLnkNode*, void*)) func, + arg); +} + +int +erts_link_tree_foreach_delete_yielding(ErtsLink **root, + void (*func)(ErtsLink *, void *), + void *arg, + void **vyspp, + Sint limit) +{ + return ml_rbt_foreach_delete_yielding((ErtsMonLnkNode **) root, + (void (*)(ErtsMonLnkNode*, void*)) func, + arg, vyspp, limit); +} + +void +erts_link_list_foreach(ErtsLink *list, + void (*func)(ErtsLink *, void *), + void *arg) +{ + void *ystate = NULL; + while (ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list, + (void (*)(ErtsMonLnkNode *, void *)) func, + arg, &ystate, (Sint) INT_MAX)); +} + +int +erts_link_list_foreach_yielding(ErtsLink *list, + void (*func)(ErtsLink *, void *), + void *arg, + void **vyspp, + Sint limit) +{ + return ml_dl_list_foreach_yielding((ErtsMonLnkNode *) list, + (void (*)(ErtsMonLnkNode *, void *)) func, + arg, vyspp, limit); +} + +void +erts_link_list_foreach_delete(ErtsLink **list, + void (*func)(ErtsLink *, void *), + void *arg) +{ + void *ystate = NULL; + while (ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list, + (void (*)(ErtsMonLnkNode*, void*)) func, + arg, &ystate, (Sint) INT_MAX)); +} + +int +erts_link_list_foreach_delete_yielding(ErtsLink **list, + void (*func)(ErtsLink *, void *), + void *arg, + void **vyspp, + Sint limit) +{ + return ml_dl_list_foreach_delete_yielding((ErtsMonLnkNode **) list, + (void (*)(ErtsMonLnkNode*, void*)) func, + arg, vyspp, limit); +} + +ErtsLinkData * +erts_link_create(Uint16 type, Eterm a, Eterm b) +{ + ErtsLinkData *ldp; + +#ifdef ERTS_ML_DEBUG + switch (type) { + case ERTS_LNK_TYPE_PROC: + ERTS_ML_ASSERT(is_internal_pid(a) && is_internal_pid(a)); + break; + case ERTS_LNK_TYPE_PORT: + ERTS_ML_ASSERT(is_internal_pid(a) || is_internal_pid(b)); + ERTS_ML_ASSERT(is_internal_port(a) || is_internal_port(b)); + break; + case ERTS_LNK_TYPE_DIST_PROC: + ERTS_ML_ASSERT(is_internal_pid(a) || is_internal_pid(b)); + ERTS_ML_ASSERT(is_external_pid(a) || is_external_pid(b)); + break; + default: + ERTS_INTERNAL_ERROR("Invalid link type"); + break; + } +#endif + + if (type != ERTS_LNK_TYPE_DIST_PROC) { + ldp = erts_alloc(ERTS_ALC_T_LINK, sizeof(ErtsLinkData)); + + ldp->a.other.item = b; + ldp->a.flags = (Uint16) 0; + + ldp->b.other.item = a; + ldp->b.flags = (Uint16) 0; + } + else { + ErtsLinkDataExtended *ldep; + Uint size, hsz; + Eterm *hp; + ErlOffHeap oh; + + if (is_internal_pid(a)) + hsz = NC_HEAP_SIZE(b); + else + hsz = NC_HEAP_SIZE(a); + ERTS_ML_ASSERT(hsz > 0); + + size = sizeof(ErtsLinkDataExtended) - sizeof(Eterm); + size += hsz*sizeof(Eterm); + + ldp = erts_alloc(ERTS_ALC_T_LINK_EXT, size); + + ldp->a.flags = ERTS_ML_FLG_EXTENDED; + ldp->b.flags = ERTS_ML_FLG_EXTENDED; + + ldep = (ErtsLinkDataExtended *) ldp; + hp = &ldep->heap[0]; + + ERTS_INIT_OFF_HEAP(&oh); + + if (is_internal_pid(a)) { + ldp->a.other.item = STORE_NC(&hp, &oh, b); + ldp->b.other.item = a; + } + else { + ldp->a.other.item = b; + ldp->b.other.item = STORE_NC(&hp, &oh, a); + } + + ldep->ohhp = oh.first; + ldep->dist = NULL; + } + + erts_atomic32_init_nob(&ldp->refc, 2); + + ldp->a.key_offset = (Uint16) offsetof(ErtsLink, other.item); + ldp->a.offset = (Uint16) offsetof(ErtsLinkData, a); + ldp->a.type = type; + + ldp->b.key_offset = (Uint16) offsetof(ErtsLink, other.item); + ldp->b.offset = (Uint16) offsetof(ErtsLinkData, b); + ldp->b.type = type; + + return ldp; +} + +/* + * erts_link_destroy__() should only be called from + * erts_link_release() or erts_link_release_both(). + */ +void +erts_link_destroy__(ErtsLinkData *ldp) +{ + ERTS_ML_ASSERT(erts_atomic32_read_nob(&ldp->refc) == 0); + ERTS_ML_ASSERT(!(ldp->a.flags & ERTS_ML_FLG_IN_TABLE)); + ERTS_ML_ASSERT(!(ldp->b.flags & ERTS_ML_FLG_IN_TABLE)); + ERTS_ML_ASSERT((ldp->a.flags & ERTS_ML_FLGS_SAME) + == (ldp->b.flags & ERTS_ML_FLGS_SAME)); + + if (!(ldp->a.flags & ERTS_ML_FLG_EXTENDED)) + erts_free(ERTS_ALC_T_LINK, ldp); + else { + ErtsLinkDataExtended *ldep = (ErtsLinkDataExtended *) ldp; + ErlOffHeap oh; + if (ldep->ohhp) { + ERTS_INIT_OFF_HEAP(&oh); + oh.first = ldep->ohhp; + erts_cleanup_offheap(&oh); + } + if (ldep->dist) + erts_mon_link_dist_dec_refc(ldep->dist); + erts_free(ERTS_ALC_T_LINK_EXT, ldep); + } +} + +void +erts_link_set_dead_dist(ErtsLink *lnk, Eterm nodename) +{ + ErtsLinkDataExtended *ldep; + ldep = (ErtsLinkDataExtended *) erts_link_to_data(lnk); + + ERTS_ML_ASSERT(lnk->flags & ERTS_ML_FLG_EXTENDED); + ERTS_ML_ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC); + ERTS_ML_ASSERT(!ldep->dist); + + ldep->dist = erts_mon_link_dist_create(nodename); + ldep->dist->alive = 0; +} + +Uint +erts_link_size(ErtsLink *lnk) +{ + Uint size, refc; + ErtsLinkData *ldp = erts_link_to_data(lnk); + + if (!(lnk->flags & ERTS_ML_FLG_EXTENDED)) + size = sizeof(ErtsLinkData); + else { + ErtsLinkDataExtended *ldep = (ErtsLinkDataExtended *) ldp; + + ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC); + ASSERT(is_external_pid_header(ldep->heap[0])); + + size = sizeof(ErtsLinkDataExtended); + size += thing_arityval(ldep->heap[0])*sizeof(Eterm); + } + + refc = (Uint) erts_atomic32_read_nob(&ldp->refc); + ASSERT(refc > 0); + + return size / refc; +} + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Misc * +\* */ + +void +erts_monitor_link_init(void) +{ + monitor_init(); + link_init(); +} diff --git a/erts/emulator/beam/erl_monitor_link.h b/erts/emulator/beam/erl_monitor_link.h new file mode 100644 index 0000000000..603aead8cc --- /dev/null +++ b/erts/emulator/beam/erl_monitor_link.h @@ -0,0 +1,2326 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +/* + * Description: Monitor and link implementation. + * + * === Monitors ================================================== + * + * The monitor data structure contains: + * - an 'origin' part that should be inserted in a data structure + * of the origin entity, i.e., the entity monitoring another + * entity + * - a 'target' part that should be inserted in a data structure + * of the target entity, i.e., the entity being monitored by + * another entity + * - a shared part that contains information shared between both + * origin and target entities + * + * That is, the two halves of the monitor as well as shared data + * are allocated in one single continuous memory block. The + * origin and target parts can separately each be inserted in + * either a (red-black) tree, a (circular double linked) list, or + * in a process signal queue. + * + * Each process and port contains: + * - a monitor list for local target monitors that is accessed + * via the ERTS_P_LT_MONITORS() macro, and + * - a monitor tree for other monitors that is accessed via the + * ERTS_P_MONITORS() macro + * + * These fields of processes/ports are protected by the main lock + * of the process/port. These are only intended to be accessed by + * the process/port itself. When setting up or tearing down a + * monitor one should *only* operate on the monitor tree/list of + * the currently executing process/port and send signals to the + * other involved process/port so it can modify its own monitor + * tree/list by itself (see erl_proc_sig_queue.h). One should + * absolutely *not* acquire the lock of the other involved + * process/port and operate on its monitor tree/list directly. + * + * Each dist entry contains a monitor/link dist structure that + * contains: + * - a monitor tree for origin named monitors that is accessed via + * the field 'orig_name_monitors', and + * - a monitor list for other monitors that is accessed via the + * 'monitors' field. + * Monitors in these fields contain information about all monitors + * over this specific connection. + * + * The fields of the dist structure are protected by a mutex in + * the same dist structure. Operations on these fields are + * normally performed by the locally involved process only, + * except when a connection is taken down. However in the case + * of distributed named monitors that originates from another + * node this is not possible. That is this operation is also + * performed from another context that the locally involved + * process. + * + * Access to monitor trees are performed using the + * erts_monitor_tree_* functions below. Access to monitor lists + * are performed using the erts_monitor_list_* functions below. + * + * + * The different monitor types: + * + * --- ERTS_MON_TYPE_PROC ---------------------------------------- + * + * A local process (origin) monitors another local process + * (target). + * + * Origin: + * Other Item: Target process identifier + * Target: + * Other Item: Origin process identifier + * Shared: + * Key: Reference + * Name: Name (atom) if by name + * + * Valid keys are only ordinary internal references. + * + * Origin part of the monitor is stored in the monitor tree of + * origin process and target part of the monitor is stored in + * monitor list for local targets on the target process. + * + * --- ERTS_MON_TYPE_PORT ---------------------------------------- + * + * A local process (origin) monitors a local port (target), or a + * local port (origin) monitors a local process (target). + * + * Origin: + * Other Item: Target process/port identifier + * Target: + * Other Item: Origin process/port identifier + * Shared: + * Key: Reference + * Name: Name (atom) if by name + * + * Valid keys are only ordinary internal references. + * + * Origin part of the monitor is stored in the monitor tree of + * origin process/port and target part of the monitor is stored + * in monitor list for local targets on the target process/port. + * + * + * --- ERTS_MON_TYPE_TIME_OFFSET --------------------------------- + * + * A local process (origin) monitors time offset (target) + * + * Origin: + * Other Item: clock_service + * Target: + * Other Item: Origin process identifier + * Shared: + * Key: Reference + * + * Valid keys are only ordinary internal references. + * + * Origin part of the monitor is stored in the monitor tree of + * origin process and target part of the monitor is stored in + * monitor list referred by the variable 'time_offset_monitors' + * (see erl_time_sup.c). + * + * + * --- ERTS_MON_TYPE_DIST_PROC ----------------------------------- + * + * A local process (origin) monitors a remote process (target). + * Origin node on local process and target node on dist entry. + * + * Origin: + * Other Item: Remote process identifier/Node name + * if by name + * Target: + * Other Item: Local process identifier + * Shared: + * Key: Reference + * Name: Name (atom) if by name + * Dist: Pointer to dist structure + * + * Valid keys are only ordinary internal references. + * + * Origin part of the monitor is stored in the monitor tree of + * origin process and target part of the monitor is stored in + * monitor list referred by 'monitors' field of the dist + * structure. + * + * + * A remote process (origin) monitors a local process (target). + * Origin node on dist entry and target node on local process. + * + * Origin: + * Other Item: Local process identifier + * Target: + * Other Item: Remote process identifier + * Shared: + * Key: Reference + * Name: Name (atom) if by name + * + * Valid keys are only external references. + * + * If monitor by name, the origin part of the monitor is stored + * in the monitor tree referred by 'orig_name_monitors' field in + * dist structure; otherwise in the monitor list referred by + * 'monitors' field in dist structure. The target part of the + * monitor is stored in the monitor tree of the local target + * process. + * + * + * --- ERTS_MON_TYPE_RESOURCE ------------------------------------ + * + * A NIF resource (origin) monitors a process (target). + * + * Origin: + * Other Item: Target process identifier + * Target: + * Other Ptr: Pointer to resource + * Shared: + * Key: Reference + * + * Valid keys are only ordinary internal references. + * + * Origin part of the monitor is stored in the monitor tree of + * origin resource (see erl_nif.c) and target part of the + * monitor is stored in monitor list for local targets on the + * target process. + * + * --- ERTS_MON_TYPE_NODE ---------------------------------------- + * + * A local process (origin) monitors a distribution connection + * (target) via erlang:monitor_node(). + * + * Origin: + * Other Item: Node name (atom) + * Key: Node name + * Target: + * Other Item: Origin process identifier + * Key: Origin process identifier + * Shared: + * Refc: Number of invocations + * + * Valid keys are only node-name atoms and internal process + * identifiers. + * + * Origin part of the monitor is stored in the monitor tree of + * origin process and target part of the monitor is stored in + * monitor list referred by 'monitors' field of the dist + * structure. + * + * --- ERTS_MON_TYPE_NODES --------------------------------------- + * + * A local process (origin) monitors all connections (target), + * via net_kernel:monitor_nodes(). + * + * Origin: + * Other Item: Bit mask (small) + * Key: Bit mask + * Target: + * Other Item: Origin process identifier + * Key: Origin process identifier + * Shared: + * Refc: Number of invocations + * + * Valid keys are only small integers and internal process + * identifiers. + * + * Origin part of the monitor is stored in the monitor tree of + * origin process and target part of the monitor is stored in + * monitor list referred by the variable 'nodes_monitors' (see + * dist.c). + * + * --- ERTS_MON_TYPE_SUSPEND ------------------------------------- + * + * Suspend monitor. + * + * Other Item: Suspendee process identifier + * Key: Suspendee process identifier + * + * Valid keys are only ordinary internal references. + * + * This type of monitor is a bit strange and the whole process + * suspend functionality should be improved... + * + * + * + * === Links ===================================================== + * + * The link data structure contains: + * - an 'a' part that should be inserted in a data structure of + * one entity and contains the identifier of the other involved + * entity (b) + * - a 'b' part that should be inserted in a data structure of + * the other involved entity and contains the identifier of the + * other involved entity (a) + * - shared part that contains information shared between both + * involved entities + * + * That is, the two halves of the link as well as shared data + * are allocated in one single continuous memory block. The 'a' + * and the 'b' parts can separately each be inserted in either + * a (red-black) tree, a (circular double linked) list, or in a + * process signal queue. + * + * Each process and port contains: + * - a link tree for links that is accessed via the + * ERTS_P_LINKS() macro + * + * This field of processes/ports is protected by the main lock of + * the process/port. It is only intended to be accessed by the + * process/port itself. When setting up or tearing down a link + * one should *only* operate on the link tree of the currently + * executing process/port and send signals to the other involved + * process/port so it can modify its own monitor tree by itself + * (see erl_proc_sig_queue.h). One should absolutely *not* + * acquire the lock of the other involved process/port and + * operate on its link tree directly. + * + * Each dist entry contains a monitor/link dist structure that + * contains: + * - a link list for links via the 'links' field. + * Links in this field contain information about all links over + * this specific connection. + * + * The fields of the dist structure are protected by a mutex in + * the same dist structure. Operation on the 'links' fields are + * normally performed by the locally involved process only, + * except when a connection is taken down. + * + * Access to link trees are performed using the erts_link_tree_* + * functions below. Access to link lists are performed using the + * erts_link_list_* functions below. + * + * There can only be one link between the same pair of + * processes/ports. Since a link can be simultaneously initiated + * from both ends we always save the link data structure with the + * lowest address if multiple links should appear between the + * same pair of processes/ports. + * + * + * The different link types: + * + * --- ERTS_LNK_TYPE_PROC ----------------------------------------- + * + * A link between a local process A and a local process B. + * + * A: + * Other Item: B process identifier + * Key: B process identifier + * B: + * Other Item: A process identifier + * Key: A process identifier + * + * Valid keys are only internal process identifiers. + * + * 'A' part of the link stored in the link tree of process A and + * 'B' part of the link is stored in link tree of process B. + * + * --- ERTS_LNK_TYPE_PORT ----------------------------------------- + * + * A link between a local process/port A and a local process/port + * B. + * + * A: + * Other Item: B process/port identifier + * Key: B process/port identifier + * B: + * Other Item: A process/port identifier + * Key: A process/port identifier + * + * Valid keys are internal process identifiers and internal port + * identifiers. + * + * 'A' part of the link stored in the link tree of process/port + * A and 'B' part of the link is stored in link tree of + * process/port B. + * + * --- ERTS_LNK_TYPE_DIST_PROC ------------------------------------ + * + * A link between a local process and a remote process. Either of + * the processes can be used as A or B. + * + * A: + * Other Item: B process identifier + * Key: B process identifier + * B: + * Other Item: A process identifier + * Key: A process identifier + * Shared: + * Dist: Pointer to dist structure + * + * Valid keys are internal and external process identifiers. + * + * The part of the link with a remote pid as "other item" is + * stored in the link tree of the local process. The part of + * the link with a local pid as "other item" is stored in the + * links list of the dist structure. + * + * =============================================================== + * + * Author: Rickard Green + * + */ + +#ifndef ERL_MONITOR_LINK_H__ +#define ERL_MONITOR_LINK_H__ + +#define ERTS_PROC_SIG_QUEUE_TYPE_ONLY +#include "erl_proc_sig_queue.h" +#undef ERTS_PROC_SIG_QUEUE_TYPE_ONLY + +#if defined(DEBUG) || 0 +# define ERTS_ML_DEBUG +#else +# undef ERTS_ML_DEBUG +#endif + +#ifdef ERTS_ML_DEBUG +# define ERTS_ML_ASSERT ERTS_ASSERT +#else +# define ERTS_ML_ASSERT(E) ((void) 1) +#endif + +#define ERTS_MON_TYPE_MAX ((Uint16) 7) + +#define ERTS_MON_TYPE_PROC ((Uint16) 0) +#define ERTS_MON_TYPE_PORT ((Uint16) 1) +#define ERTS_MON_TYPE_TIME_OFFSET ((Uint16) 2) +#define ERTS_MON_TYPE_DIST_PROC ((Uint16) 3) +#define ERTS_MON_TYPE_RESOURCE ((Uint16) 4) +#define ERTS_MON_TYPE_NODE ((Uint16) 5) +#define ERTS_MON_TYPE_NODES ((Uint16) 6) +#define ERTS_MON_TYPE_SUSPEND ERTS_MON_TYPE_MAX + +#define ERTS_MON_LNK_TYPE_MAX (ERTS_MON_TYPE_MAX + ((Uint16) 3)) +#define ERTS_LNK_TYPE_MAX ERTS_MON_LNK_TYPE_MAX + +#define ERTS_LNK_TYPE_PROC (ERTS_MON_TYPE_MAX + ((Uint16) 1)) +#define ERTS_LNK_TYPE_PORT (ERTS_MON_TYPE_MAX + ((Uint16) 2)) +#define ERTS_LNK_TYPE_DIST_PROC ERTS_LNK_TYPE_MAX + +#define ERTS_ML_FLG_TARGET (((Uint16) 1) << 0) +#define ERTS_ML_FLG_IN_TABLE (((Uint16) 1) << 1) +#define ERTS_ML_FLG_IN_SUBTABLE (((Uint16) 1) << 2) +#define ERTS_ML_FLG_NAME (((Uint16) 1) << 3) +#define ERTS_ML_FLG_EXTENDED (((Uint16) 1) << 4) + +#define ERTS_ML_FLG_DBG_VISITED (((Uint16) 1) << 15) + +/* Flags that should be the same on both monitor/link halves */ +#define ERTS_ML_FLGS_SAME \ + (ERTS_ML_FLG_EXTENDED|ERTS_ML_FLG_NAME) + +typedef struct ErtsMonLnkNode__ ErtsMonLnkNode; + +typedef struct { + UWord parent; /* Parent ptr and flags... */ + ErtsMonLnkNode *right; + ErtsMonLnkNode *left; +} ErtsMonLnkTreeNode; + +typedef struct { + ErtsMonLnkNode *next; + ErtsMonLnkNode *prev; +} ErtsMonLnkListNode; + +struct ErtsMonLnkNode__ { + union { + ErtsSignalCommon signal; + ErtsMonLnkTreeNode tree; + ErtsMonLnkListNode list; + } node; + union { + Eterm item; + void *ptr; + } other; + Uint16 offset; /* offset from monitor/link data to this structure (node) */ + Uint16 key_offset; /* offset from this structure (node) to key */ + Uint16 flags; + Uint16 type; +}; + +typedef struct { + Eterm nodename; + Uint32 connection_id; + erts_atomic_t refc; + erts_mtx_t mtx; + int alive; + ErtsMonLnkNode *links; /* Link double linked circular list */ + ErtsMonLnkNode *monitors; /* Monitor double linked circular list */ + ErtsMonLnkNode *orig_name_monitors; /* Origin named monitors + read-black tree */ +} ErtsMonLnkDist; + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Misc * +\* */ + +/** + * + * @brief Initialize monitor/link implementation + * + */ +void erts_monitor_link_init(void); + +/** + * + * @brief Create monitor/link dist structure to attach to dist entry + * + * Create dist structure containing monitor and link containers. This + * structure is to be attached to a connected dist entry. + * + * @param[in] nodename Node name as an atom + * + * @returns Pointer to dist structure + * + */ +ErtsMonLnkDist *erts_mon_link_dist_create(Eterm nodename); + +/** + * + * @brief Increase reference count of monitor/link dist structure + * + * @param[in] mld Pointer to dist structure + * + */ +ERTS_GLB_INLINE void erts_mon_link_dist_inc_refc(ErtsMonLnkDist *mld); + +/** + * + * @brief Decrease reference count of monitor/link dist structure + * + * @param[in] mld Pointer to dist structure + * + */ +ERTS_GLB_INLINE void erts_mon_link_dist_dec_refc(ErtsMonLnkDist *mld); + +/* internal functions... */ +ERTS_GLB_INLINE void erts_ml_dl_list_insert__(ErtsMonLnkNode **list, + ErtsMonLnkNode *ml); +ERTS_GLB_INLINE void erts_ml_dl_list_delete__(ErtsMonLnkNode **list, + ErtsMonLnkNode *ml); +ERTS_GLB_INLINE ErtsMonLnkNode *erts_ml_dl_list_first__(ErtsMonLnkNode *list); +ERTS_GLB_INLINE ErtsMonLnkNode *erts_ml_dl_list_last__(ErtsMonLnkNode *list); +void erts_mon_link_dist_destroy__(ErtsMonLnkDist *mld); +ERTS_GLB_INLINE void *erts_ml_node_to_main_struct__(ErtsMonLnkNode *mln); + +/* implementations for globally inlined misc functions... */ +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_INLINE void +erts_mon_link_dist_inc_refc(ErtsMonLnkDist *mld) +{ + ERTS_ML_ASSERT(erts_atomic_read_nob(&mld->refc) > 0); + erts_atomic_inc_nob(&mld->refc); +} + +ERTS_GLB_INLINE void +erts_mon_link_dist_dec_refc(ErtsMonLnkDist *mld) +{ + ERTS_ML_ASSERT(erts_atomic_read_nob(&mld->refc) > 0); + if (erts_atomic_dec_read_nob(&mld->refc) == 0) + erts_mon_link_dist_destroy__(mld); +} + +ERTS_GLB_INLINE void * +erts_ml_node_to_main_struct__(ErtsMonLnkNode *mln) +{ + return (void *) (((char *) mln) - ((size_t) mln->offset)); +} + +ERTS_GLB_INLINE void +erts_ml_dl_list_insert__(ErtsMonLnkNode **list, ErtsMonLnkNode *ml) +{ + ErtsMonLnkNode *first = *list; + ERTS_ML_ASSERT(!(ml->flags & ERTS_ML_FLG_IN_TABLE)); + if (!first) { + ml->node.list.next = ml->node.list.prev = ml; + *list = ml; + } + else { + ERTS_ML_ASSERT(first->node.list.prev->node.list.next == first); + ERTS_ML_ASSERT(first->node.list.next->node.list.prev == first); + ml->node.list.next = first; + ml->node.list.prev = first->node.list.prev; + first->node.list.prev = ml; + ml->node.list.prev->node.list.next = ml; + } + ml->flags |= ERTS_ML_FLG_IN_TABLE; +} + +ERTS_GLB_INLINE void +erts_ml_dl_list_delete__(ErtsMonLnkNode **list, ErtsMonLnkNode *ml) +{ + ERTS_ML_ASSERT(ml->flags & ERTS_ML_FLG_IN_TABLE); + if (ml->node.list.next == ml) { + ERTS_ML_ASSERT(ml->node.list.prev == ml); + ERTS_ML_ASSERT(*list == ml); + + *list = NULL; + } + else { + ERTS_ML_ASSERT(ml->node.list.prev->node.list.next == ml); + ERTS_ML_ASSERT(ml->node.list.prev != ml); + ERTS_ML_ASSERT(ml->node.list.next->node.list.prev == ml); + ERTS_ML_ASSERT(ml->node.list.next != ml); + + if (*list == ml) + *list = ml->node.list.next; + ml->node.list.prev->node.list.next = ml->node.list.next; + ml->node.list.next->node.list.prev = ml->node.list.prev; + } + ml->flags &= ~ERTS_ML_FLG_IN_TABLE; +} + +ERTS_GLB_INLINE ErtsMonLnkNode * +erts_ml_dl_list_first__(ErtsMonLnkNode *list) +{ + return list; +} + +ERTS_GLB_INLINE ErtsMonLnkNode * +erts_ml_dl_list_last__(ErtsMonLnkNode *list) +{ + if (!list) + return NULL; + return list->node.list.prev; +} + +#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Monitor Operations * +\* */ + + +typedef struct ErtsMonLnkNode__ ErtsMonitor; + +typedef struct { + ErtsMonitor origin; + ErtsMonitor target; + Eterm ref; + erts_atomic32_t refc; +} ErtsMonitorData; + +typedef struct { + ErtsMonitorData md; + ErtsORefThing oref_thing; +} ErtsMonitorDataHeap; + +typedef struct ErtsMonitorDataExtended__ ErtsMonitorDataExtended; + +struct ErtsMonitorDataExtended__ { + ErtsMonitorData md; + union { + Eterm name; + Uint refc; + } u; + union { + struct erl_off_heap_header *ohhp; + ErtsMonitor *node_monitors; + } uptr; + ErtsMonLnkDist *dist; + Eterm heap[1]; /* heap start... */ +}; + +typedef struct { + ErtsMonitor mon; + int pending; + int active; +} ErtsMonitorSuspend; + +/* + * --- Monitor tree operations --- + */ + +/** + * + * @brief Lookup a monitor in a monitor tree + * + * + * @param[in] root Pointer to root of monitor tree + * + * @param[in] key Key of monitor to lookup + * + * @returns Pointer to a monitor with the + * key 'key', or NULL if no such + * monitor was found + * + */ +ErtsMonitor *erts_monitor_tree_lookup(ErtsMonitor *root, Eterm key); + +/** + * + * @brief Lookup or insert a monitor in a monitor tree + * + * When the funcion is called it is assumed that: + * - 'mon' monitor is not part of any tree or list + * If the above is not true, bad things will happen. + * + * @param[in,out] root Pointer to pointer to root of monitor tree + * + * @param[in] mon Monitor to insert if no monitor + * with the same key already exists + * + * @returns Pointer to a monitor with the + * key 'key'. If no monitor with the key + * 'key' was found and 'mon' was inserted + * 'mon' is returned. + * + */ +ErtsMonitor *erts_monotor_tree_lookup_insert(ErtsMonitor **root, + ErtsMonitor *mon); + +/** + * + * @brief Lookup or create a node or a nodes monitor in a monitor tree. + * + * Looks up an origin monitor with the key 'target' in the monitor tree. + * If it is not found, creates a monitor and returns a pointer to the + * origin monitor. + * + * When the funcion is called it is assumed that: + * - no target monitors with the key 'target' exists in the tree. + * If the above is not true, bad things will happen. + * + * @param[in,out] root Pointer to pointer to root of monitor tree + * + * @param[out] created Pointer to integer. The integer is set to + * a non-zero value if no monitor with key + * 'target' was found, and a new monitor + * was created. If a monitor was found, it + * is set to zero. + * + * @param[in] type ERTS_MON_TYPE_NODE | ERTS_MON_TYPE_NODES + * + * @param[in] origin The key of the origin + * + * @param[in] target The key of the target + * + */ +ErtsMonitor *erts_monitor_tree_lookup_create(ErtsMonitor **root, int *created, + Uint16 type, Eterm origin, + Eterm target); + +/** + * + * @brief Insert a monitor in a monitor tree + * + * When the funcion is called it is assumed that: + * - no monitors with the same key that 'mon' exist in the tree + * - 'mon' is not part of any list of tree + * If the above are not true, bad things will happen. + * + * @param[in,out] root Pointer to pointer to root of monitor tree + * + * @param[in] mon Monitor to insert. + * + */ +void erts_monitor_tree_insert(ErtsMonitor **root, ErtsMonitor *mon); + +/** + * + * @brief Replace a monitor in a monitor tree + * + * When the funcion is called it is assumed that: + * - 'old' monitor and 'new' monitor have exactly the same key + * - 'old' monitor is part of the tree + * - 'new' monitor is not part of any tree or list + * If the above are not true, bad things will happen. + * + * @param[in,out] root Pointer to pointer to root of monitor tree + * + * @param[in] old Monitor to remove from the tree + * + * @param[in] new Monitor to insert into the tree + * + */ +void erts_monitor_tree_replace(ErtsMonitor **root, ErtsMonitor *old, + ErtsMonitor *new); + +/** + * + * @brief Delete a monitor from a monitor tree + * + * When the funcion is called it is assumed that: + * - 'mon' monitor is part of the tree + * If the above is not true, bad things will happen. + * + * @param[in,out] root Pointer to pointer to root of monitor tree + * + * @param[in] mon Monitor to remove from the tree + * + */ +void erts_monitor_tree_delete(ErtsMonitor **root, ErtsMonitor *mon); + +/** + * + * @brief Call a function for each monitor in a monitor tree + * + * The funcion 'func' will be called with a pointer to a monitor + * as first argument and 'arg' as second argument for each monitor + * in the tree referred to by 'root'. + * + * @param[in] root Pointer to root of monitor tree + * + * @param[in] func Pointer to function to call + * + * @param[in] arg Argument to pass as second argument in + * calls to 'func' + * + */ +void erts_monitor_tree_foreach(ErtsMonitor *root, + void (*func)(ErtsMonitor *, void *), + void *arg); + +/** + * + * @brief Call a function for each monitor in a monitor tree. Yield + * if lots of monitors exist. + * + * The funcion 'func' will be called with a pointer to a monitor + * as first argument and 'arg' as second argument for each monitor + * in the tree referred to by 'root'. + * + * It is assumed that: + * - *yspp equals NULL on first call + * - this function is repetedly called with *yspp set + * as set when previous call returned until a non-zero + * value is returned. + * - no modifications are made on the tree between first call + * and the call that returns a non-zero value + * If the above are not true, bad things will happen. + * + * @param[in] root Pointer to root of monitor tree + * + * @param[in] func Pointer to function to call + * + * @param[in] arg Argument to pass as second argument in + * calls to 'func' + * + * @param[in,out] vyspp Pointer to a pointer to an internal state + * used by this function. At initial call + * *yspp should be NULL. When done *yspp + * will be NULL. + * + * @param[in] limit Maximum amount of monitors to process + * before yielding. + * + * @returns A non-zero value when all monitors has been + * processed, and zero when more work is needed. + * + */ +int erts_monitor_tree_foreach_yielding(ErtsMonitor *root, + void (*func)(ErtsMonitor *, void *), + void *arg, + void **vyspp, + Sint limit); + +/** + * + * @brief Delete all monitors from a monitor tree and call a function for + * each monitor + * + * The funcion 'func' will be called with a pointer to a monitor + * as first argument and 'arg' as second argument for each monitor + * in the tree referred to by 'root'. + * + * @param[in,out] root Pointer to pointer to root of monitor tree + * + * @param[in] func Pointer to function to call + * + * @param[in] arg Argument to pass as second argument in + * calls to 'func' + * + */ +void erts_monitor_tree_foreach_delete(ErtsMonitor **root, + void (*func)(ErtsMonitor *, void *), + void *arg); + +/** + * + * @brief Delete all monitors from a monitor tree and call a function for + * each monitor + * + * The funcion 'func' will be called with a pointer to a monitor + * as first argument and 'arg' as second argument for each monitor + * in the tree referred to by 'root'. + * + * It is assumed that: + * - *yspp equals NULL on first call + * - this function is repetededly called with *yspp set + * as set when previous call returned until a non-zero + * value is returned. + * - no modifications are made on the tree between first call + * and the call that returns a non-zero value + * If the above are not true, bad things will happen. + * + * @param[in,out] root Pointer to pointer to root of monitor tree + * + * @param[in] func Pointer to function to call + * + * @param[in] arg Argument to pass as second argument in + * calls to 'func' + * + * @param[in,out] vyspp Pointer to a pointer to an internal state + * used by this function. At initial call + * *yspp should be NULL. When done *yspp + * will be NULL. + * + * @param[in] limit Maximum amount of monitors to process + * before yielding. + * + * @returns A non-zero value when all monitors has been + * processed, and zero when more work is needed. + * + */ +int erts_monitor_tree_foreach_delete_yielding(ErtsMonitor **root, + void (*func)(ErtsMonitor *, void *), + void *arg, + void **vyspp, + Sint limit); + +/* + * --- Monitor list operations -- + */ + +/** + * + * @brief Insert a monitor in a monitor list + * + * When the funcion is called it is assumed that: + * - 'mon' monitor is not part of any list or tree + * If the above is not true, bad things will happen. + * + * @param[in,out] list Pointer to pointer to monitor list + * + * @param[in] mon Monitor to insert + * + */ +ERTS_GLB_INLINE void erts_monitor_list_insert(ErtsMonitor **list, ErtsMonitor *mon); + +/** + * + * @brief Delete a monitor from a monitor list + * + * When the funcion is called it is assumed that: + * - 'mon' monitor is part of the list + * If the above is not true, bad things will happen. + * + * @param[in,out] list Pointer to pointer to monitor list + * + * @param[in] mon Monitor to remove from the list + * + */ +ERTS_GLB_INLINE void erts_monitor_list_delete(ErtsMonitor **list, ErtsMonitor *mon); + +/** + * + * @brief Get a pointer to first monitor in a monitor list + * + * The monitor will still remain in the list after the return + * + * @param[in] list Pointer to monitor list + * + * @returns Pointer to first monitor in list if + * list is not empty. If list is empty + * NULL is returned. + * + */ +ERTS_GLB_INLINE ErtsMonitor *erts_monitor_list_first(ErtsMonitor *list); + +/** + * + * @brief Get a pointer to last monitor in a monitor list + * + * The monitor will still remain in the list after the return + * + * @param[in] list Pointer to monitor list + * + * @returns Pointer to last monitor in list if + * list is not empty. If list is empty + * NULL is returned. + * + */ +ERTS_GLB_INLINE ErtsMonitor *erts_monitor_list_last(ErtsMonitor *list); + +/** + * + * @brief Call a function for each monitor in a monitor list + * + * The funcion 'func' will be called with a pointer to a monitor + * as first argument and 'arg' as second argument for each monitor + * in the tree referred to by 'list'. + * + * @param[in] list Pointer to root of monitor list + * + * @param[in] func Pointer to function to call + * + * @param[in] arg Argument to pass as second argument in + * calls to 'func' + * + */ +void erts_monitor_list_foreach(ErtsMonitor *list, + void (*func)(ErtsMonitor *, void *), + void *arg); + +/** + * + * @brief Call a function for each monitor in a monitor list. Yield + * if lots of monitors exist. + * + * The funcion 'func' will be called with a pointer to a monitor + * as first argument and 'arg' as second argument for each monitor + * in the tree referred to by 'root'. + * + * It is assumed that: + * - *yspp equals NULL on first call + * - this function is repetedly called with *yspp set + * as set when previous call returned until a non-zero + * value is returned. + * - no modifications are made on the tree between first call + * and the call that returns a non-zero value + * If the above are not true, bad things will happen. + * + * @param[in] list Pointer to monitor list + * + * @param[in] func Pointer to function to call + * + * @param[in] arg Argument to pass as second argument in + * calls to 'func' + * + * @param[in,out] vyspp Pointer to a pointer to an internal state + * used by this function. At initial call + * *yspp should be NULL. When done *yspp + * will be NULL. + * + * @param[in] limit Maximum amount of monitors to process + * before yielding. + * + * @returns A non-zero value when all monitors has been + * processed, and zero when more work is needed. + * + */ +int erts_monitor_list_foreach_yielding(ErtsMonitor *list, + void (*func)(ErtsMonitor *, void *), + void *arg, + void **vyspp, + Sint limit); + +/** + * + * @brief Delete all monitors from a monitor list and call a function for + * each monitor + * + * The funcion 'func' will be called with a pointer to a monitor + * as first argument and 'arg' as second argument for each monitor + * in the tree referred to by 'root'. + * + * @param[in,out] list Pointer to pointer to monitor list + * + * @param[in] func Pointer to function to call + * + * @param[in] arg Argument to pass as second argument in + * calls to 'func' + * + */ +void erts_monitor_list_foreach_delete(ErtsMonitor **list, + void (*func)(ErtsMonitor *, void *), + void *arg); + +/** + * + * @brief Delete all monitors from a monitor list and call a function for + * each monitor + * + * The funcion 'func' will be called with a pointer to a monitor + * as first argument and 'arg' as second argument for each monitor + * in the tree referred to by 'root'. + * + * It is assumed that: + * - *yspp equals NULL on first call + * - this function is repetededly called with *yspp set + * as set when previous call returned until a non-zero + * value is returned. + * - no modifications are made on the tree between first + * and the call that returns a non-zero value + * If the above are not true, bad things will happen. + * + * @param[in,out] list Pointer to pointer to monitor list + * + * @param[in] func Pointer to function to call + * + * @param[in] arg Argument to pass as second argument in + * calls to 'func' + * + * @param[in,out] vyspp Pointer to a pointer to an internal state + * used by this function. At initial call + * *yspp should be NULL. When done *yspp + * will be NULL. + * + * @param[in] limit Maximum amount of monitors to process + * before yielding. + * + * @returns A non-zero value when all monitors has been + * processed, and zero when more work is needed. + * + */ +int erts_monitor_list_foreach_delete_yielding(ErtsMonitor **list, + void (*func)(ErtsMonitor *, void *), + void *arg, + void **vyspp, + Sint limit); + +/* + * --- Misc monitor operations --- + */ + +/** + * + * @brief Create a monitor + * + * Can create all types of monitors exept for suspend monitors + * + * When the funcion is called it is assumed that: + * - 'ref' is an internal ordinary reference if type is ERTS_MON_TYPE_PROC, + * ERTS_MON_TYPE_PORT, ERTS_MON_TYPE_TIME_OFFSET, or ERTS_MON_TYPE_RESOURCE + * - 'ref' is NIL if type is ERTS_MON_TYPE_NODE or ERTS_MON_TYPE_NODES + * - 'ref' is and ordinary internal reference or an external reference if + * type is ERTS_MON_TYPE_DIST_PROC + * - 'name' is an atom or NIL if type is ERTS_MON_TYPE_PROC, + * ERTS_MON_TYPE_PORT, or ERTS_MON_TYPE_DIST_PROC + * - 'name is NIL if type is ERTS_MON_TYPE_TIME_OFFSET, ERTS_MON_TYPE_RESOURCE, + * ERTS_MON_TYPE_NODE, or ERTS_MON_TYPE_NODES + * If the above is not true, bad things will happen. + * + * @param[in] type ERTS_MON_TYPE_PROC, ERTS_MON_TYPE_PORT, + * ERTS_MON_TYPE_TIME_OFFSET, ERTS_MON_TYPE_DIST_PROC, + * ERTS_MON_TYPE_RESOURCE, ERTS_MON_TYPE_NODE, + * or ERTS_MON_TYPE_NODES + * + * @param[in] ref A reference or NIL depending on type + * + * @param[in] origin The key of the origin + * + * @param[in] target The key of the target + * + */ +ErtsMonitorData *erts_monitor_create(Uint16 type, Eterm ref, Eterm origin, + Eterm target, Eterm name); + +/** + * + * @brief Get pointer to monitor data structure + * + * @param[in] mon Pointer to monitor + * + * @returns Pointer to monitor data structure + * + */ +ERTS_GLB_INLINE ErtsMonitorData *erts_monitor_to_data(ErtsMonitor *mon); + +/** + * + * @brief Check if monitor is a target monitor + * + * @param[in] mon Pointer to monitor to check + * + * @returns A non-zero value if target monitor; + * otherwise zero + * + */ +ERTS_GLB_INLINE int erts_monitor_is_target(ErtsMonitor *mon); + +/** + * + * @brief Check if monitor is an origin monitor + * + * @param[in] mon Pointer to monitor to check + * + * @returns A non-zero value if origin monitor; + * otherwise zero + * + */ +ERTS_GLB_INLINE int erts_monitor_is_origin(ErtsMonitor *mon); + +/** + * + * @brief Check if monitor is in tree or list + * + * @param[in] mon Pointer to monitor to check + * + * @returns A non-zero value if in tree or list; + * otherwise zero + * + */ +ERTS_GLB_INLINE int erts_monitor_is_in_table(ErtsMonitor *mon); + +/** + * + * @brief Release monitor + * + * When both the origin and the target part of the monitor have + * been released the monitor structure will be deallocated. + * + * When the funcion is called it is assumed that: + * - 'mon' monitor is not part of any list or tree + * - 'mon' is not referred to by any other structures + * If the above are not true, bad things will happen. + * + * @param[in] mon Pointer to monitor + * + */ +ERTS_GLB_INLINE void erts_monitor_release(ErtsMonitor *mon); + +/** + * + * @brief Release both target and origin monitor structures simultaneously + * + * Release both the origin and target parts of the monitor + * simultaneously and deallocate the structure. + * + * When the funcion is called it is assumed that: + * - Neither the origin part nor the target part of the monitor + * are not part of any list or tree + * - Neither the origin part nor the target part of the monitor + * are referred to by any other structures + * If the above are not true, bad things will happen. + * + * @param[in] mdp Pointer to monitor data structure + * + */ +ERTS_GLB_INLINE void erts_monitor_release_both(ErtsMonitorData *mdp); + +/** + * + * @brief Insert monitor in dist monitor tree or list + * + * When the funcion is called it is assumed that: + * - 'mon' monitor is not part of any list or tree + * If the above is not true, bad things will happen. + * + * @param[in] mon Pointer to monitor + * + * @param[in] dist Pointer to dist structure + * + * @returns A non-zero value if inserted; + * otherwise, zero. The monitor + * is not inserted if the dist + * structure has been set in a + * dead state. + * + */ +ERTS_GLB_INLINE int erts_monitor_dist_insert(ErtsMonitor *mon, ErtsMonLnkDist *dist); + +/** + * + * @brief Delete monitor from dist monitor tree or list + * + * When the funcion is called it is assumed that: + * - 'mon' monitor earler has been inserted into 'dist' + * If the above is not true, bad things will happen. + * + * @param[in] mon Pointer to monitor + * + * @param[in] dist Pointer to dist structure + * + * @returns A non-zero value if deleted; + * otherwise, zero. The monitor + * is not deleted if the dist + * structure has been set in a + * dead state or if it has already + * been deleted. + * + */ +ERTS_GLB_INLINE int erts_monitor_dist_delete(ErtsMonitor *mon); + +/** + * + * @brief Set dead dist structure on monitor + * + * @param[in] mon Pointer to monitor + * + * @param[in] nodename Name of remote node + * + */ +void +erts_monitor_set_dead_dist(ErtsMonitor *mon, Eterm nodename); + +/** + * + * @brief Get charged size of monitor + * + * If the other side of the monitor has been released, the + * whole size of the monitor data structure is returned; otherwise, + * half of the size is returned. + * + * When the funcion is called it is assumed that: + * - 'mon' has not been released + * If the above is not true, bad things will happen. + * + * @param[in] mon Pointer to monitor + * + * @returns Charged size in bytes + * + */ +Uint erts_monitor_size(ErtsMonitor *mon); + + +/* internal function... */ +void erts_monitor_destroy__(ErtsMonitorData *mdp); + +/* implementations for globally inlined monitor functions... */ +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_INLINE int +erts_monitor_is_target(ErtsMonitor *mon) +{ + return !!(mon->flags & ERTS_ML_FLG_TARGET); +} + +ERTS_GLB_INLINE int +erts_monitor_is_origin(ErtsMonitor *mon) +{ + return !(mon->flags & ERTS_ML_FLG_TARGET); +} + +ERTS_GLB_INLINE int +erts_monitor_is_in_table(ErtsMonitor *mon) +{ + return !!(mon->flags & ERTS_ML_FLG_IN_TABLE); +} + +ERTS_GLB_INLINE void +erts_monitor_list_insert(ErtsMonitor **list, ErtsMonitor *mon) +{ + erts_ml_dl_list_insert__((ErtsMonLnkNode **) list, (ErtsMonLnkNode *) mon); +} + +ERTS_GLB_INLINE void +erts_monitor_list_delete(ErtsMonitor **list, ErtsMonitor *mon) +{ + erts_ml_dl_list_delete__((ErtsMonLnkNode **) list, (ErtsMonLnkNode *) mon); +} + +ERTS_GLB_INLINE ErtsMonitor * +erts_monitor_list_first(ErtsMonitor *list) +{ + return (ErtsMonitor *) erts_ml_dl_list_first__((ErtsMonLnkNode *) list); +} + +ERTS_GLB_INLINE ErtsMonitor * +erts_monitor_list_last(ErtsMonitor *list) +{ + return (ErtsMonitor *) erts_ml_dl_list_last__((ErtsMonLnkNode *) list); +} + +#ifdef ERTS_ML_DEBUG +extern size_t erts_monitor_origin_offset; +extern size_t erts_monitor_origin_key_offset; +extern size_t erts_monitor_target_offset; +extern size_t erts_monitor_target_key_offset; +extern size_t erts_monitor_node_key_offset; +#endif + +ERTS_GLB_INLINE ErtsMonitorData * +erts_monitor_to_data(ErtsMonitor *mon) +{ + ErtsMonitorData *mdp = erts_ml_node_to_main_struct__((ErtsMonLnkNode *) mon); + +#ifdef ERTS_ML_DEBUG + ERTS_ML_ASSERT(!(mdp->origin.flags & ERTS_ML_FLG_TARGET)); + ERTS_ML_ASSERT(erts_monitor_origin_offset == (size_t) mdp->origin.offset); + ERTS_ML_ASSERT(!!(mdp->target.flags & ERTS_ML_FLG_TARGET)); + ERTS_ML_ASSERT(erts_monitor_target_offset == (size_t) mdp->target.offset); + if (mon->type == ERTS_MON_TYPE_NODE || mon->type == ERTS_MON_TYPE_NODES) { + ERTS_ML_ASSERT(erts_monitor_node_key_offset == (size_t) mdp->origin.key_offset); + ERTS_ML_ASSERT(erts_monitor_node_key_offset == (size_t) mdp->target.key_offset); + } + else { + ERTS_ML_ASSERT(erts_monitor_origin_key_offset == (size_t) mdp->origin.key_offset); + ERTS_ML_ASSERT(erts_monitor_target_key_offset == (size_t) mdp->target.key_offset); + } +#endif + + return mdp; +} + +ERTS_GLB_INLINE void +erts_monitor_release(ErtsMonitor *mon) +{ + ErtsMonitorData *mdp = erts_monitor_to_data(mon); + ERTS_ML_ASSERT(!(mon->flags & ERTS_ML_FLG_IN_TABLE)); + ERTS_ML_ASSERT(erts_atomic32_read_nob(&mdp->refc) > 0); + + if (erts_atomic32_dec_read_nob(&mdp->refc) == 0) + erts_monitor_destroy__(mdp); +} + +ERTS_GLB_INLINE void +erts_monitor_release_both(ErtsMonitorData *mdp) +{ + ERTS_ML_ASSERT((mdp->origin.flags & ERTS_ML_FLGS_SAME) + == (mdp->target.flags & ERTS_ML_FLGS_SAME)); + ERTS_ML_ASSERT(!(mdp->origin.flags & ERTS_ML_FLG_IN_TABLE)); + ERTS_ML_ASSERT(!(mdp->target.flags & ERTS_ML_FLG_IN_TABLE)); + ERTS_ML_ASSERT(erts_atomic32_read_nob(&mdp->refc) >= 2); + + if (erts_atomic32_add_read_nob(&mdp->refc, (erts_aint32_t) -2) == 0) + erts_monitor_destroy__(mdp); +} + +ERTS_GLB_INLINE int +erts_monitor_dist_insert(ErtsMonitor *mon, ErtsMonLnkDist *dist) +{ + ErtsMonitorDataExtended *mdep; + int insert; + + ERTS_ML_ASSERT(mon->flags & ERTS_ML_FLG_EXTENDED); + ERTS_ML_ASSERT(mon->type == ERTS_MON_TYPE_DIST_PROC + || mon->type == ERTS_MON_TYPE_NODE); + + mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon); + + ERTS_ML_ASSERT(!mdep->dist); + ERTS_ML_ASSERT(dist); + mdep->dist = dist; + + erts_mon_link_dist_inc_refc(dist); + + erts_mtx_lock(&dist->mtx); + + insert = dist->alive; + if (insert) { + if ((mon->flags & (ERTS_ML_FLG_NAME + | ERTS_ML_FLG_TARGET)) == ERTS_ML_FLG_NAME) + erts_monitor_tree_insert(&dist->orig_name_monitors, mon); + else + erts_monitor_list_insert(&dist->monitors, mon); + } + + erts_mtx_unlock(&dist->mtx); + + return insert; +} + +ERTS_GLB_INLINE int +erts_monitor_dist_delete(ErtsMonitor *mon) +{ + ErtsMonitorDataExtended *mdep; + ErtsMonLnkDist *dist; + Uint16 flags; + int delete; + + ERTS_ML_ASSERT(mon->flags & ERTS_ML_FLG_EXTENDED); + ERTS_ML_ASSERT(mon->type == ERTS_MON_TYPE_DIST_PROC + || mon->type == ERTS_MON_TYPE_NODE); + + mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon); + dist = mdep->dist; + ERTS_ML_ASSERT(dist); + + erts_mtx_lock(&dist->mtx); + + flags = mon->flags; + delete = !!dist->alive & !!(flags & ERTS_ML_FLG_IN_TABLE); + if (delete) { + if ((flags & (ERTS_ML_FLG_NAME + | ERTS_ML_FLG_TARGET)) == ERTS_ML_FLG_NAME) + erts_monitor_tree_delete(&dist->orig_name_monitors, mon); + else + erts_monitor_list_delete(&dist->monitors, mon); + } + + erts_mtx_unlock(&dist->mtx); + + return delete; +} + + +#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ + +/* suspend monitors... */ +ErtsMonitorSuspend *erts_monitor_suspend_create(Eterm pid); +ErtsMonitorSuspend *erts_monitor_suspend_tree_lookup_create(ErtsMonitor **root, + int *created, + Eterm pid); +void erts_monitor_suspend_destroy(ErtsMonitorSuspend *msp); + +ERTS_GLB_INLINE ErtsMonitorSuspend *erts_monitor_suspend(ErtsMonitor *mon); + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_INLINE ErtsMonitorSuspend *erts_monitor_suspend(ErtsMonitor *mon) +{ + ERTS_ML_ASSERT(!mon || mon->type == ERTS_MON_TYPE_SUSPEND); + return (ErtsMonitorSuspend *) mon; +} + +#endif + +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\ + * Link Operations * +\* */ + +typedef struct ErtsMonLnkNode__ ErtsLink; + +typedef struct { + ErtsLink a; + ErtsLink b; + erts_atomic32_t refc; +} ErtsLinkData; + +typedef struct { + ErtsLinkData ld; + struct erl_off_heap_header *ohhp; + ErtsMonLnkDist *dist; + Eterm heap[1]; /* heap start... */ +} ErtsLinkDataExtended; + +/* + * --- Link tree operations --- + */ + +/** + * + * @brief Lookup a link in a link tree + * + * + * @param[in] root Pointer to root of link tree + * + * @param[in] key Key of link to lookup + * + * @returns Pointer to a link with the + * key 'key', or NULL if no such + * link was found + * + */ +ErtsLink *erts_link_tree_lookup(ErtsLink *root, Eterm item); + +/** + * + * @brief Lookup or insert a link in a link tree + * + * When the funcion is called it is assumed that: + * - 'lnk' link is not part of any tree or list + * If the above is not true, bad things will happen. + * + * @param[in,out] root Pointer to pointer to root of link tree + * + * @param[in] lnk Link to insert if no link + * with the same key already exists + * + * @returns Pointer to a link with the + * key 'key'. If no link with the key + * 'key' was found and 'lnk' was inserted + * 'lnk' is returned. + * + */ +ErtsLink *erts_link_tree_lookup_insert(ErtsLink **root, ErtsLink *lnk); + +/** + * + * @brief Lookup or create a link in a link tree. + * + * Looks up a link with the key 'other' in the link tree. If it is not + * found, creates and insert a link with the key 'other'. + * + * @param[in,out] root Pointer to pointer to root of link tree + * + * @param[out] created Pointer to integer. The integer is set to + * a non-zero value if no link with key + * 'other' was found, and a new link + * was created. If a link was found, it + * is set to zero. + * + * @param[in] type Type of link + * + * @param[in] this Id of this entity + * + * @param[in] other Id of other entity + * + */ +ErtsLink *erts_link_tree_lookup_create(ErtsLink **root, int *created, + Uint16 type, Eterm this, Eterm other); + +/** + * + * @brief Insert a link in a link tree + * + * When the funcion is called it is assumed that: + * - no links with the same key that 'lnk' exist in the tree + * - 'lnk' is not part of any list of tree + * If the above are not true, bad things will happen. + * + * @param[in,out] root Pointer to pointer to root of link tree + * + * @param[in] lnk Link to insert. + * + */ +void erts_link_tree_insert(ErtsLink **root, ErtsLink *lnk); + +/** + * + * @brief Replace a link in a link tree + * + * When the funcion is called it is assumed that: + * - 'old' link and 'new' link have exactly the same key + * - 'old' link is part of the tree + * - 'new' link is not part of any tree or list + * If the above are not true, bad things will happen. + * + * @param[in,out] root Pointer to pointer to root of link tree + * + * @param[in] old Link to remove from the tree + * + * @param[in] new Link to insert into the tree + * + */ +void erts_link_tree_replace(ErtsLink **root, ErtsLink *old, ErtsLink *new); + +/** + * + * @brief Replace a link in a link tree if key already exist based on adress + * + * Inserts the link 'lnk' in the tree if no link with the same key + * already exists in tree. If a link with the same key exists in + * the tree and 'lnk' has a lower address than the link in the + * tree, the existing link in the tree is replaced by 'lnk'. + * + * When the funcion is called it is assumed that: + * - 'lnk' link is not part of any tree or list + * If the above are not true, bad things will happen. + * + * @param[in,out] root Pointer to pointer to root of link tree + * + * @param[in] lnk Link to insert into the tree + * + * @returns A pointer to the link that is not part + * of the tree after this operation. + * + */ +ERTS_GLB_INLINE ErtsLink *erts_link_tree_insert_addr_replace(ErtsLink **root, + ErtsLink *lnk); + +/** + * + * @brief Delete a link from a link tree + * + * When the funcion is called it is assumed that: + * - 'lnk' link is part of the tree + * If the above is not true, bad things will happen. + * + * @param[in,out] root Pointer to pointer to root of link tree + * + * @param[in] lnk Link to remove from the tree + * + */ +void erts_link_tree_delete(ErtsLink **root, ErtsLink *lnk); + +/** + * + * @brief Delete a link from a link tree based on key + * + * If link 'lnk' is in the tree, 'lnk' is deleted from the tree. + * If link 'lnk' is not in the tree, another link with the same + * key as 'lnk' is deleted from the tree if such a link exist. + * + * When the funcion is called it is assumed that: + * - if 'lnk' link is part of a tree or list, it is part of this tree + * If the above is not true, bad things will happen. + * + * @param[in,out] root Pointer to pointer to root of link tree + * + * @param[in] lnk Link to remove from the tree + * + * @returns A pointer to the link that was deleted + * from the tree, or NULL in case no link + * was deleted from the tree + * + */ +ERTS_GLB_INLINE ErtsLink *erts_link_tree_key_delete(ErtsLink **root, ErtsLink *lnk); + +/** + * + * @brief Call a function for each link in a link tree + * + * The funcion 'func' will be called with a pointer to a link + * as first argument and 'arg' as second argument for each link + * in the tree referred to by 'root'. + * + * @param[in] root Pointer to root of link tree + * + * @param[in] func Pointer to function to call + * + * @param[in] arg Argument to pass as second argument in + * calls to 'func' + * + */ +void erts_link_tree_foreach(ErtsLink *root, + void (*func)(ErtsLink *, void *), + void *arg); + +/** + * + * @brief Call a function for each link in a link tree. Yield if lots + * of links exist. + * + * The funcion 'func' will be called with a pointer to a link + * as first argument and 'arg' as second argument for each link + * in the tree referred to by 'root'. + * + * It is assumed that: + * - *yspp equals NULL on first call + * - this function is repetedly called with *yspp set + * as set when previous call returned until a non-zero + * value is returned. + * - no modifications are made on the tree between first call + * and the call that returns a non-zero value + * If the above are not true, bad things will happen. + * + * @param[in] root Pointer to root of link tree + * + * @param[in] func Pointer to function to call + * + * @param[in] arg Argument to pass as second argument in + * calls to 'func' + * + * @param[in,out] vyspp Pointer to a pointer to an internal state + * used by this function. At initial call + * *yspp should be NULL. When done *yspp + * will be NULL. + * + * @param[in] limit Maximum amount of links to process + * before yielding. + * + * @returns A non-zero value when all links has been + * processed, and zero when more work is needed. + * + */ +int erts_link_tree_foreach_yielding(ErtsLink *root, + void (*func)(ErtsLink *, void *), + void *arg, + void **vyspp, + Sint limit); + +/** + * + * @brief Delete all links from a link tree and call a function for + * each link + * + * The funcion 'func' will be called with a pointer to a link + * as first argument and 'arg' as second argument for each link + * in the tree referred to by 'root'. + * + * @param[in,out] root Pointer to pointer to root of link tree + * + * @param[in] func Pointer to function to call + * + * @param[in] arg Argument to pass as second argument in + * calls to 'func' + * + */ +void erts_link_tree_foreach_delete(ErtsLink **root, + void (*func)(ErtsLink *, void *), + void *arg); + +/** + * + * @brief Delete all links from a link tree and call a function for + * each link + * + * The funcion 'func' will be called with a pointer to a link + * as first argument and 'arg' as second argument for each link + * in the tree referred to by 'root'. + * + * It is assumed that: + * - *yspp equals NULL on first call + * - this function is repetededly called with *yspp set + * as set when previous call returned until a non-zero + * value is returned. + * - no modifications are made on the tree between first call + * and the call that returns a non-zero value + * If the above are not true, bad things will happen. + * + * @param[in,out] root Pointer to pointer to root of link tree + * + * @param[in] func Pointer to function to call + * + * @param[in] arg Argument to pass as second argument in + * calls to 'func' + * + * @param[in,out] vyspp Pointer to a pointer to an internal state + * used by this function. At initial call + * *yspp should be NULL. When done *yspp + * will be NULL. + * + * @param[in] limit Maximum amount of links to process + * before yielding. + * + * @returns A non-zero value when all links has been + * processed, and zero when more work is needed. + * + */ +int erts_link_tree_foreach_delete_yielding(ErtsLink **root, + void (*func)(ErtsLink *, void *), + void *arg, + void **vyspp, + Sint limit); + +/* + * --- Link list operations --- + */ + +/** + * + * @brief Insert a link in a link list + * + * When the funcion is called it is assumed that: + * - 'lnk' link is not part of any list or tree + * If the above is not true, bad things will happen. + * + * @param[in,out] list Pointer to pointer to link list + * + * @param[in] lnk Link to insert + * + */ +ERTS_GLB_INLINE void erts_link_list_insert(ErtsLink **list, ErtsLink *lnk); + +/** + * + * @brief Delete a link from a link list + * + * When the funcion is called it is assumed that: + * - 'lnk' link is part of the list + * If the above is not true, bad things will happen. + * + * @param[in,out] list Pointer to pointer to link list + * + * @param[in] lnk Link to remove from the list + * + */ +ERTS_GLB_INLINE void erts_link_list_delete(ErtsLink **list, ErtsLink *lnk); + +/** + * + * @brief Get a pointer to first link in a link list + * + * The link will still remain in the list after the return + * + * @param[in] list Pointer to link list + * + * @returns Pointer to first link in list if + * list is not empty. If list is empty + * NULL is returned. + * + */ +ERTS_GLB_INLINE ErtsLink *erts_link_list_first(ErtsLink *list); + +/** + * + * @brief Get a pointer to last link in a link list + * + * The link will still remain in the list after the return + * + * @param[in] list Pointer to link list + * + * @returns Pointer to last link in list if + * list is not empty. If list is empty + * NULL is returned. + * + */ +ERTS_GLB_INLINE ErtsLink *erts_link_list_last(ErtsLink *list); + +/** + * + * @brief Call a function for each link in a link list + * + * The funcion 'func' will be called with a pointer to a link + * as first argument and 'arg' as second argument for each link + * in the tree referred to by 'list'. + * + * @param[in] list Pointer to root of link list + * + * @param[in] func Pointer to function to call + * + * @param[in] arg Argument to pass as second argument in + * calls to 'func' + * + */ +void erts_link_list_foreach(ErtsLink *list, + void (*func)(ErtsLink *, void *), + void *arg); + +/** + * + * @brief Call a function for each link in a link list. Yield + * if lots of links exist. + * + * The funcion 'func' will be called with a pointer to a link + * as first argument and 'arg' as second argument for each link + * in the tree referred to by 'root'. + * + * It is assumed that: + * - *yspp equals NULL on first call + * - this function is repetedly called with *yspp set + * as set when previous call returned until a non-zero + * value is returned. + * - no modifications are made on the tree between first call + * and the call that returns a non-zero value + * If the above are not true, bad things will happen. + * + * @param[in] list Pointer to link list + * + * @param[in] func Pointer to function to call + * + * @param[in] arg Argument to pass as second argument in + * calls to 'func' + * + * @param[in,out] vyspp Pointer to a pointer to an internal state + * used by this function. At initial call + * *yspp should be NULL. When done *yspp + * will be NULL. + * + * @param[in] limit Maximum amount of links to process + * before yielding. + * + * @returns A non-zero value when all links has been + * processed, and zero when more work is needed. + * + */ +int erts_link_list_foreach_yielding(ErtsLink *list, + void (*func)(ErtsLink *, void *), + void *arg, + void **vyspp, + Sint limit); + +/** + * + * @brief Delete all links from a link list and call a function for + * each link + * + * The funcion 'func' will be called with a pointer to a link + * as first argument and 'arg' as second argument for each link + * in the tree referred to by 'root'. + * + * @param[in,out] list Pointer to pointer to link list + * + * @param[in] func Pointer to function to call + * + * @param[in] arg Argument to pass as second argument in + * calls to 'func' + * + */ +void erts_link_list_foreach_delete(ErtsLink **list, + void (*func)(ErtsLink *, void *), + void *arg); + +/** + * + * @brief Delete all links from a link list and call a function for + * each link + * + * The funcion 'func' will be called with a pointer to a link + * as first argument and 'arg' as second argument for each link + * in the tree referred to by 'root'. + * + * It is assumed that: + * - *yspp equals NULL on first call + * - this function is repetededly called with *yspp set + * as set when previous call returned until a non-zero + * value is returned. + * - no modifications are made on the tree between first + * and the call that returns a non-zero value + * If the above are not true, bad things will happen. + * + * @param[in,out] list Pointer to pointer to link list + * + * @param[in] func Pointer to function to call + * + * @param[in] arg Argument to pass as second argument in + * calls to 'func' + * + * @param[in,out] vyspp Pointer to a pointer to an internal state + * used by this function. At initial call + * *yspp should be NULL. When done *yspp + * will be NULL. + * + * @param[in] limit Maximum amount of links to process + * before yielding. + * + * @returns A non-zero value when all links has been + * processed, and zero when more work is needed. + * + */ +int erts_link_list_foreach_delete_yielding(ErtsLink **list, + void (*func)(ErtsLink *, void *), + void *arg, + void **vyspp, + Sint limit); + +/* + * --- Misc link operations --- + */ + +/** + * + * @brief Create a link + * + * Can create all types of links + * + * When the funcion is called it is assumed that: + * - 'ref' is an internal ordinary reference if type is ERTS_MON_TYPE_PROC, + * ERTS_MON_TYPE_PORT, ERTS_MON_TYPE_TIME_OFFSET, or ERTS_MON_TYPE_RESOURCE + * - 'ref' is NIL if type is ERTS_MON_TYPE_NODE or ERTS_MON_TYPE_NODES + * - 'ref' is and ordinary internal reference or an external reference if + * type is ERTS_MON_TYPE_DIST_PROC + * - 'name' is an atom or NIL if type is ERTS_MON_TYPE_PROC, + * ERTS_MON_TYPE_PORT, or ERTS_MON_TYPE_DIST_PROC + * - 'name is NIL if type is ERTS_MON_TYPE_TIME_OFFSET, ERTS_MON_TYPE_RESOURCE, + * ERTS_MON_TYPE_NODE, or ERTS_MON_TYPE_NODES + * If the above is not true, bad things will happen. + * + * @param[in] type ERTS_MON_TYPE_PROC, ERTS_MON_TYPE_PORT, + * ERTS_MON_TYPE_TIME_OFFSET, ERTS_MON_TYPE_DIST_PROC, + * ERTS_MON_TYPE_RESOURCE, ERTS_MON_TYPE_NODE, + * or ERTS_MON_TYPE_NODES + * + * @param[in] a The key of entity a. Link structure a will + * have field other.item set to 'b'. + * + * @param[in] b The key of entity b. Link structure b will + * have field other.item set to 'a'. + * + */ +ErtsLinkData *erts_link_create(Uint16 type, Eterm a, Eterm b); + +/** + * + * @brief Get pointer to link data structure + * + * @param[in] lnk Pointer to link + * + * @returns Pointer to link data structure + * + */ +ERTS_GLB_INLINE ErtsLinkData *erts_link_to_data(ErtsLink *lnk); + +/** + * + * @brief Get pointer to the other link structure part of the link + * + * @param[in] lnk Pointer to link + * + * @param[out] ldpp Pointer to pointer to link data structure, + * if a non-NULL value is passed in the call + * + * @returns Pointer to other link + * + */ +ERTS_GLB_INLINE ErtsLink *erts_link_to_other(ErtsLink *lnk, ErtsLinkData **ldpp); + +/** + * + * @brief Check if link is in tree or list + * + * @param[in] lnk Pointer to lnk to check + * + * @returns A non-zero value if in tree or list; + * otherwise zero + * + */ +ERTS_GLB_INLINE int erts_link_is_in_table(ErtsLink *lnk); + +/** + * + * @brief Release link + * + * When both link halves part of the link have been released the link + * structure will be deallocated. + * + * When the funcion is called it is assumed that: + * - 'lnk' link is not part of any list or tree + * - 'lnk' is not referred to by any other structures + * If the above are not true, bad things will happen. + * + * @param[in] lnk Pointer to link + * + */ +ERTS_GLB_INLINE void erts_link_release(ErtsLink *lnk); + +/** + * + * @brief Release both link halves of a link simultaneously + * + * Release both halves of a link simultaneously and deallocate + * the structure. + * + * When the funcion is called it is assumed that: + * - Neither of the parts of the link are part of any list or tree + * - Neither of the parts of the link or the link data structure + * are referred to by any other structures + * If the above are not true, bad things will happen. + * + * @param[in] mdp Pointer to link data structure + * + */ +ERTS_GLB_INLINE void erts_link_release_both(ErtsLinkData *ldp); + +/** + * + * @brief Insert link in dist link list + * + * When the funcion is called it is assumed that: + * - 'lnk' link is not part of any list or tree + * If the above is not true, bad things will happen. + * + * @param[in] lnk Pointer to link + * + * @param[in] dist Pointer to dist structure + * + * @returns A non-zero value if inserted; + * otherwise, zero. The link + * is not inserted if the dist + * structure has been set in a + * dead state. + * + */ +ERTS_GLB_INLINE int erts_link_dist_insert(ErtsLink *lnk, ErtsMonLnkDist *dist); + +/** + * + * @brief Delete link from dist link list + * + * When the funcion is called it is assumed that: + * - 'lnk' link earler has been inserted into 'dist' + * If the above is not true, bad things will happen. + * + * @param[in] lnk Pointer to link + * + * @param[in] dist Pointer to dist structure + * + * @returns A non-zero value if deleted; + * otherwise, zero. The link + * is not deleted if the dist + * structure has been set in a + * dead state or if it has already + * been deleted. + * + */ +ERTS_GLB_INLINE int erts_link_dist_delete(ErtsLink *lnk); + +/** + * + * @brief Set dead dist structure on link + * + * @param[in] lnk Pointer to link + * + * @param[in] nodename Name of remote node + * + */ +void +erts_link_set_dead_dist(ErtsLink *lnk, Eterm nodename); + +/** + * + * @brief Get charged size of link + * + * If the other side of the link has been released, the + * whole size of the link data structure is returned; otherwise, + * half of the size is returned. + * + * When the funcion is called it is assumed that: + * - 'lnk' has not been released + * If the above is not true, bad things will happen. + * + * @param[in] lnk Pointer to link + * + * @returns Charged size in bytes + * + */ +Uint erts_link_size(ErtsLink *lnk); + +/* internal function... */ +void erts_link_destroy__(ErtsLinkData *ldp); + +/* implementations for globally inlined link functions... */ +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +#ifdef ERTS_ML_DEBUG +extern size_t erts_link_a_offset; +extern size_t erts_link_b_offset; +extern size_t erts_link_key_offset; +#endif + +ERTS_GLB_INLINE ErtsLinkData * +erts_link_to_data(ErtsLink *lnk) +{ + ErtsLinkData *ldp = erts_ml_node_to_main_struct__((ErtsMonLnkNode *) lnk); + +#ifdef ERTS_ML_DEBUG + ERTS_ML_ASSERT(erts_link_a_offset == (size_t) ldp->a.offset); + ERTS_ML_ASSERT(erts_link_key_offset == (size_t) ldp->a.key_offset); + ERTS_ML_ASSERT(erts_link_b_offset == (size_t) ldp->b.offset); + ERTS_ML_ASSERT(erts_link_key_offset == (size_t) ldp->b.key_offset); +#endif + + return ldp; +} + +ERTS_GLB_INLINE ErtsLink * +erts_link_to_other(ErtsLink *lnk, ErtsLinkData **ldpp) +{ + ErtsLinkData *ldp = erts_link_to_data(lnk); + if (ldpp) + *ldpp = ldp; + return lnk == &ldp->a ? &ldp->b : &ldp->a; +} + +ERTS_GLB_INLINE int +erts_link_is_in_table(ErtsLink *lnk) +{ + return !!(lnk->flags & ERTS_ML_FLG_IN_TABLE); +} + +ERTS_GLB_INLINE void +erts_link_list_insert(ErtsLink **list, ErtsLink *lnk) +{ + erts_ml_dl_list_insert__((ErtsMonLnkNode **) list, (ErtsMonLnkNode *) lnk); +} + +ERTS_GLB_INLINE void +erts_link_list_delete(ErtsLink **list, ErtsLink *lnk) +{ + erts_ml_dl_list_delete__((ErtsMonLnkNode **) list, (ErtsMonLnkNode *) lnk); +} + +ERTS_GLB_INLINE ErtsLink * +erts_link_list_first(ErtsLink *list) +{ + return (ErtsLink *) erts_ml_dl_list_first__((ErtsMonLnkNode *) list); +} + +ERTS_GLB_INLINE ErtsLink * +erts_link_list_last(ErtsLink *list) +{ + return (ErtsLink *) erts_ml_dl_list_last__((ErtsMonLnkNode *) list); +} + +ERTS_GLB_INLINE void +erts_link_release(ErtsLink *lnk) +{ + ErtsLinkData *ldp = erts_link_to_data(lnk); + ERTS_ML_ASSERT(!(lnk->flags & ERTS_ML_FLG_IN_TABLE)); + ERTS_ML_ASSERT(erts_atomic32_read_nob(&ldp->refc) > 0); + if (erts_atomic32_dec_read_nob(&ldp->refc) == 0) + erts_link_destroy__(ldp); +} + +ERTS_GLB_INLINE void +erts_link_release_both(ErtsLinkData *ldp) +{ + ERTS_ML_ASSERT(!(ldp->a.flags & ERTS_ML_FLG_IN_TABLE)); + ERTS_ML_ASSERT(!(ldp->b.flags & ERTS_ML_FLG_IN_TABLE)); + ERTS_ML_ASSERT(erts_atomic32_read_nob(&ldp->refc) >= 2); + if (erts_atomic32_add_read_nob(&ldp->refc, (erts_aint32_t) -2) == 0) + erts_link_destroy__(ldp); +} + +ERTS_GLB_INLINE ErtsLink * +erts_link_tree_insert_addr_replace(ErtsLink **root, ErtsLink *lnk) +{ + ErtsLink *lnk2 = erts_link_tree_lookup_insert(root, lnk); + if (!lnk2) + return NULL; + if (lnk2 < lnk) + return lnk; + erts_link_tree_replace(root, lnk2, lnk); + return lnk2; +} + +ERTS_GLB_INLINE ErtsLink * +erts_link_tree_key_delete(ErtsLink **root, ErtsLink *lnk) +{ + ErtsLink *dlnk; + if (erts_link_is_in_table(lnk)) + dlnk = lnk; + else + dlnk = erts_link_tree_lookup(*root, lnk->other.item); + if (dlnk) + erts_link_tree_delete(root, dlnk); + return dlnk; +} + +ERTS_GLB_INLINE int +erts_link_dist_insert(ErtsLink *lnk, ErtsMonLnkDist *dist) +{ + ErtsLinkDataExtended *ldep; + int insert; + + ERTS_ML_ASSERT(lnk->flags & ERTS_ML_FLG_EXTENDED); + ERTS_ML_ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC); + + ldep = (ErtsLinkDataExtended *) erts_link_to_data(lnk); + + ERTS_ML_ASSERT(!ldep->dist); + ERTS_ML_ASSERT(dist); + ldep->dist = dist; + + erts_mon_link_dist_inc_refc(dist); + + erts_mtx_lock(&dist->mtx); + + insert = dist->alive; + if (insert) + erts_link_list_insert(&dist->links, lnk); + + erts_mtx_unlock(&dist->mtx); + + return insert; +} + +ERTS_GLB_INLINE int +erts_link_dist_delete(ErtsLink *lnk) +{ + ErtsLinkDataExtended *ldep; + ErtsMonLnkDist *dist; + int delete; + + ERTS_ML_ASSERT(lnk->flags & ERTS_ML_FLG_EXTENDED); + ERTS_ML_ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC); + + ldep = (ErtsLinkDataExtended *) erts_link_to_data(lnk); + dist = ldep->dist; + if (!dist) + return -1; + + erts_mtx_lock(&dist->mtx); + + delete = !!dist->alive & !!(lnk->flags & ERTS_ML_FLG_IN_TABLE); + if (delete) + erts_link_list_delete(&dist->links, lnk); + + erts_mtx_unlock(&dist->mtx); + + return delete; +} + + +#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */ + +#endif /* ERL_MONITOR_LINK_H__ */ diff --git a/erts/emulator/beam/erl_monitors.c b/erts/emulator/beam/erl_monitors.c deleted file mode 100644 index 1c840d89f6..0000000000 --- a/erts/emulator/beam/erl_monitors.c +++ /dev/null @@ -1,1073 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2004-2017. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -/************************************************************************** - * Monitors and links data structure manipulation. - * Monitors and links are organized as AVL trees with the reference as - * key in the monitor case and the pid of the linked process as key in the - * link case. Lookups the order of the references is somewhat special. Local - * references are strictly smaller than remote references and are sorted - * by inlined comparison functionality. Remote references are handled by the - * usual cmp function. - * Each Monitor is tagged with different tags depending on which end of the - * monitor it is. - * A monitor is removed either explicitly by reference or all monitors are - * removed when the process exits. No need to access the monitor by pid. - **************************************************************************/ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include "sys.h" -#include "erl_vm.h" -#include "global.h" -#include "erl_process.h" -#include "error.h" -#include "erl_db.h" -#include "bif.h" -#include "big.h" -#include "erl_monitors.h" -#include "erl_bif_unique.h" - -#define STACK_NEED 50 -#define MAX_MONITORS 0xFFFFFFFFUL - -#define DIR_LEFT 0 -#define DIR_RIGHT 1 -#define DIR_END 2 - -static erts_atomic_t tot_link_lh_size; - -/* Implements the sort order in monitor trees, which is different from - the ordinary term order. - No short local ref's should ever exist (the ref is created by the bif's - in runtime), therefore: - All local ref's are less than external ref's - Local ref's are inline-compared, - External ref's are compared by cmp */ - -#if 0 -#define CMP_MON_REF(Ref1,Ref2) \ -cmp((Ref1),(Ref2)) /* XXX, the inline comparision yet to be done */ -#else -#define CMP_MON_REF(Ref1,Ref2) cmp_mon_ref((Ref1),(Ref2)) -#endif - -static ERTS_INLINE int cmp_mon_ref(Eterm ref1, Eterm ref2) -{ - Eterm *b1, *b2; - - - b1 = boxed_val(ref1); - b2 = boxed_val(ref2); - if (is_ref_thing_header(*b1)) { - if (is_ref_thing_header(*b2)) { - Uint32 *num1, *num2; - if (is_ordinary_ref_thing(b1)) { - ErtsORefThing *rtp = (ErtsORefThing *) b1; - num1 = rtp->num; - } - else { - ErtsMRefThing *mrtp = (ErtsMRefThing *) b1; - num1 = mrtp->mb->refn; - } - if (is_ordinary_ref_thing(b2)) { - ErtsORefThing *rtp = (ErtsORefThing *) b2; - num2 = rtp->num; - } - else { - ErtsMRefThing *mrtp = (ErtsMRefThing *) b2; - num2 = mrtp->mb->refn; - } - return erts_internal_ref_number_cmp(num1, num2); - } - return -1; - } - if (is_ref_thing_header(*b2)) { - return 1; - } - return CMP(ref1,ref2); -} - -#define CP_LINK_VAL(To, Hp, From) \ -do { \ - if (is_immed(From)) \ - (To) = (From); \ - else { \ - Uint i__; \ - Uint len__; \ - ASSERT((Hp)); \ - ASSERT(is_internal_ordinary_ref((From)) \ - || is_external((From))); \ - (To) = make_boxed((Hp)); \ - len__ = thing_arityval(*boxed_val((From))) + 1; \ - for(i__ = 0; i__ < len__; i__++) \ - (*((Hp)++)) = boxed_val((From))[i__]; \ - if (is_external((To))) { \ - external_thing_ptr((To))->next = NULL; \ - erts_refc_inc(&(external_thing_ptr((To))->node->refc), 2);\ - } \ - } \ -} while (0) - -static ErtsMonitor *create_monitor(Uint type, Eterm ref, UWord entity, Eterm name) -{ - Uint mon_size = ERTS_MONITOR_SIZE; - ErtsMonitor *n; - Eterm *hp; - - mon_size += NC_HEAP_SIZE(ref); - if (type != MON_NIF_TARGET && is_not_immed(entity)) { - mon_size += NC_HEAP_SIZE(entity); - } - - if (mon_size <= ERTS_MONITOR_SH_SIZE) { - n = (ErtsMonitor *) erts_alloc(ERTS_ALC_T_MONITOR_SH, - mon_size*sizeof(Uint)); - } else { - n = (ErtsMonitor *) erts_alloc(ERTS_ALC_T_MONITOR_LH, - mon_size*sizeof(Uint)); - erts_atomic_add_nob(&tot_link_lh_size, mon_size*sizeof(Uint)); - } - hp = n->heap; - - - n->left = n->right = NULL; /* Always the same initial value*/ - n->type = (Uint16) type; - n->balance = 0; /* Always the same initial value */ - n->name = name; /* atom() or [] */ - CP_LINK_VAL(n->ref, hp, ref); /*XXX Unnecessary check, never immediate*/ - if (type == MON_NIF_TARGET) - n->u.resource = (ErtsResource*)entity; - else - CP_LINK_VAL(n->u.pid, hp, (Eterm)entity); - - return n; -} - -static ErtsLink *create_link(Uint type, Eterm pid) -{ - Uint lnk_size = ERTS_LINK_SIZE; - ErtsLink *n; - Eterm *hp; - - if (is_not_immed(pid)) { - lnk_size += NC_HEAP_SIZE(pid); - } - - if (lnk_size <= ERTS_LINK_SH_SIZE) { - n = (ErtsLink *) erts_alloc(ERTS_ALC_T_NLINK_SH, - lnk_size*sizeof(Uint)); - } else { - n = (ErtsLink *) erts_alloc(ERTS_ALC_T_NLINK_LH, - lnk_size*sizeof(Uint)); - erts_atomic_add_nob(&tot_link_lh_size, lnk_size*sizeof(Uint)); - } - hp = n->heap; - - - n->left = n->right = NULL; /* Always the same initial value*/ - n->type = (Uint16) type; - n->balance = 0; /* Always the same initial value */ - if (n->type == LINK_NODE) { - ERTS_LINK_REFC(n) = 0; - } else { - ERTS_LINK_ROOT(n) = NULL; - } - CP_LINK_VAL(n->pid, hp, pid); - - return n; -} - -#undef CP_LINK_VAL - -static ErtsSuspendMonitor *create_suspend_monitor(Eterm pid) -{ - ErtsSuspendMonitor *smon = erts_alloc(ERTS_ALC_T_SUSPEND_MON, - sizeof(ErtsSuspendMonitor)); - smon->left = smon->right = NULL; /* Always the same initial value */ - smon->balance = 0; /* Always the same initial value */ - smon->pending = 0; - smon->active = 0; - smon->pid = pid; - return smon; -} - -void -erts_init_monitors(void) -{ - erts_atomic_init_nob(&tot_link_lh_size, 0); -} - -Uint -erts_tot_link_lh_size(void) -{ - return (Uint) erts_atomic_read_nob(&tot_link_lh_size); -} - -void erts_destroy_monitor(ErtsMonitor *mon) -{ - Uint mon_size = ERTS_MONITOR_SIZE; - ErlNode *node; - - ASSERT(is_not_immed(mon->ref)); - mon_size += NC_HEAP_SIZE(mon->ref); - if (is_external(mon->ref)) { - node = external_thing_ptr(mon->ref)->node; - erts_deref_node_entry(node); - } - if (mon->type != MON_NIF_TARGET && is_not_immed(mon->u.pid)) { - mon_size += NC_HEAP_SIZE(mon->u.pid); - if (is_external(mon->u.pid)) { - node = external_thing_ptr(mon->u.pid)->node; - erts_deref_node_entry(node); - } - } - if (mon_size <= ERTS_MONITOR_SH_SIZE) { - erts_free(ERTS_ALC_T_MONITOR_SH, (void *) mon); - } else { - erts_free(ERTS_ALC_T_MONITOR_LH, (void *) mon); - erts_atomic_add_nob(&tot_link_lh_size, -1*mon_size*sizeof(Uint)); - } -} - -void erts_destroy_link(ErtsLink *lnk) -{ - Uint lnk_size = ERTS_LINK_SIZE; - ErlNode *node; - - ASSERT(lnk->type == LINK_NODE || ERTS_LINK_ROOT(lnk) == NULL); - - if (is_not_immed(lnk->pid)) { - lnk_size += NC_HEAP_SIZE(lnk->pid); - if (is_external(lnk->pid)) { - node = external_thing_ptr(lnk->pid)->node; - erts_deref_node_entry(node); - } - } - if (lnk_size <= ERTS_LINK_SH_SIZE) { - erts_free(ERTS_ALC_T_NLINK_SH, (void *) lnk); - } else { - erts_free(ERTS_ALC_T_NLINK_LH, (void *) lnk); - erts_atomic_add_nob(&tot_link_lh_size, -1*lnk_size*sizeof(Uint)); - } -} - -void erts_destroy_suspend_monitor(ErtsSuspendMonitor *smon) -{ - erts_free(ERTS_ALC_T_SUSPEND_MON, smon); -} - -static void insertion_rotation(int dstack[], int dpos, - void *tstack[], int tpos, - int state) { - - ErtsMonitorOrLink **this; - ErtsMonitorOrLink *p1, *p2, *p; - int dir; - - while (state && ( dir = dstack[--dpos] ) != DIR_END) { - this = tstack[--tpos]; - p = *this; - if (dir == DIR_LEFT) { - switch (p->balance) { - case 1: - p->balance = 0; - state = 0; - break; - case 0: - p->balance = -1; - break; - case -1: /* The icky case */ - p1 = p->left; - if (p1->balance == -1) { /* Single LL rotation */ - p->left = p1->right; - p1->right = p; - p->balance = 0; - (*this) = p1; - } else { /* Double RR rotation */ - p2 = p1->right; - p1->right = p2->left; - p2->left = p1; - p->left = p2->right; - p2->right = p; - p->balance = (p2->balance == -1) ? +1 : 0; - p1->balance = (p2->balance == 1) ? -1 : 0; - (*this) = p2; - } - (*this)->balance = 0; - state = 0; - break; - } - } else { /* dir == DIR_RIGHT */ - switch (p->balance) { - case -1: - p->balance = 0; - state = 0; - break; - case 0: - p->balance = 1; - break; - case 1: - p1 = p->right; - if (p1->balance == 1) { /* Single RR rotation */ - p->right = p1->left; - p1->left = p; - p->balance = 0; - (*this) = p1; - } else { /* Double RL rotation */ - p2 = p1->left; - p1->left = p2->right; - p2->right = p1; - p->right = p2->left; - p2->left = p; - p->balance = (p2->balance == 1) ? -1 : 0; - p1->balance = (p2->balance == -1) ? 1 : 0; - (*this) = p2; - } - (*this)->balance = 0; - state = 0; - break; - } - } - } -} - -void erts_add_monitor(ErtsMonitor **root, Uint type, Eterm ref, UWord entity, - Eterm name) -{ - void *tstack[STACK_NEED]; - int tpos = 0; - int dstack[STACK_NEED+1]; - int dpos = 1; - int state = 0; - ErtsMonitor **this = root; - Sint c; - - ASSERT(is_internal_ordinary_ref(ref) || is_external_ref(ref)); - - dstack[0] = DIR_END; - for (;;) { - if (!*this) { /* Found our place */ - state = 1; - *this = create_monitor(type,ref,entity,name); - break; - } else if ((c = CMP_MON_REF(ref,(*this)->ref)) < 0) { - /* go left */ - dstack[dpos++] = DIR_LEFT; - tstack[tpos++] = this; - this = &((*this)->left); - } else if (c > 0) { /* go right */ - dstack[dpos++] = DIR_RIGHT; - tstack[tpos++] = this; - this = &((*this)->right); - } else { /* Equal key is an error for monitors */ - erts_exit(ERTS_ERROR_EXIT,"Insertion of already present monitor!"); - break; - } - } - insertion_rotation(dstack, dpos, tstack, tpos, state); -} - - -/* Returns 0 if OK, < 0 if already present */ -int erts_add_link(ErtsLink **root, Uint type, Eterm pid) -{ - void *tstack[STACK_NEED]; - int tpos = 0; - int dstack[STACK_NEED+1]; - int dpos = 1; - int state = 0; - ErtsLink **this = root; - Sint c; - - dstack[0] = DIR_END; - for (;;) { - if (!*this) { /* Found our place */ - state = 1; - *this = create_link(type,pid); - break; - } else if ((c = CMP(pid,(*this)->pid)) < 0) { - /* go left */ - dstack[dpos++] = DIR_LEFT; - tstack[tpos++] = this; - this = &((*this)->left); - } else if (c > 0) { /* go right */ - dstack[dpos++] = DIR_RIGHT; - tstack[tpos++] = this; - this = &((*this)->right); - } else { /* Equal key is an error for monitors */ - return -1; - } - } - insertion_rotation(dstack, dpos, tstack, tpos, state); - return 0; -} - -ErtsSuspendMonitor * -erts_add_or_lookup_suspend_monitor(ErtsSuspendMonitor **root, Eterm pid) -{ - void *tstack[STACK_NEED]; - int tpos = 0; - int dstack[STACK_NEED+1]; - int dpos = 1; - int state = 0; - ErtsSuspendMonitor **this = root; - ErtsSuspendMonitor *res; - Sint c; - - dstack[0] = DIR_END; - for (;;) { - if (!*this) { /* Found our place */ - state = 1; - res = *this = create_suspend_monitor(pid); - break; - } else if ((c = CMP(pid,(*this)->pid)) < 0) { - /* go left */ - dstack[dpos++] = DIR_LEFT; - tstack[tpos++] = this; - this = &((*this)->left); - } else if (c > 0) { /* go right */ - dstack[dpos++] = DIR_RIGHT; - tstack[tpos++] = this; - this = &((*this)->right); - } else { /* Already here... */ - ASSERT((*this)->pid == pid); - return *this; - } - } - insertion_rotation(dstack, dpos, tstack, tpos, state); - return res; -} - - -/* Returns the new or old link structure */ -ErtsLink *erts_add_or_lookup_link(ErtsLink **root, Uint type, Eterm pid) -{ - void *tstack[STACK_NEED]; - int tpos = 0; - int dstack[STACK_NEED+1]; - int dpos = 1; - int state = 0; - ErtsLink **this = root; - Sint c; - ErtsLink *ret = NULL; - - dstack[0] = DIR_END; - for (;;) { - if (!*this) { /* Found our place */ - state = 1; - *this = create_link(type,pid); - ret = *this; - break; - } else if ((c = CMP(pid,(*this)->pid)) < 0) { - /* go left */ - dstack[dpos++] = DIR_LEFT; - tstack[tpos++] = this; - this = &((*this)->left); - } else if (c > 0) { /* go right */ - dstack[dpos++] = DIR_RIGHT; - tstack[tpos++] = this; - this = &((*this)->right); - } else { /* Equal key is an error for monitors */ - return *this; - } - } - insertion_rotation(dstack, dpos, tstack, tpos, state); - return ret; -} - - -/* - * Deletion helpers - */ -static int balance_left(ErtsMonitorOrLink **this) -{ - ErtsMonitorOrLink *p, *p1, *p2; - int b1, b2, h = 1; - - p = *this; - switch (p->balance) { - case -1: - p->balance = 0; - break; - case 0: - p->balance = 1; - h = 0; - break; - case 1: - p1 = p->right; - b1 = p1->balance; - if (b1 >= 0) { /* Single RR rotation */ - p->right = p1->left; - p1->left = p; - if (b1 == 0) { - p->balance = 1; - p1->balance = -1; - h = 0; - } else { - p->balance = p1->balance = 0; - } - (*this) = p1; - } else { /* Double RL rotation */ - p2 = p1->left; - b2 = p2->balance; - p1->left = p2->right; - p2->right = p1; - p->right = p2->left; - p2->left = p; - p->balance = (b2 == 1) ? -1 : 0; - p1->balance = (b2 == -1) ? 1 : 0; - p2->balance = 0; - (*this) = p2; - } - break; - } - return h; -} - -static int balance_right(ErtsMonitorOrLink **this) -{ - ErtsMonitorOrLink *p, *p1, *p2; - int b1, b2, h = 1; - - p = *this; - switch (p->balance) { - case 1: - p->balance = 0; - break; - case 0: - p->balance = -1; - h = 0; - break; - case -1: - p1 = p->left; - b1 = p1->balance; - if (b1 <= 0) { /* Single LL rotation */ - p->left = p1->right; - p1->right = p; - if (b1 == 0) { - p->balance = -1; - p1->balance = 1; - h = 0; - } else { - p->balance = p1->balance = 0; - } - (*this) = p1; - } else { /* Double LR rotation */ - p2 = p1->right; - b2 = p2->balance; - p1->right = p2->left; - p2->left = p1; - p->left = p2->right; - p2->right = p; - p->balance = (b2 == -1) ? 1 : 0; - p1->balance = (b2 == 1) ? -1 : 0; - p2->balance = 0; - (*this) = p2; - } - } - return h; -} - -static int delsub(ErtsMonitorOrLink **this) -{ - ErtsMonitorOrLink **tstack[STACK_NEED]; - int tpos = 0; - ErtsMonitorOrLink *q = (*this); - ErtsMonitorOrLink **r = &(q->left); - int h; - - /* - * Walk down the tree to the right and search - * for a void right child, pick that child out - * and return it to be put in the deleted - * object's place. - */ - - while ((*r)->right != NULL) { - tstack[tpos++] = r; - r = &((*r)->right); - } - *this = *r; - *r = (*r)->left; - (*this)->left = q->left; - (*this)->right = q->right; - (*this)->balance = q->balance; - tstack[0] = &((*this)->left); - h = 1; - while (tpos && h) { - r = tstack[--tpos]; - h = balance_right(r); - } - return h; -} - -ErtsMonitor *erts_remove_monitor(ErtsMonitor **root, Eterm ref) -{ - ErtsMonitor **tstack[STACK_NEED]; - int tpos = 0; - int dstack[STACK_NEED+1]; - int dpos = 1; - int state = 0; - ErtsMonitor **this = root; - Sint c; - int dir; - ErtsMonitor *q = NULL; - - dstack[0] = DIR_END; - for (;;) { - if (!*this) { /* Failure */ - return NULL; - } else if ((c = CMP_MON_REF(ref,(*this)->ref)) < 0) { - dstack[dpos++] = DIR_LEFT; - tstack[tpos++] = this; - this = &((*this)->left); - } else if (c > 0) { /* go right */ - dstack[dpos++] = DIR_RIGHT; - tstack[tpos++] = this; - this = &((*this)->right); - } else { /* Equal key, found the one to delete */ - q = (*this); - if (q->right == NULL) { - (*this) = q->left; - state = 1; - } else if (q->left == NULL) { - (*this) = q->right; - state = 1; - } else { - dstack[dpos++] = DIR_LEFT; - tstack[tpos++] = this; - state = delsub((ErtsMonitorOrLink **) this); - } - break; - } - } - while (state && ( dir = dstack[--dpos] ) != DIR_END) { - this = tstack[--tpos]; - if (dir == DIR_LEFT) { - state = balance_left((ErtsMonitorOrLink **) this); - } else { - state = balance_right((ErtsMonitorOrLink **) this); - } - } - return q; -} - -ErtsLink *erts_remove_link(ErtsLink **root, Eterm pid) -{ - ErtsLink **tstack[STACK_NEED]; - int tpos = 0; - int dstack[STACK_NEED+1]; - int dpos = 1; - int state = 0; - ErtsLink **this = root; - Sint c; - int dir; - ErtsLink *q = NULL; - - dstack[0] = DIR_END; - for (;;) { - if (!*this) { /* Failure */ - return NULL; - } else if ((c = CMP(pid,(*this)->pid)) < 0) { - dstack[dpos++] = DIR_LEFT; - tstack[tpos++] = this; - this = &((*this)->left); - } else if (c > 0) { /* go right */ - dstack[dpos++] = DIR_RIGHT; - tstack[tpos++] = this; - this = &((*this)->right); - } else { /* Equal key, found the one to delete */ - q = (*this); - if (q->right == NULL) { - (*this) = q->left; - state = 1; - } else if (q->left == NULL) { - (*this) = q->right; - state = 1; - } else { - dstack[dpos++] = DIR_LEFT; - tstack[tpos++] = this; - state = delsub((ErtsMonitorOrLink **) this); - } - break; - } - } - while (state && ( dir = dstack[--dpos] ) != DIR_END) { - this = tstack[--tpos]; - if (dir == DIR_LEFT) { - state = balance_left((ErtsMonitorOrLink **) this); - } else { - state = balance_right((ErtsMonitorOrLink **) this); - } - } - return q; -} - -void -erts_delete_suspend_monitor(ErtsSuspendMonitor **root, Eterm pid) -{ - ErtsSuspendMonitor **tstack[STACK_NEED]; - int tpos = 0; - int dstack[STACK_NEED+1]; - int dpos = 1; - int state = 0; - ErtsSuspendMonitor **this = root; - Sint c; - int dir; - ErtsSuspendMonitor *q = NULL; - - dstack[0] = DIR_END; - for (;;) { - if (!*this) { /* Nothing found */ - return; - } else if ((c = CMP(pid,(*this)->pid)) < 0) { - dstack[dpos++] = DIR_LEFT; - tstack[tpos++] = this; - this = &((*this)->left); - } else if (c > 0) { /* go right */ - dstack[dpos++] = DIR_RIGHT; - tstack[tpos++] = this; - this = &((*this)->right); - } else { /* Equal key, found the one to delete */ - q = (*this); - ASSERT(q->pid == pid); - if (q->right == NULL) { - (*this) = q->left; - state = 1; - } else if (q->left == NULL) { - (*this) = q->right; - state = 1; - } else { - dstack[dpos++] = DIR_LEFT; - tstack[tpos++] = this; - state = delsub((ErtsMonitorOrLink **) this); - } - erts_destroy_suspend_monitor(q); - break; - } - } - while (state && ( dir = dstack[--dpos] ) != DIR_END) { - this = tstack[--tpos]; - if (dir == DIR_LEFT) { - state = balance_left((ErtsMonitorOrLink **) this); - } else { - state = balance_right((ErtsMonitorOrLink **) this); - } - } -} - -ErtsMonitor *erts_lookup_monitor(ErtsMonitor *root, Eterm ref) -{ - Sint c; - - for (;;) { - if (root == NULL || (c = CMP_MON_REF(ref,root->ref)) == 0) { - return root; - } else if (c < 0) { - root = root->left; - } else { /* c > 0 */ - root = root->right; - } - } -} - -ErtsLink *erts_lookup_link(ErtsLink *root, Eterm pid) -{ - Sint c; - - for (;;) { - if (root == NULL || (c = CMP(pid,root->pid)) == 0) { - return root; - } else if (c < 0) { - root = root->left; - } else { /* c > 0 */ - root = root->right; - } - } -} - -ErtsSuspendMonitor * -erts_lookup_suspend_monitor(ErtsSuspendMonitor *root, Eterm pid) -{ - Sint c; - - for (;;) { - if (root == NULL || (c = CMP(pid,root->pid)) == 0) { - return root; - } else if (c < 0) { - root = root->left; - } else { /* c > 0 */ - root = root->right; - } - } -} - -void erts_sweep_monitors(ErtsMonitor *root, - void (*doit)(ErtsMonitor *, void *), - void *context) -{ - ErtsMonitor *tstack[STACK_NEED]; - int tpos = 0; - int dstack[STACK_NEED+1]; - int dpos = 1; - int dir; - - dstack[0] = DIR_END; - - for (;;) { - if (root == NULL) { - if ((dir = dstack[dpos-1]) == DIR_END) { - return; - } - if (dir == DIR_LEFT) { - /* Still has DIR_RIGHT to do */ - dstack[dpos-1] = DIR_RIGHT; - root = (tstack[tpos-1])->right; - } else { - /* stacktop is an object to be deleted */ - (*doit)(tstack[--tpos],context); /* expeted to do the - deletion */ - --dpos; - root = NULL; - } - } else { - dstack[dpos++] = DIR_LEFT; - tstack[tpos++] = root; - root = root->left; - } - } -} - -void erts_sweep_links(ErtsLink *root, - void (*doit)(ErtsLink *, void *), - void *context) -{ - ErtsLink *tstack[STACK_NEED]; - int tpos = 0; - int dstack[STACK_NEED+1]; - int dpos = 1; - int dir; - - dstack[0] = DIR_END; - - for (;;) { - if (root == NULL) { - if ((dir = dstack[dpos-1]) == DIR_END) { - return; - } - if (dir == DIR_LEFT) { - /* Still has DIR_RIGHT to do */ - dstack[dpos-1] = DIR_RIGHT; - root = (tstack[tpos-1])->right; - } else { - /* stacktop is an object to be deleted */ - (*doit)(tstack[--tpos],context); /* expeted to do the - deletion */ - --dpos; - root = NULL; - } - } else { - dstack[dpos++] = DIR_LEFT; - tstack[tpos++] = root; - root = root->left; - } - } -} - -void erts_sweep_suspend_monitors(ErtsSuspendMonitor *root, - void (*doit)(ErtsSuspendMonitor *, void *), - void *context) -{ - ErtsSuspendMonitor *tstack[STACK_NEED]; - int tpos = 0; - int dstack[STACK_NEED+1]; - int dpos = 1; - int dir; - - dstack[0] = DIR_END; - - for (;;) { - if (root == NULL) { - if ((dir = dstack[dpos-1]) == DIR_END) { - return; - } - if (dir == DIR_LEFT) { - /* Still has DIR_RIGHT to do */ - dstack[dpos-1] = DIR_RIGHT; - root = (tstack[tpos-1])->right; - } else { - /* stacktop is an object to be deleted */ - (*doit)(tstack[--tpos],context); /* expeted to do the - deletion */ - --dpos; - root = NULL; - } - } else { - dstack[dpos++] = DIR_LEFT; - tstack[tpos++] = root; - root = root->left; - } - } -} - - -/* Debug BIF, always present, but undocumented... */ - -static void erts_dump_monitors(ErtsMonitor *root, int indent) -{ - if (root == NULL) - return; - erts_dump_monitors(root->right,indent+2); - erts_printf("%*s[%b16d:%b16u:%T:%T", indent, "", root->balance, - root->type, root->ref, root->name); - if (root->type == MON_NIF_TARGET) - erts_printf(":%p]\n", root->u.resource); - else - erts_printf(":%T]\n", root->u.pid); - erts_dump_monitors(root->left,indent+2); -} - -static void erts_dump_links_aux(ErtsLink *root, int indent, - erts_dsprintf_buf_t *dsbufp) -{ - if (root == NULL) - return; - erts_dump_links_aux(root->right, indent+2, dsbufp); - dsbufp->str_len = 0; - erts_dsprintf(dsbufp, "%*s[%b16d:%b16u:%T:%p]", indent, "", - root->balance, root->type, root->pid, ERTS_LINK_ROOT(root)); - if (ERTS_LINK_ROOT(root) != NULL) { - ErtsLink *sub = ERTS_LINK_ROOT(root); - int len = dsbufp->str_len; - erts_dump_links_aux(sub->right, indent+len+5, dsbufp); - erts_dsprintf(dsbufp, "-> %*s[%b16d:%b16u:%T:%p]", indent, "", - sub->balance, sub->type, sub->pid, ERTS_LINK_ROOT(sub)); - erts_printf("%s\n", dsbufp->str); - erts_dump_links_aux(sub->left, indent+len+5, dsbufp); - } else { - erts_printf("%s\n", dsbufp->str); - } - erts_dump_links_aux(root->left, indent+2, dsbufp); -} - -static void erts_dump_links(ErtsLink *root, int indent) -{ - erts_dsprintf_buf_t *dsbufp = erts_create_tmp_dsbuf(0); - erts_dump_links_aux(root, indent, dsbufp); - erts_destroy_tmp_dsbuf(dsbufp); -} - -Eterm erts_debug_dump_monitors_1(BIF_ALIST_1) -{ - Process *p = BIF_P; - Eterm pid = BIF_ARG_1; - Process *rp; - DistEntry *dep; - rp = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, pid, ERTS_PROC_LOCK_LINK); - if (!rp) { - ERTS_ASSERT_IS_NOT_EXITING(p); - if (is_atom(pid) && is_node_name_atom(pid) && - (dep = erts_find_dist_entry(pid)) != NULL) { - erts_printf("Dumping dist monitors-------------------\n"); - erts_de_links_lock(dep); - erts_dump_monitors(dep->monitors,0); - erts_de_links_unlock(dep); - erts_printf("Monitors dumped-------------------------\n"); - BIF_RET(am_true); - } else { - BIF_ERROR(p,BADARG); - } - } else { - erts_printf("Dumping pid monitors--------------------\n"); - erts_dump_monitors(ERTS_P_MONITORS(rp),0); - erts_printf("Monitors dumped-------------------------\n"); - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - BIF_RET(am_true); - } -} - -Eterm erts_debug_dump_links_1(BIF_ALIST_1) -{ - Process *p = BIF_P; - Eterm pid = BIF_ARG_1; - Process *rp; - DistEntry *dep; - if (is_internal_port(pid)) { - Port *rport = erts_id2port_sflgs(pid, - p, - ERTS_PROC_LOCK_MAIN, - ERTS_PORT_SFLGS_INVALID_LOOKUP); - if (rport) { - erts_printf("Dumping port links----------------------\n"); - erts_dump_links(ERTS_P_LINKS(rport), 0); - erts_printf("Links dumped----------------------------\n"); - erts_port_release(rport); - BIF_RET(am_true); - } else { - BIF_ERROR(p,BADARG); - } - } else { - rp = erts_pid2proc(p, ERTS_PROC_LOCK_MAIN, pid, ERTS_PROC_LOCK_LINK); - if (!rp) { - ERTS_ASSERT_IS_NOT_EXITING(p); - if (is_atom(pid) && is_node_name_atom(pid) && - (dep = erts_find_dist_entry(pid)) != NULL) { - erts_printf("Dumping dist links----------------------\n"); - erts_de_links_lock(dep); - erts_dump_links(dep->nlinks,0); - erts_de_links_unlock(dep); - erts_printf("Links dumped----------------------------\n"); - BIF_RET(am_true); - } else { - BIF_ERROR(p,BADARG); - } - - } else { - erts_printf("Dumping pid links-----------------------\n"); - erts_dump_links(ERTS_P_LINKS(rp), 0); - erts_printf("Links dumped----------------------------\n"); - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - BIF_RET(am_true); - } - } -} - -void erts_one_link_size(ErtsLink *lnk, void *vpu) -{ - Uint *pu = vpu; - *pu += ERTS_LINK_SIZE*sizeof(Uint); - if(is_not_immed(lnk->pid)) - *pu += NC_HEAP_SIZE(lnk->pid)*sizeof(Uint); - if (lnk->type != LINK_NODE && ERTS_LINK_ROOT(lnk) != NULL) { - erts_doforall_links(ERTS_LINK_ROOT(lnk),&erts_one_link_size,vpu); - } -} -void erts_one_mon_size(ErtsMonitor *mon, void *vpu) -{ - Uint *pu = vpu; - *pu += ERTS_MONITOR_SIZE*sizeof(Uint); - if(mon->type != MON_NIF_TARGET && is_not_immed(mon->u.pid)) - *pu += NC_HEAP_SIZE(mon->u.pid)*sizeof(Uint); - if(is_not_immed(mon->ref)) - *pu += NC_HEAP_SIZE(mon->ref)*sizeof(Uint); -} diff --git a/erts/emulator/beam/erl_monitors.h b/erts/emulator/beam/erl_monitors.h deleted file mode 100644 index 1cacecd7e9..0000000000 --- a/erts/emulator/beam/erl_monitors.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2004-2017. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * %CopyrightEnd% - */ - -/********************************************************************** - * Header for monitors and links data structures. - * Monitors are kept in an AVL tree and the data structures for - * the four different types of monitors are like this: - ********************************************************************** - * Local monitor by pid/port: - * (Ref is always same in all involved data structures) - ********************************************************************** - * Process/Port X Process Y - * +-------------+ +-------------+ - * Type: | MON_ORIGIN | | MON_TARGET | - * +-------------+ +-------------+ - * Pid: | Pid(Y) | | Pid/Port(X) | - * +-------------+ +-------------+ - * Name: | [] | | [] | - * +-------------+ +-------------+ - ********************************************************************** - * Local monitor by name: (Ref is always same in all involved data structures) - ********************************************************************** - * Process X Process Y (name foo) - * +-------------+ +-------------+ - * Type: | MON_ORIGIN | | MON_TARGET | - * +-------------+ +-------------+ - * Pid: | Pid(Y) | | Pid(X) | - * +-------------+ +-------------+ - * Name: | Atom(foo) | | Atom(foo) | - * +-------------+ +-------------+ - ********************************************************************** - * Remote monitor by pid: (Ref is always same in all involved data structures) - ********************************************************************** - * Node A | Node B - * ---------------------------------+---------------------------------- - * Process X (@A) Distentry @A Distentry @B Process Y (@B) - * for node B for node A - * +-------------+ +-------------+ +-------------+ +-------------+ - * Type: | MON_ORIGIN | | MON_TARGET | | MON_ORIGIN | | MON_TARGET | - * +-------------+ +-------------+ +-------------+ +-------------+ - * Pid: | Pid(Y) | | Pid(X) | | Pid(Y) | | Pid(X) | - * +-------------+ +-------------+ +-------------+ +-------------+ - * Name: | [] | | [] | | [] | | [] | - * +-------------+ +-------------+ +-------------+ +-------------+ - ********************************************************************** - * Remote monitor by name: (Ref is always same in all involved data structures) - ********************************************************************** - * Node A | Node B - * ---------------------------------+---------------------------------- - * Process X (@A) Distentry @A Distentry @B Process Y (@B) - * for node B for node A (name foo) - * +-------------+ +-------------+ +-------------+ +-------------+ - * Type: | MON_ORIGIN | | MON_TARGET | | MON_ORIGIN | | MON_TARGET | - * +-------------+ +-------------+ +-------------+ +-------------+ - * Pid: | Atom(node B)| | Pid(X) | | Pid(Y) | | Pid(X) | - * +-------------+ +-------------+ +-------------+ +-------------+ - * Name: | Atom(foo) | | Atom(foo) | | Atom(foo) | | Atom(foo) | - * +-------------+ +-------------+ +-------------+ +-------------+ - * The reason for the node atom in X->pid is that we don't know the actual - * pid of the monitored process on the other node when setting the monitor - * (which is done asyncronously). - **********************************************************************/ -#ifndef _ERL_MONITORS_H -#define _ERL_MONITORS_H - -/* Type tags for monitors */ -#define MON_ORIGIN 1 -#define MON_TARGET 2 -#define MON_NIF_TARGET 3 -#define MON_TIME_OFFSET 4 - -/* Type tags for links */ -#define LINK_PID 1 /* ...Or port */ -#define LINK_NODE 3 /* "Node monitor" */ - -/* Size of a monitor without heap, in words (fixalloc) */ -#define ERTS_MONITOR_SIZE ((sizeof(ErtsMonitor) - sizeof(Uint))/sizeof(Uint)) -#define ERTS_MONITOR_SH_SIZE (ERTS_MONITOR_SIZE + ERTS_REF_THING_SIZE) -#define ERTS_LINK_SIZE ((sizeof(ErtsLink) - sizeof(Uint))/sizeof(Uint)) -#define ERTS_LINK_SH_SIZE ERTS_LINK_SIZE /* Size of fix-alloced links */ - -/* ErtsMonitor and ErtsLink *need* to begin in a similar way as - ErtsMonitorOrLink */ -typedef struct erts_monitor_or_link { - struct erts_monitor_or_link *left, *right; - Sint16 balance; -} ErtsMonitorOrLink; - -typedef struct erts_monitor { - struct erts_monitor *left, *right; - Sint16 balance; - Uint16 type; /* MON_ORIGIN | MON_TARGET | MON_NIF_TARGET | MON_TIME_OFFSET */ - Eterm ref; - union { - Eterm pid; /* In case of distributed named monitor, this is the - * nodename atom in MON_ORIGIN process, otherwise a pid or, - * in case of a MON_TARGET, a port - */ - struct ErtsResource_* resource; /* MON_NIF_TARGET */ - }u; - Eterm name; /* When monitoring a named process: atom() else [] */ - Uint heap[1]; /* Larger in reality */ -} ErtsMonitor; - -typedef struct erts_link { - struct erts_link *left, *right; - Sint16 balance; - Uint16 type; /* LINK_PID | LINK_NODE */ - Eterm pid; /* When node monitor, - the node atom is here instead */ - union { - struct erts_link *root; /* Used only in dist entries */ - Uint refc; - } shared; - Uint heap[1]; /* Larger in reality */ -} ErtsLink; - -typedef struct erts_suspend_monitor { - struct erts_suspend_monitor *left, *right; - Sint16 balance; - - int pending; - int active; - Eterm pid; -} ErtsSuspendMonitor; - -#define ERTS_LINK_ROOT(Linkp) ((Linkp)->shared.root) -#define ERTS_LINK_REFC(Linkp) ((Linkp)->shared.refc) - -Uint erts_tot_link_lh_size(void); - - -/* Prototypes */ -void erts_destroy_monitor(ErtsMonitor *mon); -void erts_add_monitor(ErtsMonitor **root, Uint type, Eterm ref, UWord entity, - Eterm name); -ErtsMonitor *erts_remove_monitor(ErtsMonitor **root, Eterm ref); -ErtsMonitor *erts_lookup_monitor(ErtsMonitor *root, Eterm ref); -void erts_sweep_monitors(ErtsMonitor *root, - void (*doit)(ErtsMonitor *, void *), - void *context); - -void erts_destroy_link(ErtsLink *lnk); -/* Returns 0 if OK, < 0 if already present */ -int erts_add_link(ErtsLink **root, Uint type, Eterm pid); -ErtsLink *erts_add_or_lookup_link(ErtsLink **root, Uint type, Eterm pid); -ErtsLink *erts_remove_link(ErtsLink **root, Eterm pid); -ErtsLink *erts_lookup_link(ErtsLink *root, Eterm pid); -void erts_sweep_links(ErtsLink *root, - void (*doit)(ErtsLink *, void *), - void *context); - -void erts_destroy_suspend_monitor(ErtsSuspendMonitor *sproc); -void erts_sweep_suspend_monitors(ErtsSuspendMonitor *root, - void (*doit)(ErtsSuspendMonitor *, void *), - void *context); -ErtsSuspendMonitor *erts_add_or_lookup_suspend_monitor(ErtsSuspendMonitor **root, - Eterm pid); -ErtsSuspendMonitor *erts_lookup_suspend_monitor(ErtsSuspendMonitor *root, - Eterm pid); -void erts_delete_suspend_monitor(ErtsSuspendMonitor **root, Eterm pid); -void erts_init_monitors(void); -void erts_one_link_size(ErtsLink *lnk, void *vpu); -void erts_one_mon_size(ErtsMonitor *mon, void *vpu); - -#define erts_doforall_monitors erts_sweep_monitors -#define erts_doforall_links erts_sweep_links -#define erts_doforall_suspend_monitors erts_sweep_suspend_monitors - -#endif /* _ERL_MONITORS_H */ diff --git a/erts/emulator/beam/erl_msacc.c b/erts/emulator/beam/erl_msacc.c index d659842b7e..d13d6080e1 100644 --- a/erts/emulator/beam/erl_msacc.c +++ b/erts/emulator/beam/erl_msacc.c @@ -81,7 +81,7 @@ void erts_msacc_init(void) { sizeof(Eterm)*ERTS_MSACC_STATE_COUNT); for (i = 0; i < ERTS_MSACC_STATE_COUNT; i++) { erts_msacc_state_atoms[i] = am_atom_put(erts_msacc_states[i], - strlen(erts_msacc_states[i])); + sys_strlen(erts_msacc_states[i])); } } @@ -191,7 +191,7 @@ Eterm erts_msacc_gather_stats(ErtsMsAcc *msacc, ErtsHeapFactory *factory) { map->keys = key; hp[0] = state_map; hp[1] = msacc->id; - hp[2] = am_atom_put(msacc->type,strlen(msacc->type)); + hp[2] = am_atom_put(msacc->type,sys_strlen(msacc->type)); return make_flatmap(map); } diff --git a/erts/emulator/beam/erl_mtrace.c b/erts/emulator/beam/erl_mtrace.c index f2a660f085..2807b443a1 100644 --- a/erts/emulator/beam/erl_mtrace.c +++ b/erts/emulator/beam/erl_mtrace.c @@ -314,7 +314,7 @@ disable_trace(int error, char *reason, int eno) erts_fprintf(stderr, "%s: %s\n", mt_dis, reason); else { eno_str = erl_errno_id(eno); - if (strcmp(eno_str, "unknown") == 0) + if (sys_strcmp(eno_str, "unknown") == 0) erts_fprintf(stderr, "%s: %s: %d\n", mt_dis, reason, eno); else erts_fprintf(stderr, "%s: %s: %s\n", mt_dis, reason, eno_str); @@ -401,9 +401,9 @@ write_trace_header(char *nodename, char *pid, char *hostname) PUT_UI32(tracep, ERTS_MT_MAJOR_VSN); PUT_UI32(tracep, ERTS_MT_MINOR_VSN); - n_len = strlen(nodename); - h_len = strlen(hostname); - p_len = strlen(pid); + n_len = sys_strlen(nodename); + h_len = sys_strlen(hostname); + p_len = sys_strlen(pid); hdr_prolog_len = (2*UI32_SZ + 3*UI16_SZ + 3*UI32_SZ @@ -436,15 +436,15 @@ write_trace_header(char *nodename, char *pid, char *hostname) PUT_UI32(tracep, start_time.usec); PUT_UI8(tracep, (byte) n_len); - memcpy((void *) tracep, (void *) nodename, n_len); + sys_memcpy((void *) tracep, (void *) nodename, n_len); tracep += n_len; PUT_UI8(tracep, (byte) h_len); - memcpy((void *) tracep, (void *) hostname, h_len); + sys_memcpy((void *) tracep, (void *) hostname, h_len); tracep += h_len; PUT_UI8(tracep, (byte) p_len); - memcpy((void *) tracep, (void *) pid, p_len); + sys_memcpy((void *) tracep, (void *) pid, p_len); tracep += p_len; ASSERT(startp + hdr_prolog_len == tracep); @@ -472,7 +472,7 @@ write_trace_header(char *nodename, char *pid, char *hostname) str = ERTS_ALC_A2AD(i); ASSERT(str); - str_len = strlen(str); + str_len = sys_strlen(str); if (str_len >= (1 << 8)) { disable_trace(1, "Excessively large allocator string", 0); return 0; @@ -493,7 +493,7 @@ write_trace_header(char *nodename, char *pid, char *hostname) PUT_UI16(tracep, aflags); PUT_UI16(tracep, (Uint16) i); PUT_UI8( tracep, (byte) str_len); - memcpy((void *) tracep, (void *) str, str_len); + sys_memcpy((void *) tracep, (void *) str, str_len); tracep += str_len; if (erts_allctrs_info[i].alloc_util) { PUT_UI8(tracep, 2); @@ -519,7 +519,7 @@ write_trace_header(char *nodename, char *pid, char *hostname) str = ERTS_ALC_N2TD(i); ASSERT(str); - str_len = strlen(str); + str_len = sys_strlen(str); if (str_len >= (1 << 8)) { disable_trace(1, "Excessively large type string", 0); return 0; @@ -543,7 +543,7 @@ write_trace_header(char *nodename, char *pid, char *hostname) PUT_UI16(tracep, nflags); PUT_UI16(tracep, (Uint16) i); PUT_UI8(tracep, (byte) str_len); - memcpy((void *) tracep, (void *) str, str_len); + sys_memcpy((void *) tracep, (void *) str, str_len); tracep += str_len; PUT_UI16(tracep, no); ASSERT(startp + entry_sz == tracep); diff --git a/erts/emulator/beam/erl_nif.c b/erts/emulator/beam/erl_nif.c index d2000aa71e..0e0013e8a4 100644 --- a/erts/emulator/beam/erl_nif.c +++ b/erts/emulator/beam/erl_nif.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2017. All Rights Reserved. + * Copyright Ericsson AB 2009-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,6 +57,7 @@ #include "erl_bif_unique.h" #include "erl_utils.h" #include "erl_io_queue.h" +#include "erl_proc_sig_queue.h" #undef ERTS_WANT_NFUNC_SCHED_INTERNALS__ #define ERTS_WANT_NFUNC_SCHED_INTERNALS__ #include "erl_nfunc_sched.h" @@ -529,7 +530,7 @@ setup_nif_env(struct enif_msg_environment_t* msg_env, msg_env->env.tmp_obj_list = NULL; msg_env->env.proc = &msg_env->phony_proc; msg_env->env.exception_thrown = 0; - memset(&msg_env->phony_proc, 0, sizeof(Process)); + sys_memset(&msg_env->phony_proc, 0, sizeof(Process)); HEAP_START(&msg_env->phony_proc) = phony_heap; HEAP_TOP(&msg_env->phony_proc) = phony_heap; HEAP_LIMIT(&msg_env->phony_proc) = phony_heap; @@ -658,7 +659,7 @@ int erts_flush_trace_messages(Process *c_p, ErtsProcLocks c_p_locks) rp_locks = 0; if (rp->common.id == c_p->common.id) rp_locks = c_p_locks; - erts_queue_messages(rp, rp_locks, first, last, len, c_p->common.id); + erts_queue_messages(rp, rp_locks, first, last, len); if (rp->common.id == c_p->common.id) rp_locks &= ~c_p_locks; if (rp_locks) @@ -700,6 +701,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, Process* rp; Process* c_p; ErtsMessage *mp; + Eterm from; Eterm receiver = to_pid->pid; int scheduler; @@ -778,7 +780,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, msg = copy_struct_litopt(msg, sz, &hp, ohp, &litarea); } - ERL_MESSAGE_TERM(mp) = msg; + from = c_p ? c_p->common.id : am_undefined; if (!env || !env->tracee) { @@ -797,7 +799,6 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, ErlTraceMessageQueue *msgq; Process *t_p = env->tracee; - erts_proc_lock(t_p, ERTS_PROC_LOCK_TRACE); msgq = t_p->trace_msg_q; @@ -817,6 +818,9 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, rp_locks & ERTS_PROC_LOCK_MSGQ || erts_proc_trylock(rp, ERTS_PROC_LOCK_MSGQ) == EBUSY) { + ERL_MESSAGE_TERM(mp) = msg; + ERL_MESSAGE_FROM(mp) = from; + if (!msgq) { msgq = erts_alloc(ERTS_ALC_T_TRACE_MSG_QUEUE, sizeof(ErlTraceMessageQueue)); @@ -846,8 +850,7 @@ int enif_send(ErlNifEnv* env, const ErlNifPid* to_pid, } } - erts_queue_message(rp, rp_locks, mp, msg, - c_p ? c_p->common.id : am_undefined); + erts_queue_message(rp, rp_locks, mp, msg, from); done: if (c_p == rp) @@ -1963,6 +1966,12 @@ ErlNifTid enif_thread_self(void) { return erl_drv_thread_self(); } int enif_equal_tids(ErlNifTid tid1, ErlNifTid tid2) { return erl_drv_equal_tids(tid1,tid2); } void enif_thread_exit(void *resp) { erl_drv_thread_exit(resp); } int enif_thread_join(ErlNifTid tid, void **respp) { return erl_drv_thread_join(tid,respp); } + +char* enif_mutex_name(ErlNifMutex *mtx) {return erl_drv_mutex_name(mtx); } +char* enif_cond_name(ErlNifCond *cnd) { return erl_drv_cond_name(cnd); } +char* enif_rwlock_name(ErlNifRWLock* rwlck) { return erl_drv_rwlock_name(rwlck); } +char* enif_thread_name(ErlNifTid tid) { return erl_drv_thread_name(tid); } + int enif_getenv(const char *key, char *value, size_t *value_size) { return erl_drv_getenv(key, value, value_size); } ErlNifTime enif_monotonic_time(ErlNifTimeUnit time_unit) @@ -1985,16 +1994,21 @@ enif_convert_time_unit(ErlNifTime val, (int) to); } -int enif_fprintf(void* filep, const char* format, ...) +int enif_fprintf(FILE* filep, const char* format, ...) { int ret; va_list arglist; va_start(arglist, format); - ret = erts_vfprintf((FILE*)filep, format, arglist); + ret = erts_vfprintf(filep, format, arglist); va_end(arglist); return ret; } +int enif_vfprintf(FILE* filep, const char *format, va_list ap) +{ + return erts_vfprintf(filep, format, ap); +} + int enif_snprintf(char *buffer, size_t size, const char* format, ...) { int ret; @@ -2005,6 +2019,12 @@ int enif_snprintf(char *buffer, size_t size, const char* format, ...) return ret; } +int enif_vsnprintf(char* buffer, size_t size, const char *format, va_list ap) +{ + return erts_vsnprintf(buffer, size, format, ap); +} + + /*********************************************************** ** Memory managed (GC'ed) "resource" objects ** ***********************************************************/ @@ -2206,71 +2226,61 @@ static void rollback_opened_resource_types(void) } } -struct destroy_monitor_ctx -{ - ErtsResource* resource; - int exiting_procs; - int scheduler; -}; +#ifdef ARCH_64 +# define ERTS_RESOURCE_DYING_FLAG (((Uint) 1) << 63) +#else +# define ERTS_RESOURCE_DYING_FLAG (((Uint) 1) << 31) +#endif +#define ERTS_RESOURCE_REFC_MASK (~ERTS_RESOURCE_DYING_FLAG) -static void destroy_one_monitor(ErtsMonitor* mon, void* context) +static ERTS_INLINE void +rmon_set_dying(ErtsResourceMonitors *rms) { - struct destroy_monitor_ctx* ctx = (struct destroy_monitor_ctx*) context; - Process* rp; - ErtsMonitor *rmon = NULL; - int is_exiting; + rms->refc |= ERTS_RESOURCE_DYING_FLAG; +} - ASSERT(mon->type == MON_ORIGIN); - ASSERT(is_internal_pid(mon->u.pid)); - ASSERT(is_internal_ref(mon->ref)); +static ERTS_INLINE int +rmon_is_dying(ErtsResourceMonitors *rms) +{ + return !!(rms->refc & ERTS_RESOURCE_DYING_FLAG); +} - if (ctx->scheduler > 0) { /* Normal scheduler */ - rp = erts_proc_lookup(mon->u.pid); - } - else { - rp = erts_proc_lookup_inc_refc(mon->u.pid); - } +static ERTS_INLINE void +rmon_refc_inc(ErtsResourceMonitors *rms) +{ + rms->refc++; +} - if (!rp) { - is_exiting = 1; - } - if (rp) { - erts_proc_lock(rp, ERTS_PROC_LOCK_LINK); - if (ERTS_PROC_IS_EXITING(rp)) { - is_exiting = 1; - } else { - rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref); - ASSERT(rmon); - is_exiting = 0; - } - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - if (ctx->scheduler <= 0) - erts_proc_dec_refc(rp); - } - if (is_exiting) { - ctx->resource->monitors->pending_failed_fire++; - } +static ERTS_INLINE Uint +rmon_refc_dec_read(ErtsResourceMonitors *rms) +{ + Uint res; + ASSERT((rms->refc & ERTS_RESOURCE_REFC_MASK) != 0); + res = --rms->refc; + return res & ERTS_RESOURCE_REFC_MASK; +} - /* ToDo: Delay destruction after monitor_locks */ - if (rmon) { - ASSERT(rmon->type == MON_NIF_TARGET); - ASSERT(rmon->u.resource == ctx->resource); - erts_destroy_monitor(rmon); - } - erts_destroy_monitor(mon); +static ERTS_INLINE void +rmon_refc_dec(ErtsResourceMonitors *rms) +{ + ASSERT((rms->refc & ERTS_RESOURCE_REFC_MASK) != 0); + --rms->refc; } -static void destroy_all_monitors(ErtsMonitor* monitors, ErtsResource* resource) +static ERTS_INLINE Uint +rmon_refc_read(ErtsResourceMonitors *rms) { - struct destroy_monitor_ctx ctx; + return rms->refc & ERTS_RESOURCE_REFC_MASK; +} - execution_state(NULL, NULL, &ctx.scheduler); +static void dtor_demonitor(ErtsMonitor* mon, void* context) +{ + ASSERT(erts_monitor_is_origin(mon)); + ASSERT(is_internal_pid(mon->other.item)); - ctx.resource = resource; - erts_sweep_monitors(monitors, &destroy_one_monitor, &ctx); + erts_proc_sig_send_demonitor(mon); } - # define NIF_RESOURCE_DTOR &nif_resource_dtor static int nif_resource_dtor(Binary* bin) @@ -2281,34 +2291,36 @@ static int nif_resource_dtor(Binary* bin) if (resource->monitors) { ErtsResourceMonitors* rm = resource->monitors; + int kill; + ErtsMonitor *root; + Uint refc; ASSERT(type->down); erts_mtx_lock(&rm->lock); ASSERT(erts_refc_read(&bin->intern.refc, 0) == 0); - if (rm->root) { - ASSERT(!rm->is_dying); - destroy_all_monitors(rm->root, resource); + kill = !rmon_is_dying(rm); + if (kill) { + rmon_set_dying(rm); + root = rm->root; rm->root = NULL; } - if (rm->pending_failed_fire) { - /* - * Resource death struggle prolonged to serve exiting process(es). - * Destructor will be called again when last exiting process - * tries to fire its MON_NIF_TARGET monitor (and fails). - * - * This resource is doomed. It has no "real" references and - * should get not get called upon to do anything except the - * final destructor call. - * - * We keep refc at 0 and use a separate counter for exiting - * processes to avoid resource getting revived by "dec_term". - */ - ASSERT(!rm->is_dying); - rm->is_dying = 1; - erts_mtx_unlock(&rm->lock); - return 0; - } + refc = rmon_refc_read(rm); erts_mtx_unlock(&rm->lock); + + if (kill) + erts_monitor_tree_foreach_delete(&root, + dtor_demonitor, + NULL); + + /* + * If resource->monitors->refc != 0 there are + * outstanding references to the resource from + * monitors that has not been removed yet. + * nif_resource_dtor() will be called again this + * reference count reach zero. + */ + if (refc != 0) + return 0; /* we'll be back... */ erts_mtx_destroy(&rm->lock); } @@ -2338,54 +2350,82 @@ void erts_resource_stop(ErtsResource* resource, ErlNifEvent e, post_nif_noproc(&msg_env); } -void erts_fire_nif_monitor(ErtsResource* resource, Eterm pid, Eterm ref) +void erts_nif_demonitored(ErtsResource* resource) { - ErtsMonitor* rmon; + ErtsResourceMonitors* rmp = resource->monitors; ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource); + int free_me; + + ASSERT(rmp); + ASSERT(resource->type->down); + + erts_mtx_lock(&rmp->lock); + free_me = ((rmon_refc_dec_read(rmp) == 0) & !!rmon_is_dying(rmp)); + erts_mtx_unlock(&rmp->lock); + + if (free_me) + erts_bin_free(&bin->binary); +} + +void erts_fire_nif_monitor(ErtsMonitor *tmon) +{ + ErtsResource* resource; + ErtsMonitorData *mdp; + ErtsMonitor *omon; + ErtsBinary* bin; struct enif_msg_environment_t msg_env; ErlNifPid nif_pid; ErlNifMonitor nif_monitor; - ErtsResourceMonitors* rmp = resource->monitors; + ErtsResourceMonitors* rmp; + Uint mrefc, brefc; + int active, is_dying; + + ASSERT(tmon->type == ERTS_MON_TYPE_RESOURCE); + ASSERT(erts_monitor_is_target(tmon)); + + resource = tmon->other.ptr; + bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource); + rmp = resource->monitors; + + mdp = erts_monitor_to_data(tmon); + omon = &mdp->origin; ASSERT(rmp); ASSERT(resource->type->down); erts_mtx_lock(&rmp->lock); - rmon = erts_remove_monitor(&rmp->root, ref); - if (!rmon) { - int free_me = (--rmp->pending_failed_fire == 0) && rmp->is_dying; - ASSERT(rmp->pending_failed_fire >= 0); - erts_mtx_unlock(&rmp->lock); - - if (free_me) { - ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) == 0); - erts_bin_free(&bin->binary); - } - return; + + mrefc = rmon_refc_dec_read(rmp); + is_dying = rmon_is_dying(rmp); + active = !is_dying && erts_monitor_is_in_table(omon); + + if (active) { + erts_monitor_tree_delete(&rmp->root, omon); + brefc = (Uint) erts_refc_inc_unless(&bin->binary.intern.refc, 0, 0); } - ASSERT(!rmp->is_dying); - if (erts_refc_inc_unless(&bin->binary.intern.refc, 0, 0) == 0) { - /* - * Racing resource destruction. - * To avoid a more complex refc-dance with destructing thread - * we avoid calling 'down' and just silently remove the monitor. - * This can happen even for non smp as destructor calls may be scheduled. - */ - erts_mtx_unlock(&rmp->lock); + + erts_mtx_unlock(&rmp->lock); + + if (!active) { + ASSERT(!is_dying || erts_refc_read(&bin->binary.intern.refc, 0) == 0); + if (is_dying && mrefc == 0) + erts_bin_free(&bin->binary); + erts_monitor_release(tmon); } else { - erts_mtx_unlock(&rmp->lock); - - ASSERT(rmon->u.pid == pid); - erts_ref_to_driver_monitor(ref, &nif_monitor); - nif_pid.pid = pid; - pre_nif_noproc(&msg_env, resource->type->owner, NULL); - resource->type->down(&msg_env.env, resource->data, &nif_pid, &nif_monitor); - post_nif_noproc(&msg_env); + if (brefc > 0) { + ASSERT(is_internal_pid(omon->other.item)); + erts_ref_to_driver_monitor(mdp->ref, &nif_monitor); + nif_pid.pid = omon->other.item; + pre_nif_noproc(&msg_env, resource->type->owner, NULL); + resource->type->down(&msg_env.env, resource->data, &nif_pid, &nif_monitor); + post_nif_noproc(&msg_env); + + erts_bin_release(&bin->binary); + } - erts_bin_release(&bin->binary); + erts_monitor_release_both(mdp); } - erts_destroy_monitor(rmon); } void* enif_alloc_resource(ErlNifResourceType* type, size_t data_sz) @@ -2422,8 +2462,7 @@ void* enif_alloc_resource(ErlNifResourceType* type, size_t data_sz) erts_mtx_init(&resource->monitors->lock, "resource_monitors", NIL, ERTS_LOCK_FLAGS_CATEGORY_GENERIC); resource->monitors->root = NULL; - resource->monitors->pending_failed_fire = 0; - resource->monitors->is_dying = 0; + resource->monitors->refc = 0; resource->monitors->user_data_sz = data_sz; } else { @@ -2438,7 +2477,7 @@ void enif_release_resource(void* obj) ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource); ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == NIF_RESOURCE_DTOR); - ASSERT(!(resource->monitors && resource->monitors->is_dying)); + ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) != 0); #ifdef DEBUG erts_refc_dec(&resource->nif_refc, 0); #endif @@ -2451,7 +2490,7 @@ void enif_keep_resource(void* obj) ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource); ASSERT(ERTS_MAGIC_BIN_DESTRUCTOR(bin) == NIF_RESOURCE_DTOR); - ASSERT(!(resource->monitors && resource->monitors->is_dying)); + ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) != 0); #ifdef DEBUG erts_refc_inc(&resource->nif_refc, 1); #endif @@ -2461,7 +2500,7 @@ void enif_keep_resource(void* obj) Eterm erts_bld_resource_ref(Eterm** hpp, ErlOffHeap* oh, ErtsResource* resource) { ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource); - ASSERT(!(resource->monitors && resource->monitors->is_dying)); + ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) != 0); return erts_mk_magic_ref(hpp, oh, &bin->binary); } @@ -2470,7 +2509,7 @@ ERL_NIF_TERM enif_make_resource(ErlNifEnv* env, void* obj) ErtsResource* resource = DATA_TO_RESOURCE(obj); ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(resource); Eterm* hp = alloc_heap(env, ERTS_MAGIC_REF_THING_SIZE); - ASSERT(!(resource->monitors && resource->monitors->is_dying)); + ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) != 0); return erts_mk_magic_ref(&hp, &MSO(env->proc), &bin->binary); } @@ -2895,6 +2934,44 @@ ERL_NIF_TERM enif_make_new_map(ErlNifEnv* env) return make_flatmap(mp); } +int enif_make_map_from_arrays(ErlNifEnv *env, + ERL_NIF_TERM keys[], + ERL_NIF_TERM values[], + size_t cnt, + ERL_NIF_TERM *map_out) +{ + ErtsHeapFactory factory; + int succeeded; + +#ifdef ERTS_NIF_ASSERT_IN_ENV + size_t index = 0; + + while (index < cnt) { + ASSERT_IN_ENV(env, keys[index], index, "key"); + ASSERT_IN_ENV(env, values[index], index, "value"); + index++; + } +#endif + + flush_env(env); + + erts_factory_proc_prealloc_init(&factory, env->proc, + cnt * 2 + MAP_HEADER_FLATMAP_SZ + 1); + + (*map_out) = erts_map_from_ks_and_vs(&factory, keys, values, cnt); + succeeded = (*map_out) != THE_NON_VALUE; + + if (!succeeded) { + erts_factory_undo(&factory); + } + + erts_factory_close(&factory); + + cache_env(env); + + return succeeded; +} + int enif_make_map_put(ErlNifEnv* env, Eterm map_in, Eterm key, @@ -3150,123 +3227,88 @@ int enif_map_iterator_get_pair(ErlNifEnv *env, int enif_monitor_process(ErlNifEnv* env, void* obj, const ErlNifPid* target_pid, ErlNifMonitor* monitor) { - int scheduler; ErtsResource* rsrc = DATA_TO_RESOURCE(obj); - Process *rp; Eterm tmp[ERTS_REF_THING_SIZE]; Eterm ref; - int retval; + ErtsResourceMonitors *rm; + ErtsMonitorData *mdp; ASSERT(ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(rsrc)->magic_binary.destructor == NIF_RESOURCE_DTOR); - ASSERT(!(rsrc->monitors && rsrc->monitors->is_dying)); + ASSERT(erts_refc_read(&ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(rsrc)->binary.intern.refc, 0) != 0); ASSERT(!rsrc->monitors == !rsrc->type->down); - - if (!rsrc->monitors) { + rm = rsrc->monitors; + if (!rm) { ASSERT(!rsrc->type->down); return -1; } ASSERT(rsrc->type->down); - execution_state(env, NULL, &scheduler); + ref = erts_make_ref_in_buffer(tmp); - if (scheduler > 0) /* Normal scheduler */ - rp = erts_proc_lookup_raw(target_pid->pid); - else - rp = erts_proc_lookup_raw_inc_refc(target_pid->pid); + mdp = erts_monitor_create(ERTS_MON_TYPE_RESOURCE, ref, + (Eterm) rsrc, target_pid->pid, NIL); + erts_mtx_lock(&rm->lock); + ASSERT(!rmon_is_dying(rm)); + erts_monitor_tree_insert(&rm->root, &mdp->origin); + rmon_refc_inc(rm); + erts_mtx_unlock(&rm->lock); - if (!rp) - return 1; + if (!erts_proc_sig_send_monitor(&mdp->target, target_pid->pid)) { + /* Failed to send monitor signal; cleanup... */ +#ifdef DEBUG + ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(rsrc); +#endif - ref = erts_make_ref_in_buffer(tmp); + erts_mtx_lock(&rm->lock); + ASSERT(!rmon_is_dying(rm)); + erts_monitor_tree_delete(&rm->root, &mdp->origin); + rmon_refc_dec(rm); + ASSERT(erts_refc_read(&bin->binary.intern.refc, 1) != 0); + erts_mtx_unlock(&rm->lock); + erts_monitor_release_both(mdp); - erts_mtx_lock(&rsrc->monitors->lock); - erts_proc_lock(rp, ERTS_PROC_LOCK_LINK); - if (ERTS_PSFLG_FREE & erts_atomic32_read_nob(&rp->state)) { - retval = 1; - } - else { - erts_add_monitor(&rsrc->monitors->root, MON_ORIGIN, ref, rp->common.id, NIL); - erts_add_monitor(&ERTS_P_MONITORS(rp), MON_NIF_TARGET, ref, (UWord)rsrc, NIL); - retval = 0; + return 1; } - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - erts_mtx_unlock(&rsrc->monitors->lock); - if (scheduler <= 0) - erts_proc_dec_refc(rp); if (monitor) erts_ref_to_driver_monitor(ref,monitor); - return retval; + return 0; } int enif_demonitor_process(ErlNifEnv* env, void* obj, const ErlNifMonitor* monitor) { - int scheduler; ErtsResource* rsrc = DATA_TO_RESOURCE(obj); #ifdef DEBUG ErtsBinary* bin = ERTS_MAGIC_BIN_FROM_UNALIGNED_DATA(rsrc); #endif - Process *rp; + ErtsResourceMonitors *rm; ErtsMonitor *mon; - ErtsMonitor *rmon = NULL; Eterm ref_heap[ERTS_REF_THING_SIZE]; Eterm ref; - int is_exiting; ASSERT(bin->magic_binary.destructor == NIF_RESOURCE_DTOR); - ASSERT(!(rsrc->monitors && rsrc->monitors->is_dying)); - - execution_state(env, NULL, &scheduler); + ASSERT(erts_refc_read(&bin->binary.intern.refc, 0) != 0); ref = erts_driver_monitor_to_ref(ref_heap, monitor); - erts_mtx_lock(&rsrc->monitors->lock); - mon = erts_remove_monitor(&rsrc->monitors->root, ref); + rm = rsrc->monitors; + erts_mtx_lock(&rm->lock); + ASSERT(!rmon_is_dying(rm)); + mon = erts_monitor_tree_lookup(rm->root, ref); + if (mon) + erts_monitor_tree_delete(&rm->root, mon); + erts_mtx_unlock(&rm->lock); - if (mon == NULL) { - erts_mtx_unlock(&rsrc->monitors->lock); + if (!mon) return 1; - } - ASSERT(mon->type == MON_ORIGIN); - ASSERT(is_internal_pid(mon->u.pid)); + ASSERT(erts_monitor_is_origin(mon)); + ASSERT(is_internal_pid(mon->other.item)); - if (scheduler > 0) /* Normal scheduler */ - rp = erts_proc_lookup(mon->u.pid); - else - rp = erts_proc_lookup_inc_refc(mon->u.pid); - - if (!rp) { - is_exiting = 1; - } - else { - erts_proc_lock(rp, ERTS_PROC_LOCK_LINK); - if (ERTS_PROC_IS_EXITING(rp)) { - is_exiting = 1; - } else { - rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref); - ASSERT(rmon); - is_exiting = 0; - } - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - - if (scheduler <= 0) - erts_proc_dec_refc(rp); - } - if (is_exiting) { - rsrc->monitors->pending_failed_fire++; - } - erts_mtx_unlock(&rsrc->monitors->lock); - - if (rmon) { - ASSERT(rmon->type == MON_NIF_TARGET); - ASSERT(rmon->u.resource == rsrc); - erts_destroy_monitor(rmon); - } - erts_destroy_monitor(mon); + erts_proc_sig_send_demonitor(mon); return 0; } @@ -3726,16 +3768,26 @@ static Eterm mkatom(const char *str) return am_atom_put(str, sys_strlen(str)); } -static struct tainted_module_t +struct tainted_module_t { struct tainted_module_t* next; Eterm module_atom; -}*first_tainted_module = NULL; +}; -static void add_taint(Eterm mod_atom) +erts_atomic_t first_taint; /* struct tainted_module_t* */ + +void erts_add_taint(Eterm mod_atom) { - struct tainted_module_t* t; - for (t=first_tainted_module ; t!=NULL; t=t->next) { +#ifdef ERTS_ENABLE_LOCK_CHECK + extern erts_rwmtx_t erts_driver_list_lock; /* Mutex for driver list */ +#endif + struct tainted_module_t *first, *t; + + ERTS_LC_ASSERT(erts_lc_rwmtx_is_rwlocked(&erts_driver_list_lock) + || erts_thr_progress_is_blocking()); + + first = (struct tainted_module_t*) erts_atomic_read_nob(&first_taint); + for (t=first ; t; t=t->next) { if (t->module_atom == mod_atom) { return; } @@ -3743,22 +3795,24 @@ static void add_taint(Eterm mod_atom) t = erts_alloc_fnf(ERTS_ALC_T_TAINT, sizeof(*t)); if (t != NULL) { t->module_atom = mod_atom; - t->next = first_tainted_module; - first_tainted_module = t; + t->next = first; + erts_atomic_set_nob(&first_taint, (erts_aint_t)t); } } Eterm erts_nif_taints(Process* p) { - struct tainted_module_t* t; + struct tainted_module_t *first, *t; unsigned cnt = 0; Eterm list = NIL; Eterm* hp; - for (t=first_tainted_module ; t!=NULL; t=t->next) { + + first = (struct tainted_module_t*) erts_atomic_read_nob(&first_taint); + for (t=first ; t!=NULL; t=t->next) { cnt++; } hp = HAlloc(p,cnt*2); - for (t=first_tainted_module ; t!=NULL; t=t->next) { + for (t=first ; t!=NULL; t=t->next) { list = CONS(hp, t->module_atom, list); hp += 2; } @@ -3767,9 +3821,11 @@ Eterm erts_nif_taints(Process* p) void erts_print_nif_taints(fmtfn_t to, void* to_arg) { - struct tainted_module_t* t; + struct tainted_module_t *t; const char* delim = ""; - for (t=first_tainted_module ; t!=NULL; t=t->next) { + + t = (struct tainted_module_t*) erts_atomic_read_nob(&first_taint); + for ( ; t; t = t->next) { const Atom* atom = atom_tab(atom_val(t->module_atom)); erts_cbprintf(to,to_arg,"%s%.*s", delim, atom->len, atom->name); delim = ","; @@ -3856,6 +3912,11 @@ static struct erl_module_nif* create_lib(const ErlNifEntry* src) } else { dst->sizeof_ErlNifResourceTypeInit = 0; } + if (AT_LEAST_VERSION(src, 2, 14)) { + dst->min_erts = src->min_erts; + } else { + dst->min_erts = "erts-?"; + } return lib; }; @@ -3878,6 +3939,7 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) ErtsSysDdllError errdesc = ERTS_SYS_DDLL_ERROR_INIT; Eterm ret = am_ok; int veto; + int taint = 1; struct erl_module_nif* lib = NULL; struct erl_module_instance* this_mi; struct erl_module_instance* prev_mi; @@ -3924,10 +3986,15 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) ASSERT(module_p != NULL); mod_atomp = atom_tab(atom_val(mod_atom)); - init_func = erts_static_nif_get_nif_init((char*)mod_atomp->name, mod_atomp->len); - if (init_func != NULL) - handle = init_func; - + { + ErtsStaticNifEntry* sne; + sne = erts_static_nif_get_nif_init((char*)mod_atomp->name, mod_atomp->len); + if (sne != NULL) { + init_func = sne->nif_init; + handle = init_func; + taint = sne->taint; + } + } this_mi = &module_p->curr; prev_mi = &module_p->old; if (in_area(caller, module_p->old.code_hdr, module_p->old.code_length)) { @@ -3964,18 +4031,24 @@ BIF_RETTYPE load_nif_2(BIF_ALIST_2) " function: '%s'", errdesc.str); } - else if ((add_taint(mod_atom), + else if ((taint ? erts_add_taint(mod_atom) : 0, (entry = erts_sys_ddll_call_nif_init(init_func)) == NULL)) { ret = load_nif_error(BIF_P, bad_lib, "Library init-call unsuccessful"); } + else if (entry->major > ERL_NIF_MAJOR_VERSION + || (entry->major == ERL_NIF_MAJOR_VERSION + && entry->minor > ERL_NIF_MINOR_VERSION)) { + char* fmt = "That '%T' NIF library needs %s or newer. Either try to" + " recompile the NIF lib or use a newer erts runtime."; + ret = load_nif_error(BIF_P, bad_lib, fmt, mod_atom, entry->min_erts); + } else if (entry->major < ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD - || (ERL_NIF_MAJOR_VERSION < entry->major - || (ERL_NIF_MAJOR_VERSION == entry->major - && ERL_NIF_MINOR_VERSION < entry->minor)) || (entry->major==2 && entry->minor == 5)) { /* experimental maps */ - ret = load_nif_error(BIF_P, bad_lib, "Library version (%d.%d) not compatible (with %d.%d).", - entry->major, entry->minor, ERL_NIF_MAJOR_VERSION, ERL_NIF_MINOR_VERSION); + char* fmt = "That old NIF library (%d.%d) is not compatible with this " + "erts runtime (%d.%d). Try recompile the NIF lib."; + ret = load_nif_error(BIF_P, bad_lib, fmt, entry->major, entry->minor, + ERL_NIF_MAJOR_VERSION, ERL_NIF_MINOR_VERSION); } else if (AT_LEAST_VERSION(entry, 2, 1) && sys_strcmp(entry->vm_variant, ERL_NIF_VM_VARIANT) != 0) { @@ -4369,7 +4442,7 @@ static void get_string_maybe(ErlNifEnv *env, const ERL_NIF_TERM term, str_bin.size > bufsiz) { *ptr = NULL; } else { - memcpy(buf, (char *) str_bin.data, str_bin.size); + sys_memcpy(buf, (char *) str_bin.data, str_bin.size); buf[str_bin.size] = '\0'; *ptr = buf; } @@ -4386,7 +4459,7 @@ ERL_NIF_TERM erl_nif_user_trace_s1(ErlNifEnv* env, int argc, message_bin.size > MESSAGE_BUFSIZ) { return am_badarg; } - memcpy(messagebuf, (char *) message_bin.data, message_bin.size); + sys_memcpy(messagebuf, (char *) message_bin.data, message_bin.size); messagebuf[message_bin.size] = '\0'; DTRACE1(user_trace_s1, messagebuf); return am_true; diff --git a/erts/emulator/beam/erl_nif.h b/erts/emulator/beam/erl_nif.h index a99b4db705..1906da732b 100644 --- a/erts/emulator/beam/erl_nif.h +++ b/erts/emulator/beam/erl_nif.h @@ -52,10 +52,12 @@ ** 2.11: 19.0 enif_snprintf ** 2.12: 20.0 add enif_select, enif_open_resource_type_x ** 2.13: 20.1 add enif_ioq -** 2.14: 21.0 add enif_ioq_peek_head +** 2.14: 21.0 add enif_ioq_peek_head, enif_(mutex|cond|rwlock|thread)_name +** enif_vfprintf, enif_vsnprintf, enif_make_map_from_arrays */ #define ERL_NIF_MAJOR_VERSION 2 #define ERL_NIF_MINOR_VERSION 14 +#define ERL_NIF_MIN_ERTS_VERSION "erts-10.0 (OTP-21)" /* * The emulator will refuse to load a nif-lib with a major version @@ -70,6 +72,8 @@ #define ERL_NIF_MIN_REQUIRED_MAJOR_VERSION_ON_LOAD 2 #include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> #ifdef __cplusplus extern "C" { @@ -128,6 +132,9 @@ typedef struct enif_entry_t /* Added in 2.12 */ size_t sizeof_ErlNifResourceTypeInit; + + /* Added in 2.14 */ + const char* min_erts; }ErlNifEntry; @@ -350,7 +357,8 @@ ERL_NIF_INIT_DECL(NAME) \ LOAD, RELOAD, UPGRADE, UNLOAD, \ ERL_NIF_VM_VARIANT, \ 1, \ - sizeof(ErlNifResourceTypeInit) \ + sizeof(ErlNifResourceTypeInit), \ + ERL_NIF_MIN_ERTS_VERSION \ }; \ ERL_NIF_INIT_BODY; \ return &entry; \ diff --git a/erts/emulator/beam/erl_nif_api_funcs.h b/erts/emulator/beam/erl_nif_api_funcs.h index 3750fd9b68..61f8fcf6ed 100644 --- a/erts/emulator/beam/erl_nif_api_funcs.h +++ b/erts/emulator/beam/erl_nif_api_funcs.h @@ -92,7 +92,7 @@ ERL_NIF_API_FUNC_DECL(int,enif_thread_join,(ErlNifTid, void **respp)); ERL_NIF_API_FUNC_DECL(void*,enif_realloc,(void* ptr, size_t size)); ERL_NIF_API_FUNC_DECL(void,enif_system_info,(ErlNifSysInfo *sip, size_t si_size)); -ERL_NIF_API_FUNC_DECL(int,enif_fprintf,(void/* FILE* */ *filep, const char *format, ...)); +ERL_NIF_API_FUNC_DECL(int,enif_fprintf,(FILE* filep, const char *format, ...)); ERL_NIF_API_FUNC_DECL(int,enif_inspect_iolist_as_binary,(ErlNifEnv*, ERL_NIF_TERM term, ErlNifBinary* bin)); ERL_NIF_API_FUNC_DECL(ERL_NIF_TERM,enif_make_sub_binary,(ErlNifEnv*, ERL_NIF_TERM bin_term, size_t pos, size_t size)); ERL_NIF_API_FUNC_DECL(int,enif_get_string,(ErlNifEnv*, ERL_NIF_TERM list, char* buf, unsigned len, ErlNifCharEncoding)); @@ -200,6 +200,16 @@ ERL_NIF_API_FUNC_DECL(void,enif_free_iovec,(ErlNifIOVec *iov)); ERL_NIF_API_FUNC_DECL(int,enif_ioq_peek_head,(ErlNifEnv *env, ErlNifIOQueue *q, size_t *size, ERL_NIF_TERM *head)); +ERL_NIF_API_FUNC_DECL(char*,enif_mutex_name,(ErlNifMutex*)); +ERL_NIF_API_FUNC_DECL(char*,enif_cond_name,(ErlNifCond*)); +ERL_NIF_API_FUNC_DECL(char*,enif_rwlock_name,(ErlNifRWLock*)); +ERL_NIF_API_FUNC_DECL(char*,enif_thread_name,(ErlNifTid)); + +ERL_NIF_API_FUNC_DECL(int,enif_vfprintf,(FILE*, const char *fmt, va_list)); +ERL_NIF_API_FUNC_DECL(int,enif_vsnprintf,(char*, size_t, const char *fmt, va_list)); + +ERL_NIF_API_FUNC_DECL(int,enif_make_map_from_arrays,(ErlNifEnv *env, ERL_NIF_TERM keys[], ERL_NIF_TERM values[], size_t cnt, ERL_NIF_TERM *map_out)); + /* ** ADD NEW ENTRIES HERE (before this comment) !!! */ @@ -375,6 +385,13 @@ ERL_NIF_API_FUNC_DECL(int,enif_ioq_peek_head,(ErlNifEnv *env, ErlNifIOQueue *q, # define enif_inspect_iovec ERL_NIF_API_FUNC_MACRO(enif_inspect_iovec) # define enif_free_iovec ERL_NIF_API_FUNC_MACRO(enif_free_iovec) # define enif_ioq_peek_head ERL_NIF_API_FUNC_MACRO(enif_ioq_peek_head) +# define enif_mutex_name ERL_NIF_API_FUNC_MACRO(enif_mutex_name) +# define enif_cond_name ERL_NIF_API_FUNC_MACRO(enif_cond_name) +# define enif_rwlock_name ERL_NIF_API_FUNC_MACRO(enif_rwlock_name) +# define enif_thread_name ERL_NIF_API_FUNC_MACRO(enif_thread_name) +# define enif_vfprintf ERL_NIF_API_FUNC_MACRO(enif_vfprintf) +# define enif_vsnprintf ERL_NIF_API_FUNC_MACRO(enif_vsnprintf) +# define enif_make_map_from_arrays ERL_NIF_API_FUNC_MACRO(enif_make_map_from_arrays) /* ** ADD NEW ENTRIES HERE (before this comment) diff --git a/erts/emulator/beam/erl_node_container_utils.h b/erts/emulator/beam/erl_node_container_utils.h index 6ec428e282..99e938266b 100644 --- a/erts/emulator/beam/erl_node_container_utils.h +++ b/erts/emulator/beam/erl_node_container_utils.h @@ -318,5 +318,3 @@ extern ErtsPTab erts_port; #define is_not_ref(x) (!is_ref(x)) #endif - - diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c index eaf133f5c0..1f147011a8 100644 --- a/erts/emulator/beam/erl_node_tables.c +++ b/erts/emulator/beam/erl_node_tables.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2017. All Rights Reserved. + * Copyright Ericsson AB 2001-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,6 +31,7 @@ #include "dtrace-wrapper.h" #include "erl_binary.h" #include "erl_bif_unique.h" +#include "erl_proc_sig_queue.h" Hash erts_dist_table; Hash erts_node_table; @@ -170,15 +171,11 @@ dist_table_alloc(void *dep_tmpl) dep->cid = NIL; erts_atomic_init_nob(&dep->input_handler, (erts_aint_t) NIL); dep->connection_id = 0; - dep->status = 0; + dep->state = ERTS_DE_STATE_IDLE; dep->flags = 0; dep->version = 0; - erts_mtx_init(&dep->lnk_mtx, "dist_entry_links", sysname, - ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION); - dep->node_links = NULL; - dep->nlinks = NULL; - dep->monitors = NULL; + dep->mld = NULL; erts_mtx_init(&dep->qlock, "dist_entry_out_queue", sysname, ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION); @@ -223,11 +220,9 @@ dist_table_free(void *vdep) DistEntry *dep = (DistEntry *) vdep; ASSERT(de_refc_read(dep, -1) == -1); - ASSERT(dep->status == 0); + ASSERT(dep->state == ERTS_DE_STATE_IDLE); ASSERT(is_nil(dep->cid)); - ASSERT(dep->nlinks == NULL); - ASSERT(dep->node_links == NULL); - ASSERT(dep->monitors == NULL); + ASSERT(dep->mld == NULL); /* Link out */ @@ -250,7 +245,6 @@ dist_table_free(void *vdep) ASSERT(!dep->cache); erts_rwmtx_destroy(&dep->rwmtx); - erts_mtx_destroy(&dep->lnk_mtx); erts_mtx_destroy(&dep->qlock); #ifdef DEBUG @@ -556,14 +550,14 @@ erts_set_dist_entry_not_connected(DistEntry *dep) ASSERT(dep != erts_this_dist_entry); - if (dep->status & ERTS_DE_SFLG_PENDING) { + if (dep->state == ERTS_DE_STATE_PENDING) { ASSERT(is_nil(dep->cid)); ASSERT(erts_no_of_pending_dist_entries > 0); erts_no_of_pending_dist_entries--; head = &erts_pending_dist_entries; } else { - ASSERT(dep->status != 0); + ASSERT(dep->state != ERTS_DE_STATE_IDLE); ASSERT(is_internal_port(dep->cid) || is_internal_pid(dep->cid)); if (dep->flags & DFLAG_PUBLISHED) { ASSERT(erts_no_of_visible_dist_entries > 0); @@ -588,7 +582,7 @@ erts_set_dist_entry_not_connected(DistEntry *dep) if(dep->next) dep->next->prev = dep->prev; - dep->status &= ~(ERTS_DE_SFLG_PENDING | ERTS_DE_SFLG_CONNECTED); + dep->state = ERTS_DE_STATE_EXITING; dep->flags = 0; dep->prev = NULL; dep->cid = NIL; @@ -606,11 +600,13 @@ erts_set_dist_entry_not_connected(DistEntry *dep) void erts_set_dist_entry_pending(DistEntry *dep) { + ErtsMonLnkDist *mld = erts_mon_link_dist_create(dep->sysname); ERTS_LC_ASSERT(erts_lc_is_de_rwlocked(dep)); + erts_rwmtx_rwlock(&erts_dist_table_rwmtx); ASSERT(dep != erts_this_dist_entry); - ASSERT(dep->status == 0); + ASSERT(dep->state == ERTS_DE_STATE_IDLE); ASSERT(is_nil(dep->cid)); if(dep->prev) { @@ -627,10 +623,14 @@ erts_set_dist_entry_pending(DistEntry *dep) erts_no_of_not_connected_dist_entries--; - dep->status = ERTS_DE_SFLG_PENDING; - dep->flags = (DFLAG_DIST_MANDATORY | DFLAG_DIST_HOPEFULLY); + dep->state = ERTS_DE_STATE_PENDING; + dep->flags = (DFLAG_DIST_MANDATORY | DFLAG_DIST_HOPEFULLY | DFLAG_NO_MAGIC); dep->connection_id = (dep->connection_id + 1) & ERTS_DIST_CON_ID_MASK; + ASSERT(!dep->mld); + mld->connection_id = dep->connection_id; + dep->mld = mld; + dep->prev = NULL; dep->next = erts_pending_dist_entries; if(erts_pending_dist_entries) { @@ -647,12 +647,14 @@ erts_set_dist_entry_connected(DistEntry *dep, Eterm cid, Uint flags) { erts_aint32_t set_qflgs; + ASSERT(dep->mld); + ERTS_LC_ASSERT(erts_lc_is_de_rwlocked(dep)); erts_rwmtx_rwlock(&erts_dist_table_rwmtx); ASSERT(dep != erts_this_dist_entry); ASSERT(is_nil(dep->cid)); - ASSERT(dep->status & ERTS_DE_SFLG_PENDING); + ASSERT(dep->state == ERTS_DE_STATE_PENDING); ASSERT(is_internal_port(cid) || is_internal_pid(cid)); if(dep->prev) { @@ -670,8 +672,7 @@ erts_set_dist_entry_connected(DistEntry *dep, Eterm cid, Uint flags) ASSERT(erts_no_of_pending_dist_entries > 0); erts_no_of_pending_dist_entries--; - dep->status &= ~ERTS_DE_SFLG_PENDING; - dep->status |= ERTS_DE_SFLG_CONNECTED; + dep->state = ERTS_DE_STATE_CONNECTED; dep->flags = flags & ~DFLAG_NO_MAGIC; dep->cid = cid; erts_atomic_set_nob(&dep->input_handler, @@ -1050,14 +1051,16 @@ static void erts_lcnt_enable_dist_lock_count(void *dep_raw, void *enable) { if(enable) { erts_lcnt_install_new_lock_info(&dep->rwmtx.lcnt, "dist_entry", dep->sysname, ERTS_LOCK_TYPE_RWMUTEX | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION); - erts_lcnt_install_new_lock_info(&dep->lnk_mtx.lcnt, "dist_entry_links", dep->sysname, - ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION); erts_lcnt_install_new_lock_info(&dep->qlock.lcnt, "dist_entry_out_queue", dep->sysname, ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION); + if (dep->mld) + erts_lcnt_install_new_lock_info(&dep->mld->mtx.lcnt, "dist_entry_links", dep->sysname, + ERTS_LOCK_TYPE_MUTEX | ERTS_LOCK_FLAGS_CATEGORY_DISTRIBUTION); } else { erts_lcnt_uninstall(&dep->rwmtx.lcnt); - erts_lcnt_uninstall(&dep->lnk_mtx.lcnt); erts_lcnt_uninstall(&dep->qlock.lcnt); + if (dep->mld) + erts_lcnt_uninstall(&dep->mld->mtx.lcnt); } } @@ -1100,6 +1103,7 @@ static Eterm AM_system; static Eterm AM_timer; static Eterm AM_delayed_delete_timer; static Eterm AM_thread_progress_delete_timer; +static Eterm AM_signal; static void setup_reference_table(void); static Eterm reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp); @@ -1121,6 +1125,7 @@ typedef struct node_referrer_ { int bin_ref; int timer_ref; int system_ref; + int signal_ref; Eterm id; Uint id_heap[ID_HEAP_SIZE]; ErlOffHeap off_heap; @@ -1138,6 +1143,7 @@ typedef struct dist_referrer_ { int node_ref; int ctrl_ref; int system_ref; + int signal_ref; Eterm id; Uint creation; Uint id_heap[ID_HEAP_SIZE]; @@ -1191,6 +1197,7 @@ erts_get_node_and_dist_references(struct process *proc) INIT_AM(system); INIT_AM(delayed_delete_timer); INIT_AM(thread_progress_delete_timer); + INIT_AM(signal); references_atoms_need_init = 0; } @@ -1227,6 +1234,7 @@ erts_get_node_and_dist_references(struct process *proc) #define MONITOR_REF 7 #define TIMER_REF 8 #define SYSTEM_REF 9 +#define SIGNAL_REF 10 #define INC_TAB_SZ 10 @@ -1261,6 +1269,7 @@ insert_dist_referrer(ReferredDist *referred_dist, drp->node_ref = 0; drp->ctrl_ref = 0; drp->system_ref = 0; + drp->signal_ref = 0; } switch (type) { @@ -1269,6 +1278,7 @@ insert_dist_referrer(ReferredDist *referred_dist, case HEAP_REF: drp->heap_ref++; break; case ETS_REF: drp->ets_ref++; break; case SYSTEM_REF: drp->system_ref++; break; + case SIGNAL_REF: drp->signal_ref++; break; default: ASSERT(0); } } @@ -1322,6 +1332,7 @@ insert_node_referrer(ReferredNode *referred_node, int type, Eterm id) nrp->bin_ref = 0; nrp->timer_ref = 0; nrp->system_ref = 0; + nrp->signal_ref = 0; } switch (type) { @@ -1332,6 +1343,7 @@ insert_node_referrer(ReferredNode *referred_node, int type, Eterm id) case MONITOR_REF: nrp->monitor_ref++; break; case TIMER_REF: nrp->timer_ref++; break; case SYSTEM_REF: nrp->system_ref++; break; + case SIGNAL_REF: nrp->signal_ref++; break; default: ASSERT(0); } } @@ -1439,49 +1451,145 @@ insert_offheap(ErlOffHeap *oh, int type, Eterm id) } } -static void doit_insert_monitor(ErtsMonitor *monitor, void *p) +static void insert_monitor_data(ErtsMonitor *mon, int type, Eterm id) { - Eterm *idp = p; - if(monitor->type != MON_NIF_TARGET && is_external(monitor->u.pid)) - insert_node(external_thing_ptr(monitor->u.pid)->node, MONITOR_REF, *idp); - if(is_external(monitor->ref)) - insert_node(external_thing_ptr(monitor->ref)->node, MONITOR_REF, *idp); + ErtsMonitorData *mdp = erts_monitor_to_data(mon); + if ((mdp->origin.flags & (ERTS_ML_FLG_DBG_VISITED + | ERTS_ML_FLG_EXTENDED)) == ERTS_ML_FLG_EXTENDED) { + if (mon->type != ERTS_MON_TYPE_NODE) { + ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp; + ASSERT(mon->flags & ERTS_ML_FLG_EXTENDED); + if (mdep->uptr.ohhp) { + ErlOffHeap oh; + ERTS_INIT_OFF_HEAP(&oh); + oh.first = mdep->uptr.ohhp; + insert_offheap(&oh, type, id); + } + } + } + mdp->origin.flags |= ERTS_ML_FLG_DBG_VISITED; } -static void doit_insert_link(ErtsLink *lnk, void *p) +static void insert_monitor(ErtsMonitor *mon, void *idp) { - Eterm *idp = p; - if(is_external(lnk->pid)) - insert_node(external_thing_ptr(lnk->pid)->node, LINK_REF, - *idp); + Eterm id = *((Eterm *) idp); + insert_monitor_data(mon, MONITOR_REF, id); } +static void clear_visited_monitor(ErtsMonitor *mon, void *p) +{ + ErtsMonitorData *mdp = erts_monitor_to_data(mon); + mdp->origin.flags &= ~ERTS_ML_FLG_DBG_VISITED; +} static void -insert_monitors(ErtsMonitor *monitors, Eterm id) +insert_p_monitors(ErtsPTabElementCommon *p) { - erts_doforall_monitors(monitors,&doit_insert_monitor,&id); + Eterm id = p->id; + erts_monitor_tree_foreach(p->u.alive.monitors, + insert_monitor, + (void *) &id); + erts_monitor_list_foreach(p->u.alive.lt_monitors, + insert_monitor, + (void *) &id); +} + +static void +insert_dist_monitors(DistEntry *dep) +{ + if (dep->mld) { + erts_monitor_list_foreach(dep->mld->monitors, + insert_monitor, + (void *) &dep->sysname); + erts_monitor_tree_foreach(dep->mld->orig_name_monitors, + insert_monitor, + (void *) &dep->sysname); + } } static void -insert_links(ErtsLink *lnk, Eterm id) +clear_visited_p_monitors(ErtsPTabElementCommon *p) { - erts_doforall_links(lnk,&doit_insert_link,&id); + erts_monitor_tree_foreach(p->u.alive.monitors, + clear_visited_monitor, + NULL); + erts_monitor_list_foreach(p->u.alive.lt_monitors, + clear_visited_monitor, + NULL); } -static void doit_insert_link2(ErtsLink *lnk, void *p) +static void +clear_visited_dist_monitors(DistEntry *dep) +{ + if (dep->mld) { + erts_monitor_list_foreach(dep->mld->monitors, + clear_visited_monitor, + NULL); + erts_monitor_tree_foreach(dep->mld->orig_name_monitors, + clear_visited_monitor, + NULL); + } +} + +static void insert_link_data(ErtsLink *lnk, int type, Eterm id) +{ + ErtsLinkData *ldp = erts_link_to_data(lnk); + if ((ldp->a.flags & (ERTS_ML_FLG_DBG_VISITED + | ERTS_ML_FLG_EXTENDED)) == ERTS_ML_FLG_EXTENDED) { + ErtsLinkDataExtended *ldep = (ErtsLinkDataExtended *) ldp; + if (ldep->ohhp) { + ErlOffHeap oh; + ERTS_INIT_OFF_HEAP(&oh); + oh.first = ldep->ohhp; + insert_offheap(&oh, type, id); + } + } + ldp->a.flags |= ERTS_ML_FLG_DBG_VISITED; +} + +static void insert_link(ErtsLink *lnk, void *idp) { - Eterm *idp = p; - if(is_external(lnk->pid)) - insert_node(external_thing_ptr(lnk->pid)->node, LINK_REF, - *idp); - insert_links(ERTS_LINK_ROOT(lnk), *idp); + Eterm id = *((Eterm *) idp); + insert_link_data(lnk, LINK_REF, id); +} + +static void clear_visited_link(ErtsLink *lnk, void *p) +{ + ErtsLinkData *ldp = erts_link_to_data(lnk); + ldp->a.flags &= ~ERTS_ML_FLG_DBG_VISITED; } static void -insert_links2(ErtsLink *lnk, Eterm id) +insert_p_links(ErtsPTabElementCommon *p) { - erts_doforall_links(lnk,&doit_insert_link2,&id); + Eterm id = p->id; + erts_link_tree_foreach(p->u.alive.links, insert_link, (void *) &id); +} + +static void +insert_dist_links(DistEntry *dep) +{ + if (dep->mld) + erts_link_list_foreach(dep->mld->links, + insert_link, + (void *) &dep->sysname); +} + +static void +clear_visited_p_links(ErtsPTabElementCommon *p) +{ + erts_link_tree_foreach(p->u.alive.links, + clear_visited_link, + NULL); +} + +static void +clear_visited_dist_links(DistEntry *dep) +{ + if (dep->mld) + erts_link_list_foreach(dep->mld->links, + clear_visited_link, + NULL); } static void @@ -1577,6 +1685,60 @@ insert_delayed_delete_dist_entry(void *state, } static void +insert_message(ErtsMessage *msg, int type, Process *proc) +{ + ErlHeapFragment *heap_frag = NULL; + + ASSERT(ERTS_SIG_IS_MSG(msg)); + if (msg->data.attached) { + if (msg->data.attached == ERTS_MSG_COMBINED_HFRAG) + heap_frag = &msg->hfrag; + else if (ERTS_SIG_IS_INTERNAL_MSG(msg)) + heap_frag = msg->data.heap_frag; + else { + if (msg->data.dist_ext->dep) + insert_dist_entry(msg->data.dist_ext->dep, + type, proc->common.id, 0); + if (is_not_nil(ERL_MESSAGE_TOKEN(msg))) + heap_frag = erts_dist_ext_trailer(msg->data.dist_ext); + } + } + while (heap_frag) { + insert_offheap(&(heap_frag->off_heap), + type, + proc->common.id); + heap_frag = heap_frag->next; + } +} + +static void +insert_sig_msg(ErtsMessage *msg, void *arg) +{ + insert_message(msg, SIGNAL_REF, (Process *) arg); +} + +static void +insert_sig_offheap(ErlOffHeap *ohp, void *arg) +{ + Process *proc = arg; + insert_offheap(ohp, SIGNAL_REF, proc->common.id); +} + +static void +insert_sig_monitor(ErtsMonitor *mon, void *arg) +{ + Process *proc = arg; + insert_monitor_data(mon, SIGNAL_REF, proc->common.id); +} + +static void +insert_sig_link(ErtsLink *lnk, void *arg) +{ + Process *proc = arg; + insert_link_data(lnk, SIGNAL_REF, proc->common.id); +} + +static void setup_reference_table(void) { ErlHeapFragment *hfp; @@ -1631,10 +1793,7 @@ setup_reference_table(void) Process *proc = erts_pix2proc(i); if (proc) { int mli; - ErtsMessage *msg_list[] = { - proc->msg.first, - proc->msg_inq.first, - proc->msg_frag}; + ErtsMessage *msg_list[] = {proc->msg_frag}; /* Insert Heap */ insert_offheap(&(proc->off_heap), @@ -1649,34 +1808,24 @@ setup_reference_table(void) /* Insert msg buffers */ for (mli = 0; mli < sizeof(msg_list)/sizeof(msg_list[0]); mli++) { ErtsMessage *msg; - for (msg = msg_list[mli]; msg; msg = msg->next) { - ErlHeapFragment *heap_frag = NULL; - if (msg->data.attached) { - if (msg->data.attached == ERTS_MSG_COMBINED_HFRAG) - heap_frag = &msg->hfrag; - else if (is_value(ERL_MESSAGE_TERM(msg))) - heap_frag = msg->data.heap_frag; - else { - if (msg->data.dist_ext->dep) - insert_dist_entry(msg->data.dist_ext->dep, - HEAP_REF, proc->common.id, 0); - if (is_not_nil(ERL_MESSAGE_TOKEN(msg))) - heap_frag = erts_dist_ext_trailer(msg->data.dist_ext); - } - } - while (heap_frag) { - insert_offheap(&(heap_frag->off_heap), - HEAP_REF, - proc->common.id); - heap_frag = heap_frag->next; - } - } + for (msg = msg_list[mli]; msg; msg = msg->next) + insert_message(msg, HEAP_REF, proc); } + + /* Insert signal queue */ + erts_proc_sig_debug_foreach_sig(proc, + insert_sig_msg, + insert_sig_offheap, + insert_sig_monitor, + insert_sig_link, + (void *) proc); + /* Insert links */ - if (ERTS_P_LINKS(proc)) - insert_links(ERTS_P_LINKS(proc), proc->common.id); - if (ERTS_P_MONITORS(proc)) - insert_monitors(ERTS_P_MONITORS(proc), proc->common.id); + insert_p_links(&proc->common); + + /* Insert monitors */ + insert_p_monitors(&proc->common); + { DistEntry *dep = ERTS_PROC_GET_DIST_ENTRY(proc); if (dep) @@ -1706,11 +1855,9 @@ setup_reference_table(void) continue; /* Insert links */ - if (ERTS_P_LINKS(prt)) - insert_links(ERTS_P_LINKS(prt), prt->common.id); + insert_p_links(&prt->common); /* Insert monitors */ - if (ERTS_P_MONITORS(prt)) - insert_monitors(ERTS_P_MONITORS(prt), prt->common.id); + insert_p_monitors(&prt->common); /* Insert port data */ ohp = erts_port_data_offheap(prt); if (ohp) @@ -1759,41 +1906,25 @@ setup_reference_table(void) /* Insert all dist links */ for(dep = erts_visible_dist_entries; dep; dep = dep->next) { - if(dep->nlinks) - insert_links2(dep->nlinks, dep->sysname); - if(dep->node_links) - insert_links(dep->node_links, dep->sysname); - if(dep->monitors) - insert_monitors(dep->monitors, dep->sysname); + insert_dist_links(dep); + insert_dist_monitors(dep); } for(dep = erts_hidden_dist_entries; dep; dep = dep->next) { - if(dep->nlinks) - insert_links2(dep->nlinks, dep->sysname); - if(dep->node_links) - insert_links(dep->node_links, dep->sysname); - if(dep->monitors) - insert_monitors(dep->monitors, dep->sysname); + insert_dist_links(dep); + insert_dist_monitors(dep); } for(dep = erts_pending_dist_entries; dep; dep = dep->next) { - if(dep->nlinks) - insert_links2(dep->nlinks, dep->sysname); - if(dep->node_links) - insert_links(dep->node_links, dep->sysname); - if(dep->monitors) - insert_monitors(dep->monitors, dep->sysname); + insert_dist_links(dep); + insert_dist_monitors(dep); } /* Not connected dist entries should not have any links, but inspect them anyway */ for(dep = erts_not_connected_dist_entries; dep; dep = dep->next) { - if(dep->nlinks) - insert_links2(dep->nlinks, dep->sysname); - if(dep->node_links) - insert_links(dep->node_links, dep->sysname); - if(dep->monitors) - insert_monitors(dep->monitors, dep->sysname); + insert_dist_links(dep); + insert_dist_monitors(dep); } /* Insert all ets tables */ @@ -1878,6 +2009,10 @@ reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp) tup = MK_2TUP(AM_system, MK_UINT(nrp->system_ref)); nrl = MK_CONS(tup, nrl); } + if(nrp->signal_ref) { + tup = MK_2TUP(AM_signal, MK_UINT(nrp->signal_ref)); + nrl = MK_CONS(tup, nrl); + } nrid = nrp->id; if (!IS_CONST(nrp->id)) { @@ -1901,24 +2036,25 @@ reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp) } else if(is_internal_port(nrid)) { ASSERT(!nrp->heap_ref && !nrp->ets_ref && !nrp->bin_ref - && !nrp->timer_ref && !nrp->system_ref); + && !nrp->timer_ref && !nrp->system_ref && !nrp->signal_ref); tup = MK_2TUP(AM_port, nrid); } else if(nrp->ets_ref) { ASSERT(!nrp->heap_ref && !nrp->link_ref && !nrp->monitor_ref && !nrp->bin_ref - && !nrp->timer_ref && !nrp->system_ref); + && !nrp->timer_ref && !nrp->system_ref && !nrp->signal_ref); tup = MK_2TUP(AM_ets, nrid); } else if(nrp->bin_ref) { ASSERT(is_small(nrid) || is_big(nrid)); ASSERT(!nrp->heap_ref && !nrp->ets_ref && !nrp->link_ref && !nrp->monitor_ref && !nrp->timer_ref - && !nrp->system_ref); + && !nrp->system_ref && !nrp->signal_ref); tup = MK_2TUP(AM_match_spec, nrid); } else { - ASSERT(!nrp->heap_ref && !nrp->ets_ref && !nrp->bin_ref); + ASSERT(!nrp->heap_ref && !nrp->ets_ref && !nrp->bin_ref + && !nrp->signal_ref); ASSERT(is_atom(nrid)); tup = MK_2TUP(AM_dist, nrid); } @@ -1962,30 +2098,36 @@ reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp) tup = MK_2TUP(AM_system, MK_UINT(drp->system_ref)); drl = MK_CONS(tup, drl); } + if(drp->signal_ref) { + tup = MK_2TUP(AM_signal, MK_UINT(drp->signal_ref)); + drl = MK_CONS(tup, drl); + } if (is_internal_pid(drp->id)) { ASSERT(!drp->node_ref); tup = MK_2TUP(AM_process, drp->id); } else if(is_internal_port(drp->id)) { - ASSERT(drp->ctrl_ref && !drp->node_ref); + ASSERT(drp->ctrl_ref && !drp->node_ref && !drp->signal_ref); tup = MK_2TUP(AM_port, drp->id); } else if (is_tuple(drp->id)) { Eterm *t; ASSERT(drp->system_ref && !drp->node_ref - && !drp->ctrl_ref && !drp->heap_ref && !drp->ets_ref); + && !drp->ctrl_ref && !drp->heap_ref && !drp->ets_ref + && !drp->signal_ref); t = tuple_val(drp->id); ASSERT(2 == arityval(t[0])); tup = MK_2TUP(t[1], t[2]); } else if (drp->ets_ref) { ASSERT(!drp->heap_ref && !drp->node_ref && - !drp->ctrl_ref && !drp->system_ref); + !drp->ctrl_ref && !drp->system_ref + && !drp->signal_ref); tup = MK_2TUP(AM_ets, drp->id); } - else { - ASSERT(!drp->ctrl_ref && drp->node_ref); + else { + ASSERT(!drp->ctrl_ref && drp->node_ref && !drp->signal_ref); ASSERT(is_atom(drp->id)); tup = MK_2TUP(drp->id, MK_UINT(drp->creation)); tup = MK_2TUP(AM_node, tup); @@ -2020,10 +2162,21 @@ reference_table_term(Uint **hpp, ErlOffHeap *ohp, Uint *szp) } +static void noop_sig_msg(ErtsMessage *msg, void *arg) +{ + +} + +static void noop_sig_offheap(ErlOffHeap *oh, void *arg) +{ + +} + static void delete_reference_table(void) { - Uint i; + DistEntry *dep; + int i, max; for(i = 0; i < no_referred_nodes; i++) { NodeReferrer *nrp; NodeReferrer *tnrp; @@ -2055,6 +2208,60 @@ delete_reference_table(void) inserted_bins = inserted_bins->next; erts_free(ERTS_ALC_T_NC_TMP, (void *)ib); } + + /* Cleanup... */ + + max = erts_ptab_max(&erts_proc); + for (i = 0; i < max; i++) { + Process *proc = erts_pix2proc(i); + if (proc) { + clear_visited_p_links(&proc->common); + clear_visited_p_monitors(&proc->common); + erts_proc_sig_debug_foreach_sig(proc, + noop_sig_msg, + noop_sig_offheap, + clear_visited_monitor, + clear_visited_link, + (void *) proc); + } + } + + max = erts_ptab_max(&erts_port); + for (i = 0; i < max; i++) { + erts_aint32_t state; + Port *prt; + + prt = erts_pix2port(i); + if (!prt) + continue; + + state = erts_atomic32_read_nob(&prt->state); + if (state & ERTS_PORT_SFLGS_DEAD) + continue; + + clear_visited_p_links(&prt->common); + clear_visited_p_monitors(&prt->common); + } + + for(dep = erts_visible_dist_entries; dep; dep = dep->next) { + clear_visited_dist_links(dep); + clear_visited_dist_monitors(dep); + } + + for(dep = erts_hidden_dist_entries; dep; dep = dep->next) { + clear_visited_dist_links(dep); + clear_visited_dist_monitors(dep); + } + + for(dep = erts_pending_dist_entries; dep; dep = dep->next) { + clear_visited_dist_links(dep); + clear_visited_dist_monitors(dep); + } + + for(dep = erts_not_connected_dist_entries; dep; dep = dep->next) { + clear_visited_dist_links(dep); + clear_visited_dist_monitors(dep); + } } void diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h index 8d29c83e15..9a792b10b1 100644 --- a/erts/emulator/beam/erl_node_tables.h +++ b/erts/emulator/beam/erl_node_tables.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2017. All Rights Reserved. + * Copyright Ericsson AB 2001-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,17 @@ * %CopyrightEnd% */ -#ifndef ERL_NODE_TABLES_H__ +#ifndef ERL_NODE_TABLES_BASIC__ +#define ERL_NODE_TABLES_BASIC__ + +typedef struct dist_entry_ DistEntry; +typedef struct ErtsDistOutputBuf_ ErtsDistOutputBuf; +void erts_ref_dist_entry(DistEntry *dep); +void erts_deref_dist_entry(DistEntry *dep); + +#endif + +#if !defined(ERL_NODE_TABLES_BASIC_ONLY) && !defined(ERL_NODE_TABLES_H__) #define ERL_NODE_TABLES_H__ /* @@ -42,14 +52,14 @@ #include "sys.h" #include "hash.h" #include "erl_alloc.h" -#include "erl_process.h" -#include "erl_monitors.h" #define ERTS_PORT_TASK_ONLY_BASIC_TYPES__ #include "erl_port_task.h" #undef ERTS_PORT_TASK_ONLY_BASIC_TYPES__ +#include "erl_process.h" #define ERTS_BINARY_TYPES_ONLY__ #include "erl_binary.h" #undef ERTS_BINARY_TYPES_ONLY__ +#include "erl_monitor_link.h" #define ERTS_NODE_TAB_DELAY_GC_DEFAULT (60) #define ERTS_NODE_TAB_DELAY_GC_MAX (100*1000*1000) @@ -57,9 +67,12 @@ #define ERST_INTERNAL_CHANNEL_NO 0 -#define ERTS_DE_SFLG_PENDING (((Uint32) 1) << 0) -#define ERTS_DE_SFLG_CONNECTED (((Uint32) 1) << 1) -#define ERTS_DE_SFLG_EXITING (((Uint32) 1) << 2) +enum dist_entry_state { + ERTS_DE_STATE_IDLE, + ERTS_DE_STATE_PENDING, + ERTS_DE_STATE_CONNECTED, + ERTS_DE_STATE_EXITING +}; #define ERTS_DE_QFLG_BUSY (((erts_aint32_t) 1) << 0) #define ERTS_DE_QFLG_EXIT (((erts_aint32_t) 1) << 1) @@ -79,13 +92,13 @@ #define ERTS_DIST_OUTPUT_BUF_DBG_PATTERN ((Uint) 0xf713f713) #endif -typedef struct ErtsDistOutputBuf_ ErtsDistOutputBuf; struct ErtsDistOutputBuf_ { #ifdef DEBUG Uint dbg_pattern; byte *alloc_endp; #endif ErtsDistOutputBuf *next; + Uint hopefull_flags; byte *extp; byte *ext_endp; byte *msg_start; @@ -109,7 +122,7 @@ struct ErtsProcList_; * unlock mutexes with higher numbers before mutexes with higher numbers. */ -typedef struct dist_entry_ { +struct dist_entry_ { HashBucket hash_bucket; /* Hash bucket */ struct dist_entry_ *next; /* Next entry in dist_table (not sorted) */ struct dist_entry_ *prev; /* Previous entry in dist_table (not sorted) */ @@ -121,23 +134,12 @@ typedef struct dist_entry_ { Eterm cid; /* connection handler (pid or port), NIL == free */ Uint32 connection_id; /* Connection id incremented on connect */ - Uint32 status; /* Slot status, like exiting reserved etc */ + enum dist_entry_state state; Uint32 flags; /* Distribution flags, like hidden, atom cache etc. */ unsigned long version; /* Protocol version */ - - erts_mtx_t lnk_mtx; /* Protects node_links, nlinks, and - monitors. */ - ErtsLink *node_links; /* In a dist entry, node links are kept - in a separate tree, while they are - colocted with the ordinary link tree - for processes. It's not due to confusion, - it's because the link tree for the dist - entry is in two levels, see erl_monitors.h - */ - ErtsLink *nlinks; /* Link tree with subtrees */ - ErtsMonitor *monitors; /* Monitor tree */ + ErtsMonLnkDist *mld; /* Monitors and links */ erts_mtx_t qlock; /* Protects qflgs and out_queue */ erts_atomic32_t qflgs; @@ -159,7 +161,7 @@ typedef struct dist_entry_ { ErtsThrPrgrLaterOp later_op; struct transcode_context* transcode_ctx; -} DistEntry; +}; typedef struct erl_node_ { HashBucket hash_bucket; /* Hash bucket */ @@ -215,16 +217,12 @@ int erts_dist_entry_destructor(Binary *bin); DistEntry *erts_dhandle_to_dist_entry(Eterm dhandle); Eterm erts_build_dhandle(Eterm **hpp, ErlOffHeap*, DistEntry*); Eterm erts_make_dhandle(Process *c_p, DistEntry *dep); -void erts_ref_dist_entry(DistEntry *dep); -void erts_deref_dist_entry(DistEntry *dep); ERTS_GLB_INLINE void erts_deref_node_entry(ErlNode *np); ERTS_GLB_INLINE void erts_de_rlock(DistEntry *dep); ERTS_GLB_INLINE void erts_de_runlock(DistEntry *dep); ERTS_GLB_INLINE void erts_de_rwlock(DistEntry *dep); ERTS_GLB_INLINE void erts_de_rwunlock(DistEntry *dep); -ERTS_GLB_INLINE void erts_de_links_lock(DistEntry *dep); -ERTS_GLB_INLINE void erts_de_links_unlock(DistEntry *dep); #if ERTS_GLB_INLINE_INCL_FUNC_DEF @@ -260,18 +258,6 @@ erts_de_rwunlock(DistEntry *dep) erts_rwmtx_rwunlock(&dep->rwmtx); } -ERTS_GLB_INLINE void -erts_de_links_lock(DistEntry *dep) -{ - erts_mtx_lock(&dep->lnk_mtx); -} - -ERTS_GLB_INLINE void -erts_de_links_unlock(DistEntry *dep) -{ - erts_mtx_unlock(&dep->lnk_mtx); -} - #endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ void erts_debug_test_node_tab_delayed_delete(Sint64 millisecs); diff --git a/erts/emulator/beam/erl_port.h b/erts/emulator/beam/erl_port.h index 0d148ee048..2a98a6f00b 100644 --- a/erts/emulator/beam/erl_port.h +++ b/erts/emulator/beam/erl_port.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012-2017. All Rights Reserved. + * Copyright Ericsson AB 2012-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -373,7 +373,7 @@ Eterm erts_request_io_bytes(Process *c_p); void print_port_info(Port *, fmtfn_t, void *); void erts_port_free(Port *); -void erts_fire_port_monitor(Port *prt, Eterm ref); +void erts_fire_port_monitor(Port *prt, ErtsMonitor *tmon); int erts_port_handle_xports(Port *); #if defined(ERTS_ENABLE_LOCK_CHECK) @@ -887,20 +887,20 @@ struct ErtsProc2PortSigData_ { Eterm item; } info; struct { - Eterm port; - Eterm to; + Eterm port_id; + ErtsLink *lnk; } link; struct { - Eterm from; + Eterm port_id; + ErtsLink *lnk; } unlink; struct { - Eterm origin; /* who receives monitor event, pid */ - Eterm name; /* either name for named monitor, or port id */ + Eterm port_id; + ErtsMonitor *mon; } 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 */ + Eterm port_id; + ErtsMonitor *mon; } demonitor; } u; } ; @@ -946,7 +946,6 @@ typedef int (*ErtsProc2PortSigCallback)(Port *, typedef enum { ERTS_PORT_OP_BADARG, - ERTS_PORT_OP_CALLER_EXIT, ERTS_PORT_OP_BUSY, ERTS_PORT_OP_BUSY_SCHEDULED, ERTS_PORT_OP_SCHEDULED, @@ -982,32 +981,13 @@ ErtsPortOpResult erts_port_command(Process *, int, Port *, Eterm, Eterm *); ErtsPortOpResult erts_port_output(Process *, int, Port *, Eterm, Eterm, Eterm *); ErtsPortOpResult erts_port_exit(Process *, int, Port *, Eterm, Eterm, Eterm *); ErtsPortOpResult erts_port_connect(Process *, int, Port *, Eterm, Eterm, Eterm *); -ErtsPortOpResult erts_port_link(Process *, Port *, Eterm, Eterm *); -ErtsPortOpResult erts_port_unlink(Process *, Port *, Eterm, Eterm *); +ErtsPortOpResult erts_port_link(Process *, Port *, ErtsLink *, Eterm *); +ErtsPortOpResult erts_port_unlink(Process *, Port *, ErtsLink *, Eterm *); 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); +ErtsPortOpResult erts_port_monitor(Process *, Port *, ErtsMonitor *); +ErtsPortOpResult erts_port_demonitor(Process *, Port *, ErtsMonitor *); /* defined in erl_bif_port.c */ Port *erts_sig_lookup_port(Process *c_p, Eterm id_or_name); diff --git a/erts/emulator/beam/erl_posix_str.c b/erts/emulator/beam/erl_posix_str.c index deb7e3e173..7b3e640d3f 100644 --- a/erts/emulator/beam/erl_posix_str.c +++ b/erts/emulator/beam/erl_posix_str.c @@ -156,6 +156,9 @@ erl_errno_id(error) #ifdef EFAULT case EFAULT: return "efault"; #endif +#ifdef EFTYPE + case EFTYPE: return "eftype"; +#endif #ifdef EFBIG case EFBIG: return "efbig"; #endif @@ -351,6 +354,9 @@ erl_errno_id(error) #if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP)) case EOPNOTSUPP: return "eopnotsupp"; #endif +#ifdef EOVERFLOW + case EOVERFLOW: return "eoverflow"; +#endif #ifdef EPERM case EPERM: return "eperm"; #endif diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c new file mode 100644 index 0000000000..1ba0b789ec --- /dev/null +++ b/erts/emulator/beam/erl_proc_sig_queue.c @@ -0,0 +1,3927 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +/* + * Description: Process signal queue implementation. + * + * Author: Rickard Green + */ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "sys.h" +#include "global.h" +#include "dist.h" +#include "erl_process.h" +#include "erl_port_task.h" +#include "erl_trace.h" +#include "beam_bp.h" +#include "big.h" +#include "erl_gc.h" +#include "bif.h" +#include "erl_proc_sig_queue.h" +#include "dtrace-wrapper.h" + +#define ERTS_SIG_REDS_CNT_FACTOR 4 +#define ERTS_PROC_SIG_TRACE_COUNT_LIMIT 200 + +/* + * Note that not all signal are handled using this functionality! + */ + +#define ERTS_SIG_Q_OP_MAX 10 + +#define ERTS_SIG_Q_OP_EXIT 0 +#define ERTS_SIG_Q_OP_EXIT_LINKED 1 +#define ERTS_SIG_Q_OP_MONITOR_DOWN 2 +#define ERTS_SIG_Q_OP_MONITOR 3 +#define ERTS_SIG_Q_OP_DEMONITOR 4 +#define ERTS_SIG_Q_OP_LINK 5 +#define ERTS_SIG_Q_OP_UNLINK 6 +#define ERTS_SIG_Q_OP_GROUP_LEADER 7 +#define ERTS_SIG_Q_OP_TRACE_CHANGE_STATE 8 +#define ERTS_SIG_Q_OP_PERSISTENT_MON_MSG 9 +#define ERTS_SIG_Q_OP_IS_ALIVE ERTS_SIG_Q_OP_MAX + +#define ERTS_SIG_Q_TYPE_MAX (ERTS_MON_LNK_TYPE_MAX + 5) + +#define ERTS_SIG_Q_TYPE_UNDEFINED \ + (ERTS_MON_LNK_TYPE_MAX + 1) +#define ERTS_SIG_Q_TYPE_DIST_LINK \ + (ERTS_MON_LNK_TYPE_MAX + 2) +#define ERTS_SIG_Q_TYPE_GEN_EXIT \ + (ERTS_MON_LNK_TYPE_MAX + 3) +#define ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR \ + (ERTS_MON_LNK_TYPE_MAX + 4) +#define ERTS_SIG_Q_TYPE_ADJUST_TRACE_INFO \ + ERTS_SIG_Q_TYPE_MAX + + +#define ERTS_SIG_Q_OP_BITS 8 +#define ERTS_SIG_Q_OP_SHIFT 0 +#define ERTS_SIG_Q_OP_MASK ((1 << ERTS_SIG_Q_OP_BITS) - 1) + +#define ERTS_SIG_Q_TYPE_BITS 8 +#define ERTS_SIG_Q_TYPE_SHIFT ERTS_SIG_Q_OP_BITS +#define ERTS_SIG_Q_TYPE_MASK ((1 << ERTS_SIG_Q_TYPE_BITS) - 1) + +#define ERTS_SIG_Q_NON_X_BITS__ (_HEADER_ARITY_OFFS \ + + ERTS_SIG_Q_OP_BITS \ + + ERTS_SIG_Q_TYPE_BITS) + +#define ERTS_SIG_Q_XTRA_BITS (32 - ERTS_SIG_Q_NON_X_BITS__) +#define ERTS_SIG_Q_XTRA_SHIFT (ERTS_SIG_Q_OP_BITS \ + + ERTS_SIG_Q_TYPE_BITS) +#define ERTS_SIG_Q_XTRA_MASK ((1 << ERTS_SIG_Q_XTRA_BITS) - 1) + +#define ERTS_PROC_SIG_OP(Tag) \ + ((int) (_unchecked_thing_arityval((Tag)) \ + >> ERTS_SIG_Q_OP_SHIFT) & ERTS_SIG_Q_OP_MASK) + +#define ERTS_PROC_SIG_TYPE(Tag) \ + ((Uint16) (_unchecked_thing_arityval((Tag)) \ + >> ERTS_SIG_Q_TYPE_SHIFT) & ERTS_SIG_Q_TYPE_MASK) + +#define ERTS_PROC_SIG_XTRA(Tag) \ + ((Uint32) (_unchecked_thing_arityval((Tag)) \ + >> ERTS_SIG_Q_XTRA_SHIFT) & ERTS_SIG_Q_XTRA_MASK) + +#define ERTS_PROC_SIG_MAKE_TAG(Op, Type, Xtra) \ + (ASSERT(0 <= (Xtra) && (Xtra) <= ERTS_SIG_Q_XTRA_MASK), \ + _make_header((((Type) & ERTS_SIG_Q_TYPE_MASK) \ + << ERTS_SIG_Q_TYPE_SHIFT) \ + | (((Op) & ERTS_SIG_Q_OP_MASK) \ + << ERTS_SIG_Q_OP_SHIFT) \ + | (((Xtra) & ERTS_SIG_Q_XTRA_MASK) \ + << ERTS_SIG_Q_XTRA_SHIFT), \ + _TAG_HEADER_EXTERNAL_PID)) + +Process *ERTS_WRITE_UNLIKELY(erts_dirty_process_signal_handler); +Process *ERTS_WRITE_UNLIKELY(erts_dirty_process_signal_handler_high); +Process *ERTS_WRITE_UNLIKELY(erts_dirty_process_signal_handler_max); + +void +erts_proc_sig_queue_init(void) +{ + ERTS_CT_ASSERT(ERTS_SIG_Q_OP_MASK >= ERTS_SIG_Q_OP_MAX); + ERTS_CT_ASSERT(ERTS_SIG_Q_TYPE_MASK >= ERTS_SIG_Q_TYPE_MAX); +} + +typedef struct { + int active; + int procs; + struct { + int active; +#if defined(USE_VM_PROBES) + int vm_probes; + char receiver_name[DTRACE_TERM_BUF_SIZE]; +#endif + int receive_trace; + int bp_ix; + ErtsMessage **next; + ErtsTracingEvent *event; + } messages; +} ErtsSigRecvTracing; + +typedef struct { + Eterm message; + Eterm from; + Eterm reason; + union { + Eterm ref; + int normal_kills; + } u; +} ErtsExitSignalData; + +typedef struct { + Eterm message; + Eterm key; +} ErtsPersistMonMsg; + +typedef struct { + ErtsSignalCommon common; + Eterm local; /* internal pid (immediate) */ + Eterm remote; /* external pid (heap for it follow) */ + Eterm heap[EXTERNAL_THING_HEAD_SIZE + 1]; +} ErtsSigDistLinkOp; + +typedef struct { + ErtsSignalCommon common; + Uint flags_on; + Uint flags_off; + Eterm tracer; +} ErtsSigTraceInfo; + +#define ERTS_SIG_GL_FLG_ACTIVE (((erts_aint_t) 1) << 0) +#define ERTS_SIG_GL_FLG_RECEIVER (((erts_aint_t) 1) << 1) +#define ERTS_SIG_GL_FLG_SENDER (((erts_aint_t) 1) << 2) + +typedef struct { + ErtsSignalCommon common; + erts_atomic_t flags; + Eterm group_leader; + Eterm reply_to; + Eterm ref; + ErlOffHeap oh; + Eterm heap[1]; +} ErtsSigGroupLeader; + +typedef struct { + Eterm message; + Eterm requester; +} ErtsIsAliveRequest; + +static int handle_msg_tracing(Process *c_p, + ErtsSigRecvTracing *tracing, + ErtsMessage ***next_nm_sig); +static int handle_trace_change_state(Process *c_p, + ErtsSigRecvTracing *tracing, + Uint16 type, + ErtsMessage *sig, + ErtsMessage ***next_nm_sig); +static void getting_unlinked(Process *c_p, Eterm unlinker); +static void getting_linked(Process *c_p, Eterm linker); +static void group_leader_reply(Process *c_p, Eterm to, + Eterm ref, int success); +static int stretch_limit(Process *c_p, ErtsSigRecvTracing *tp, + int abs_lim, int *limp); + +#ifdef ERTS_PROC_SIG_HARD_DEBUG +#define ERTS_PROC_SIG_HDBG_PRIV_CHKQ(P, T, NMN) \ + do { \ + ErtsMessage **nm_next__ = *(NMN); \ + ErtsMessage **nm_last__ = (P)->sig_qs.nmsigs.last; \ + if (!nm_next__ || !*nm_next__) { \ + nm_next__ = NULL; \ + nm_last__ = NULL; \ + } \ + proc_sig_hdbg_check_queue((P), \ + 1, \ + &(P)->sig_qs.cont, \ + (P)->sig_qs.cont_last, \ + nm_next__, \ + nm_last__, \ + (T), \ + NULL, \ + ERTS_PSFLG_FREE); \ + } while (0); +static Sint +proc_sig_hdbg_check_queue(Process *c_p, + int privq, + ErtsMessage **sig_next, + ErtsMessage **sig_last, + ErtsMessage **sig_nm_next, + ErtsMessage **sig_nm_last, + ErtsSigRecvTracing *tracing, + int *found_saved_last_p, + erts_aint32_t sig_psflg); +#else +#define ERTS_PROC_SIG_HDBG_PRIV_CHKQ(P, T, NMN) +#endif + +typedef struct { + ErtsSignalCommon common; + Eterm ref; + Eterm heap[1]; +} ErtsSigDistProcDemonitor; + +static void +destroy_dist_proc_demonitor(ErtsSigDistProcDemonitor *dmon) +{ + Eterm ref = dmon->ref; + if (is_external(ref)) { + ExternalThing *etp = external_thing_ptr(ref); + erts_deref_node_entry(etp->node); + } + erts_free(ERTS_ALC_T_DIST_DEMONITOR, dmon); +} + +static ERTS_INLINE ErtsSigDistLinkOp * +make_sig_dist_link_op(int op, Eterm local, Eterm remote) +{ + Eterm *hp; + ErlOffHeap oh = {0}; + ErtsSigDistLinkOp *sdlnk = erts_alloc(ERTS_ALC_T_SIG_DATA, + sizeof(ErtsSigDistLinkOp)); + ASSERT(is_internal_pid(local)); + ASSERT(is_external_pid(remote)); + + hp = &sdlnk->heap[0]; + + sdlnk->common.tag = ERTS_PROC_SIG_MAKE_TAG(op, + ERTS_SIG_Q_TYPE_DIST_LINK, + 0); + sdlnk->local = local; + sdlnk->remote = STORE_NC(&hp, &oh, remote); + + ASSERT(&sdlnk->heap[0] < hp); + ASSERT(hp <= &sdlnk->heap[0] + sizeof(sdlnk->heap)/sizeof(sdlnk->heap[0])); + ASSERT(boxed_val(sdlnk->remote) == &sdlnk->heap[0]); + + return sdlnk; +} + +static ERTS_INLINE void +destroy_sig_dist_link_op(ErtsSigDistLinkOp *sdlnk) +{ + ASSERT(is_external_pid(sdlnk->remote)); + ASSERT(boxed_val(sdlnk->remote) == &sdlnk->heap[0]); + erts_deref_node_entry(((ExternalThing *) &sdlnk->heap[0])->node); + erts_free(ERTS_ALC_T_SIG_DATA, sdlnk); +} + +static ERTS_INLINE ErtsExitSignalData * +get_exit_signal_data(ErtsMessage *xsig) +{ + ASSERT(ERTS_SIG_IS_NON_MSG(xsig)); + ASSERT((ERTS_PROC_SIG_OP(((ErtsSignal *) xsig)->common.tag) + == ERTS_SIG_Q_OP_EXIT) + || (ERTS_PROC_SIG_OP(((ErtsSignal *) xsig)->common.tag) + == ERTS_SIG_Q_OP_EXIT_LINKED) + || (ERTS_PROC_SIG_OP(((ErtsSignal *) xsig)->common.tag) + == ERTS_SIG_Q_OP_MONITOR_DOWN)); + ASSERT(xsig->hfrag.alloc_size > xsig->hfrag.used_size); + ASSERT((xsig->hfrag.alloc_size - xsig->hfrag.used_size)*sizeof(UWord) + >= sizeof(ErtsExitSignalData)); + return (ErtsExitSignalData *) (char *) (&xsig->hfrag.mem[0] + + xsig->hfrag.used_size); +} + +static ERTS_INLINE void +destroy_trace_info(ErtsSigTraceInfo *ti) +{ + if (is_value(ti->tracer)) + erts_tracer_update(&ti->tracer, NIL); + erts_free(ERTS_ALC_T_SIG_DATA, ti); +} + +static void +destroy_sig_group_leader(ErtsSigGroupLeader *sgl) +{ + erts_cleanup_offheap(&sgl->oh); + erts_free(ERTS_ALC_T_SIG_DATA, sgl); +} + +static ERTS_INLINE void +sig_enqueue_trace(Process *c_p, ErtsMessage *sig, int op, + Process *rp, ErtsMessage **first, + ErtsMessage **last, ErtsMessage ***last_next) +{ + switch (op) { + case ERTS_SIG_Q_OP_LINK: + if (c_p + && ((!!IS_TRACED(c_p)) + & (ERTS_TRACE_FLAGS(c_p) & (F_TRACE_SOL + | F_TRACE_SOL1)))) { + ErtsSigTraceInfo *ti; + Eterm tag; + /* + * Set on link enabled. + * + * Prepend a trace-change-state signal before the + * link signal... + */ + + tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_TRACE_CHANGE_STATE, + ERTS_SIG_Q_TYPE_ADJUST_TRACE_INFO, + 0); + ti = erts_alloc(ERTS_ALC_T_SIG_DATA, sizeof(ErtsSigTraceInfo)); + ti->common.next = *last; + ti->common.specific.next = &ti->common.next; + ti->common.tag = tag; + ti->flags_on = ERTS_TRACE_FLAGS(c_p) & TRACEE_FLAGS; + if (!(ti->flags_on & F_TRACE_SOL1)) + ti->flags_off = 0; + else { + ti->flags_off = F_TRACE_SOL1|F_TRACE_SOL; + erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); + ERTS_TRACE_FLAGS(c_p) &= ~(F_TRACE_SOL1|F_TRACE_SOL); + erts_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); + } + erts_tracer_update(&ti->tracer, ERTS_TRACER(c_p)); + *first = (ErtsMessage *) ti; + *last_next = &ti->common.next; + } + break; + +#ifdef USE_VM_PROBES + case ERTS_SIG_Q_OP_EXIT: + case ERTS_SIG_Q_OP_EXIT_LINKED: + + if (DTRACE_ENABLED(process_exit_signal)) { + Uint16 type = ERTS_PROC_SIG_TYPE(((ErtsSignal *) sig)->common.tag); + Eterm reason, from; + + if (type == ERTS_SIG_Q_TYPE_GEN_EXIT) { + ErtsExitSignalData *xsigd = get_exit_signal_data(sig); + reason = xsigd->reason; + from = xsigd->from; + } + else { + ErtsLink *lnk = (ErtsLink *) sig, *olnk; + + ASSERT(type == ERTS_LNK_TYPE_PROC + || type == ERTS_LNK_TYPE_PORT + || type == ERTS_LNK_TYPE_DIST_PROC); + + olnk = erts_link_to_other(lnk, NULL); + reason = lnk->other.item; + from = olnk->other.item; + } + + if (is_pid(from)) { + + DTRACE_CHARBUF(sender_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(receiver_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(reason_buf, DTRACE_TERM_BUF_SIZE); + + if (reason == am_kill) { + reason = am_killed; + } + + dtrace_pid_str(from, sender_str); + dtrace_proc_str(rp, receiver_str); + erts_snprintf(reason_buf, sizeof(DTRACE_CHARBUF_NAME(reason_buf)) - 1, "%T", reason); + DTRACE3(process_exit_signal, sender_str, receiver_str, reason_buf); + } + } + break; + +#endif + + default: + break; + } +} + +static void +sig_enqueue_trace_cleanup(ErtsMessage *first, ErtsSignal *sig, ErtsMessage *last) +{ + ErtsMessage *tmp; + + /* The usual case; no tracing signals... */ + if (sig == (ErtsSignal *) first && sig == (ErtsSignal *) last) { + sig->common.next = NULL; + return; + } + + /* Got trace signals to clean up... */ + + tmp = first; + + while (tmp) { + ErtsMessage *tmp_free = tmp; + tmp = tmp->next; + if (sig != (ErtsSignal *) tmp_free) { + switch (ERTS_PROC_SIG_OP(((ErtsSignal *) tmp_free)->common.tag)) { + case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE: + destroy_trace_info((ErtsSigTraceInfo *) tmp_free); + break; + default: + ERTS_INTERNAL_ERROR("Unexpected signal op"); + break; + } + } + } +} + +static ERTS_INLINE erts_aint32_t +enqueue_signals(Process *rp, ErtsMessage *first, + ErtsMessage *last, ErtsMessage **last_next, + erts_aint32_t in_state) +{ + erts_aint32_t state = in_state; + ErtsMessage **this = rp->sig_inq.last; + + ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(rp); + + ASSERT(!*this); + *this = first; + rp->sig_inq.last = &last->next; + + if (!rp->sig_inq.nmsigs.next) { + ASSERT(!rp->sig_inq.nmsigs.last); + rp->sig_inq.nmsigs.next = this; + state = erts_atomic32_read_bor_nob(&rp->state, + ERTS_PSFLG_SIG_IN_Q); + ASSERT(!(state & ERTS_PSFLG_SIG_IN_Q)); + } + else { + ErtsSignal *sig; + ASSERT(rp->sig_inq.nmsigs.last); + + sig = (ErtsSignal *) *rp->sig_inq.nmsigs.last; + + ASSERT(sig && !sig->common.specific.next); + ASSERT(state & ERTS_PSFLG_SIG_IN_Q); + sig->common.specific.next = this; + } + + if (last_next) { + ASSERT(first != last); + rp->sig_inq.nmsigs.last = last_next; + } + else { + ASSERT(first == last); + rp->sig_inq.nmsigs.last = this; + } + + ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(rp); + + return state; +} + +static ERTS_INLINE void +ensure_dirty_proc_handled(Eterm pid, + erts_aint32_t state, + erts_aint32_t prio) +{ + if (state & (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)) { + Eterm *hp; + ErtsMessage *mp; + Process *sig_handler; + + if (prio < 0) + prio = (int) ERTS_PSFLGS_GET_USR_PRIO(state); + + switch (prio) { + case PRIORITY_MAX: + sig_handler = erts_dirty_process_signal_handler_max; + break; + case PRIORITY_HIGH: + sig_handler = erts_dirty_process_signal_handler_high; + break; + default: + sig_handler = erts_dirty_process_signal_handler; + break; + } + + /* Make sure signals are handled... */ + mp = erts_alloc_message(0, &hp); + erts_queue_message(sig_handler, 0, mp, pid, am_system); + } +} + +static int +proc_queue_signal(Process *c_p, Eterm pid, ErtsSignal *sig, int op) +{ + int res; + Process *rp; + ErtsMessage *first, *last, **last_next; + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + int is_normal_sched = !!esdp && esdp->type == ERTS_SCHED_NORMAL; + erts_aint32_t state; + + if (is_normal_sched) + rp = erts_proc_lookup_raw(pid); + else + rp = erts_proc_lookup_raw_inc_refc(pid); + + if (!rp) + return 0; + + sig->common.specific.next = NULL; + first = last = (ErtsMessage *) sig; + last_next = NULL; + + /* may add signals before and/or after sig */ + sig_enqueue_trace(c_p, first, op, rp, + &first, &last, &last_next); + + last->next = NULL; + + erts_proc_lock(rp, ERTS_PROC_LOCK_MSGQ); + + state = erts_atomic32_read_nob(&rp->state); + + if (ERTS_PSFLG_FREE & state) + res = 0; + else { + state = enqueue_signals(rp, first, last, last_next, state); + res = !0; + } + + erts_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ); + + if (res == 0) + sig_enqueue_trace_cleanup(first, sig, last); + + if (!(state & (ERTS_PSFLG_EXITING + | ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_SIG_IN_Q))) { + /* Schedule process... */ + state = erts_proc_sys_schedule(rp, state, 0); + } + + ensure_dirty_proc_handled(rp->common.id, state, -1); + + if (!is_normal_sched) + erts_proc_dec_refc(rp); + + return res; +} + +static int +maybe_elevate_sig_handling_prio(Process *c_p, Eterm other) +{ + /* + * returns: + * > 0 -> elevated prio; process alive or exiting + * < 0 -> no elevation needed; process alive or exiting + * 0 -> process terminated (free) + */ + int res; + Process *rp; + erts_aint32_t state, my_prio, other_prio; + + rp = erts_proc_lookup_raw(other); + if (!rp) + res = 0; + else { + res = -1; + state = erts_atomic32_read_nob(&c_p->state); + my_prio = ERTS_PSFLGS_GET_USR_PRIO(state); + + state = erts_atomic32_read_nob(&rp->state); + other_prio = ERTS_PSFLGS_GET_USR_PRIO(state); + + if (other_prio > my_prio) { + /* Others prio is lower than mine; elevate it... */ + res = !!erts_sig_prio(other, my_prio); + if (res) { + /* ensure handled if dirty executing... */ + state = erts_atomic32_read_nob(&rp->state); + ensure_dirty_proc_handled(other, state, my_prio); + } + } + } + return res; +} + +void +erts_proc_sig_fetch(Process *proc) +{ +#ifdef ERTS_PROC_SIG_HARD_DEBUG + ErtsSignalPrivQueues sig_qs = proc->sig_qs; + ErtsSignalInQueue sig_inq = proc->sig_inq; +#endif + + ERTS_LC_ASSERT(erts_thr_progress_is_blocking() + || ERTS_PROC_IS_EXITING(proc) + || ((erts_proc_lc_my_proc_locks(proc) + & (ERTS_PROC_LOCK_MAIN + | ERTS_PROC_LOCK_MSGQ)) + == (ERTS_PROC_LOCK_MAIN + | ERTS_PROC_LOCK_MSGQ))); + + if (!proc->sig_inq.first) { + ASSERT(proc->sig_inq.last == &proc->sig_inq.first); + ASSERT(proc->sig_inq.len == 0); + ASSERT(!proc->sig_inq.nmsigs.next); + ASSERT(!proc->sig_inq.nmsigs.last); + return; + } + + ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(proc); + ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(proc); + + if (!proc->sig_inq.nmsigs.next) { + ASSERT(!(ERTS_PSFLG_SIG_IN_Q + & erts_atomic32_read_nob(&proc->state))); + ASSERT(!proc->sig_inq.nmsigs.last); + + if (proc->sig_qs.cont || ERTS_MSG_RECV_TRACED(proc)) { + *proc->sig_qs.cont_last = proc->sig_inq.first; + proc->sig_qs.cont_last = proc->sig_inq.last; + } + else { + *proc->sig_qs.last = proc->sig_inq.first; + proc->sig_qs.last = proc->sig_inq.last; + } + } + else { +#ifdef DEBUG + erts_aint32_t s; +#endif + ASSERT(proc->sig_inq.nmsigs.last); + if (!proc->sig_qs.nmsigs.last) { + ASSERT(!proc->sig_qs.nmsigs.next); + if (proc->sig_inq.nmsigs.next == &proc->sig_inq.first) + proc->sig_qs.nmsigs.next = proc->sig_qs.cont_last; + else + proc->sig_qs.nmsigs.next = proc->sig_inq.nmsigs.next; + +#ifdef DEBUG + s = +#endif + erts_atomic32_read_bset_nob(&proc->state, + (ERTS_PSFLG_SIG_Q + | ERTS_PSFLG_SIG_IN_Q), + ERTS_PSFLG_SIG_Q); + + ASSERT((s & (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q)) + == ERTS_PSFLG_SIG_IN_Q); + } + else { + ErtsSignal *sig; + ASSERT(proc->sig_qs.nmsigs.next); + sig = ((ErtsSignal *) *proc->sig_qs.nmsigs.last); + ASSERT(ERTS_SIG_IS_NON_MSG(sig)); + ASSERT(!sig->common.specific.next); + if (proc->sig_inq.nmsigs.next == &proc->sig_inq.first) + sig->common.specific.next = proc->sig_qs.cont_last; + else + sig->common.specific.next = proc->sig_inq.nmsigs.next; + +#ifdef DEBUG + s = +#endif + erts_atomic32_read_band_nob(&proc->state, + ~ERTS_PSFLG_SIG_IN_Q); + + ASSERT((s & (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q)) + == (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q)); + } + if (proc->sig_inq.nmsigs.last == &proc->sig_inq.first) + proc->sig_qs.nmsigs.last = proc->sig_qs.cont_last; + else + proc->sig_qs.nmsigs.last = proc->sig_inq.nmsigs.last; + proc->sig_inq.nmsigs.next = NULL; + proc->sig_inq.nmsigs.last = NULL; + + *proc->sig_qs.cont_last = proc->sig_inq.first; + proc->sig_qs.cont_last = proc->sig_inq.last; + } + + proc->sig_qs.len += proc->sig_inq.len; + + proc->sig_inq.first = NULL; + proc->sig_inq.last = &proc->sig_inq.first; + proc->sig_inq.len = 0; + + ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(proc); +} + +void do_seq_trace_output(Eterm to, Eterm token, Eterm msg); + +static void +send_gen_exit_signal(Process *c_p, Eterm from_tag, + Eterm from, Eterm to, + Sint16 op, Eterm reason, Eterm ref, + Eterm token, int normal_kills) +{ + ErtsExitSignalData *xsigd; + Eterm *hp, *start_hp, s_reason, s_ref, s_message, s_token, s_from; + ErtsMessage *mp; + ErlHeapFragment *hfrag; + ErlOffHeap *ohp; + Uint hsz, from_sz, reason_sz, ref_sz, token_sz; + int seq_trace; +#ifdef USE_VM_PROBES + Eterm s_utag, utag; + Uint utag_sz; +#endif + + ASSERT(is_immed(from_tag)); + + hsz = sizeof(ErtsExitSignalData)/sizeof(Uint); + + seq_trace = c_p && have_seqtrace(token); + if (seq_trace) + seq_trace_update_send(c_p); + +#ifdef USE_VM_PROBES + utag_sz = 0; + utag = NIL; + if (c_p && token != NIL && (DT_UTAG_FLAGS(c_p) & DT_UTAG_SPREADING)) { + utag_sz = size_object(DT_UTAG(c_p)); + utag = DT_UTAG(c_p); + } + else if (token == am_have_dt_utag) { + token = NIL; + } + hsz += utag_sz; +#endif + + token_sz = is_immed(token) ? 0 : size_object(token); + hsz += token_sz; + + from_sz = is_immed(from) ? 0 : size_object(from); + hsz += from_sz; + + reason_sz = is_immed(reason) ? 0 : size_object(reason); + hsz += reason_sz; + + switch (op) { + case ERTS_SIG_Q_OP_EXIT: + case ERTS_SIG_Q_OP_EXIT_LINKED: { + /* {'EXIT', From, Reason} */ + hsz += 4; /* 3-tuple */ + ref_sz = 0; + break; + } + case ERTS_SIG_Q_OP_MONITOR_DOWN: { + /* {'DOWN', Ref, process, From, Reason} */ + hsz += 6; /* 5-tuple */ + ref_sz = NC_HEAP_SIZE(ref); + hsz += ref_sz; + break; + } + default: + ERTS_INTERNAL_ERROR("Invalid exit signal op"); + break; + } + + /* + * Allocate message combined with heap fragment... + */ + mp = erts_alloc_message(hsz, &hp); + hfrag = &mp->hfrag; + mp->next = NULL; + ohp = &hfrag->off_heap; + start_hp = hp; + + s_token = (is_immed(token) + ? token + : copy_struct(token, token_sz, &hp, ohp)); + + s_reason = (is_immed(reason) + ? reason + : copy_struct(reason, reason_sz, &hp, ohp)); + + s_from = (is_immed(from) + ? from + : copy_struct(from, from_sz, &hp, ohp)); + + if (!ref_sz) + s_ref = NIL; + else + s_ref = STORE_NC(&hp, ohp, ref); + + switch (op) { + case ERTS_SIG_Q_OP_EXIT: + case ERTS_SIG_Q_OP_EXIT_LINKED: + /* {'EXIT', From, Reason} */ + s_message = TUPLE3(hp, am_EXIT, s_from, s_reason); + hp += 4; + break; + case ERTS_SIG_Q_OP_MONITOR_DOWN: + /* {'DOWN', Ref, process, From, Reason} */ + s_message = TUPLE5(hp, am_DOWN, s_ref, am_process, s_from, s_reason); + hp += 6; + break; + } + +#ifdef USE_VM_PROBES + s_utag = (is_immed(utag) + ? utag + : copy_struct(utag, utag_sz, &hp, ohp)); + ERL_MESSAGE_DT_UTAG(mp) = s_utag; +#endif + + ERL_MESSAGE_TERM(mp) = ERTS_PROC_SIG_MAKE_TAG(op, + ERTS_SIG_Q_TYPE_GEN_EXIT, + 0); + ERL_MESSAGE_TOKEN(mp) = s_token; + ERL_MESSAGE_FROM(mp) = from_tag; /* immediate... */ + + hfrag->used_size = hp - start_hp; + + xsigd = (ErtsExitSignalData *) (char *) hp; + + xsigd->message = s_message; + xsigd->from = s_from; + xsigd->reason = s_reason; + if (is_nil(s_ref)) + xsigd->u.normal_kills = normal_kills; + else { + ASSERT(is_ref(s_ref)); + xsigd->u.ref = s_ref; + } + + if (seq_trace) + do_seq_trace_output(to, s_token, s_message); + + if (!proc_queue_signal(c_p, to, (ErtsSignal *) mp, op)) { + mp->next = NULL; + erts_cleanup_messages(mp); + } +} + +void +do_seq_trace_output(Eterm to, Eterm token, Eterm msg) +{ + /* + * We could do this when enqueuing the signal and avoid some + * locking. However, the enqueuing code would then always + * have the penalty of this seq-tracing code which we do not + * want... + */ + ErtsSchedulerData *esdp = erts_get_scheduler_data(); + int is_normal_sched = !!esdp && esdp->type == ERTS_SCHED_NORMAL; + Process *rp; + + if (is_normal_sched) + rp = erts_proc_lookup_raw(to); + else + rp = erts_proc_lookup_raw_inc_refc(to); + + erts_proc_lock(rp, ERTS_PROC_LOCK_MSGQ); + + if (!ERTS_PROC_IS_EXITING(rp)) + seq_trace_output(token, msg, SEQ_TRACE_SEND, to, rp); + + erts_proc_unlock(rp, ERTS_PROC_LOCK_MSGQ); + + if (!is_normal_sched) + erts_proc_dec_refc(rp); +} + +void +erts_proc_sig_send_persistent_monitor_msg(Uint16 type, Eterm key, + Eterm from, Eterm to, + Eterm msg, Uint msg_sz) +{ + ErtsPersistMonMsg *prst_mon; + ErtsMessage *mp; + ErlHeapFragment *hfrag; + Eterm *hp, *start_hp, message; + ErlOffHeap *ohp; + Uint hsz = sizeof(ErtsPersistMonMsg) + msg_sz; + + /* + * Allocate message combined with heap fragment... + */ + mp = erts_alloc_message(hsz, &hp); + hfrag = &mp->hfrag; + mp->next = NULL; + ohp = &hfrag->off_heap; + start_hp = hp; + + ASSERT(msg_sz == size_object(msg)); + message = copy_struct(msg, msg_sz, &hp, ohp); + hfrag->used_size = hp - start_hp; + + prst_mon = (ErtsPersistMonMsg *) (char *) hp; + prst_mon->message = message; + + switch (type) { + case ERTS_MON_TYPE_NODES: + ASSERT(is_small(key)); + prst_mon->key = key; + break; + + case ERTS_MON_TYPE_TIME_OFFSET: + ASSERT(is_internal_ref(key)); + ASSERT(is_tuple_arity(message, 5)); + + prst_mon->key = tuple_val(message)[2]; + + ASSERT(eq(prst_mon->key, key)); + break; + + default: + ERTS_INTERNAL_ERROR("Invalid persistent monitor type"); + prst_mon->key = key; + break; + } + + ASSERT(is_immed(from)); + + ERL_MESSAGE_TERM(mp) = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_PERSISTENT_MON_MSG, + type, 0); + ERL_MESSAGE_FROM(mp) = from; + ERL_MESSAGE_TOKEN(mp) = NIL; +#ifdef USE_VM_PROBES + ERL_MESSAGE_DT_UTAG(mp) = NIL; +#endif + + if (!proc_queue_signal(NULL, to, (ErtsSignal *) mp, + ERTS_SIG_Q_OP_PERSISTENT_MON_MSG)) { + mp->next = NULL; + erts_cleanup_messages(mp); + } +} + +static ERTS_INLINE Eterm +get_persist_mon_msg(ErtsMessage *sig, Eterm *msg) +{ + ErtsPersistMonMsg *prst_mon; + prst_mon = ((ErtsPersistMonMsg *) + (char *) (&sig->hfrag.mem[0] + + sig->hfrag.used_size)); + *msg = prst_mon->message; + return prst_mon->key; +} + +void +erts_proc_sig_send_exit(Process *c_p, Eterm from, Eterm to, + Eterm reason, Eterm token, + int normal_kills) +{ + Eterm from_tag; + ASSERT(!c_p || c_p->common.id == from); + if (is_immed(from)) { + ASSERT(is_internal_pid(from) || is_internal_port(from)); + from_tag = from; + } + else { + DistEntry *dep; + ASSERT(is_external_pid(from)); + dep = external_pid_dist_entry(from); + from_tag = dep->sysname; + } + send_gen_exit_signal(c_p, from_tag, from, to, ERTS_SIG_Q_OP_EXIT, + reason, NIL, token, normal_kills); +} + +void +erts_proc_sig_send_link_exit(Process *c_p, Eterm from, ErtsLink *lnk, + Eterm reason, Eterm token) +{ + Eterm to; + ASSERT(!c_p || c_p->common.id == from); + ASSERT(lnk); + to = lnk->other.item; + if (is_not_immed(reason) || is_not_nil(token)) { + ASSERT(is_internal_pid(from) || is_internal_port(from)); + send_gen_exit_signal(c_p, from, from, to, ERTS_SIG_Q_OP_EXIT_LINKED, + reason, NIL, token, 0); + } + else { + /* Pass signal using old link structure... */ + ErtsSignal *sig = (ErtsSignal *) lnk; + lnk->other.item = reason; /* pass reason via this other.item */ + sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_EXIT_LINKED, + lnk->type, 0); + if (proc_queue_signal(c_p, to, sig, ERTS_SIG_Q_OP_EXIT_LINKED)) + return; /* receiver will destroy lnk structure */ + } + if (lnk) + erts_link_release(lnk); +} + +int +erts_proc_sig_send_link(Process *c_p, Eterm to, ErtsLink *lnk) +{ + ErtsSignal *sig; + Uint16 type = lnk->type; + + ASSERT(!c_p || c_p->common.id == lnk->other.item); + ASSERT(lnk); + ASSERT(is_internal_pid(to)); + + sig = (ErtsSignal *) lnk; + sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_LINK, + type, 0); + + return proc_queue_signal(c_p, to, sig, ERTS_SIG_Q_OP_LINK); +} + +void +erts_proc_sig_send_unlink(Process *c_p, ErtsLink *lnk) +{ + ErtsSignal *sig; + Eterm to; + + ASSERT(lnk); + + sig = (ErtsSignal *) lnk; + to = lnk->other.item; + + ASSERT(is_internal_pid(to)); + + sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_UNLINK, + lnk->type, 0); + + if (!proc_queue_signal(c_p, to, sig, ERTS_SIG_Q_OP_UNLINK)) + erts_link_release(lnk); +} + +void +erts_proc_sig_send_dist_link_exit(DistEntry *dep, + Eterm from, Eterm to, + Eterm reason, Eterm token) +{ + send_gen_exit_signal(NULL, dep->sysname, from, to, ERTS_SIG_Q_OP_EXIT_LINKED, + reason, NIL, token, 0); +} + +void +erts_proc_sig_send_dist_unlink(DistEntry *dep, Eterm from, Eterm to) +{ + ErtsSignal *sig; + + ASSERT(is_internal_pid(to)); + ASSERT(is_external_pid(from)); + ASSERT(dep == external_pid_dist_entry(from)); + + sig = (ErtsSignal *) make_sig_dist_link_op(ERTS_SIG_Q_OP_UNLINK, + to, from); + + if (!proc_queue_signal(NULL, to, sig, ERTS_SIG_Q_OP_UNLINK)) + destroy_sig_dist_link_op((ErtsSigDistLinkOp *) sig); +} + +void +erts_proc_sig_send_dist_monitor_down(DistEntry *dep, Eterm ref, + Eterm from, Eterm to, + Eterm reason) +{ + Eterm monitored, heap[3]; + if (is_atom(from)) + monitored = TUPLE2(&heap[0], from, dep->sysname); + else + monitored = from; + send_gen_exit_signal(NULL, dep->sysname, monitored, + to, ERTS_SIG_Q_OP_MONITOR_DOWN, + reason, ref, NIL, 0); +} + +void +erts_proc_sig_send_monitor_down(ErtsMonitor *mon, Eterm reason) +{ + Eterm to; + + ASSERT(erts_monitor_is_target(mon)); + ASSERT(!erts_monitor_is_in_table(mon)); + + to = mon->other.item; + ASSERT(is_internal_pid(to)); + + if (is_immed(reason)) { + /* Pass signal using old monitor structure... */ + ErtsSignal *sig; + + mon->other.item = reason; /* Pass immed reason via other.item... */ + sig = (ErtsSignal *) mon; + sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_MONITOR_DOWN, + mon->type, 0); + if (proc_queue_signal(NULL, to, sig, ERTS_SIG_Q_OP_MONITOR_DOWN)) + return; /* receiver will destroy mon structure */ + } + else { + ErtsMonitorData *mdp = erts_monitor_to_data(mon); + Eterm from_tag, monitored, heap[3]; + + if (!(mon->flags & ERTS_ML_FLG_NAME)) { + from_tag = monitored = mdp->origin.other.item; + if (is_external_pid(from_tag)) { + DistEntry *dep = external_pid_dist_entry(from_tag); + from_tag = dep->sysname; + } + } + else { + ErtsMonitorDataExtended *mdep; + Eterm name, node; + mdep = (ErtsMonitorDataExtended *) mdp; + name = mdep->u.name; + ASSERT(is_atom(name)); + if (mdep->dist) { + node = mdep->dist->nodename; + from_tag = node; + } + else { + node = erts_this_dist_entry->sysname; + from_tag = mdp->origin.other.item; + } + ASSERT(is_internal_port(from_tag) + || is_internal_pid(from_tag) + || is_atom(from_tag)); + monitored = TUPLE2(&heap[0], name, node); + } + send_gen_exit_signal(NULL, from_tag, monitored, + to, ERTS_SIG_Q_OP_MONITOR_DOWN, + reason, mdp->ref, NIL, 0); + } + erts_monitor_release(mon); +} + +void +erts_proc_sig_send_dist_demonitor(Eterm to, Eterm ref) +{ + ErtsSigDistProcDemonitor *dmon; + ErtsSignal *sig; + Eterm *hp; + ErlOffHeap oh; + size_t size; + + ERTS_INIT_OFF_HEAP(&oh); + + ASSERT(is_internal_pid(to)); + + size = sizeof(ErtsSigDistProcDemonitor) - sizeof(Eterm); + ASSERT(is_ref(ref)); + size += NC_HEAP_SIZE(ref)*sizeof(Eterm); + + dmon = erts_alloc(ERTS_ALC_T_DIST_DEMONITOR, size); + + hp = &dmon->heap[0]; + dmon->ref = STORE_NC(&hp, &oh, ref); + sig = (ErtsSignal *) dmon; + + sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_DEMONITOR, + ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR, + 0); + + if (!proc_queue_signal(NULL, to, sig, ERTS_SIG_Q_OP_DEMONITOR)) + destroy_dist_proc_demonitor(dmon); +} + +void +erts_proc_sig_send_demonitor(ErtsMonitor *mon) +{ + ErtsSignal *sig = (ErtsSignal *) mon; + Uint16 type = mon->type; + Eterm to = mon->other.item; + + ASSERT(is_internal_pid(to)); + ASSERT(erts_monitor_is_origin(mon)); + ASSERT(!erts_monitor_is_in_table(mon)); + + sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_DEMONITOR, + type, 0); + + if (!proc_queue_signal(NULL, to, sig, ERTS_SIG_Q_OP_DEMONITOR)) + erts_monitor_release(mon); +} + +int +erts_proc_sig_send_monitor(ErtsMonitor *mon, Eterm to) +{ + ErtsSignal *sig = (ErtsSignal *) mon; + Uint16 type = mon->type; + + ASSERT(is_internal_pid(to) || to == am_undefined); + ASSERT(erts_monitor_is_target(mon)); + + sig->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_MONITOR, + type, 0); + + return proc_queue_signal(NULL, to, sig, ERTS_SIG_Q_OP_MONITOR); +} + +void +erts_proc_sig_send_trace_change(Eterm to, Uint on, Uint off, Eterm tracer) +{ + ErtsSigTraceInfo *ti; + Eterm tag; + + ti = erts_alloc(ERTS_ALC_T_SIG_DATA, sizeof(ErtsSigTraceInfo)); + tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_TRACE_CHANGE_STATE, + ERTS_SIG_Q_TYPE_ADJUST_TRACE_INFO, + 0); + + ti->common.tag = tag; + ti->flags_off = off; + ti->flags_on = on; + ti->tracer = NIL; + if (is_not_nil(tracer)) + erts_tracer_update(&ti->tracer, tracer); + + if (!proc_queue_signal(NULL, to, (ErtsSignal *) ti, + ERTS_SIG_Q_OP_TRACE_CHANGE_STATE)) + destroy_trace_info(ti); +} + +void +erts_proc_sig_send_group_leader(Process *c_p, Eterm to, Eterm gl, Eterm ref) +{ + int res; + ErtsSigGroupLeader *sgl; + Eterm *hp; + Uint gl_sz, ref_sz, size; + erts_aint_t init_flags = ERTS_SIG_GL_FLG_ACTIVE|ERTS_SIG_GL_FLG_RECEIVER; + if (c_p) + init_flags |= ERTS_SIG_GL_FLG_SENDER; + + ASSERT(c_p ? is_internal_ref(ref) : ref == NIL); + + gl_sz = is_immed(gl) ? 0 : size_object(gl); + ref_sz = is_immed(ref) ? 0 : size_object(ref); + + size = sizeof(ErtsSigGroupLeader); + + size += (gl_sz + ref_sz - 1) * sizeof(Eterm); + + sgl = erts_alloc(ERTS_ALC_T_SIG_DATA, size); + + erts_atomic_init_nob(&sgl->flags, init_flags); + + ERTS_INIT_OFF_HEAP(&sgl->oh); + + hp = &sgl->heap[0]; + + sgl->group_leader = is_immed(gl) ? gl : copy_struct(gl, gl_sz, &hp, &sgl->oh); + sgl->reply_to = c_p ? c_p->common.id : NIL; + sgl->ref = is_immed(ref) ? ref : copy_struct(ref, ref_sz, &hp, &sgl->oh); + + sgl->common.tag = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_GROUP_LEADER, + ERTS_SIG_Q_TYPE_UNDEFINED, + 0); + + res = proc_queue_signal(c_p, to, (ErtsSignal *) sgl, + ERTS_SIG_Q_OP_GROUP_LEADER); + + if (!res) + destroy_sig_group_leader(sgl); + else if (c_p) { + erts_aint_t flags, rm_flags = ERTS_SIG_GL_FLG_SENDER; + int prio_res = maybe_elevate_sig_handling_prio(c_p, to); + if (!prio_res) + rm_flags |= ERTS_SIG_GL_FLG_ACTIVE; + flags = erts_atomic_read_band_nob(&sgl->flags, ~rm_flags); + if (!prio_res && (flags & ERTS_SIG_GL_FLG_ACTIVE)) + res = 0; /* We deactivated signal... */ + if ((flags & ~rm_flags) == 0) + destroy_sig_group_leader(sgl); + } + + if (!res && c_p) + group_leader_reply(c_p, c_p->common.id, ref, 0); +} + +void +erts_proc_sig_send_is_alive_request(Process *c_p, Eterm to, Eterm ref) +{ + ErlHeapFragment *hfrag; + Uint hsz; + Eterm *hp, *start_hp, ref_cpy, msg; + ErlOffHeap *ohp; + ErtsMessage *mp; + ErtsIsAliveRequest *alive_req; + + ASSERT(is_internal_ordinary_ref(ref)); + + hsz = ERTS_REF_THING_SIZE + 3 + sizeof(ErtsIsAliveRequest)/sizeof(Eterm); + + mp = erts_alloc_message(hsz, &hp); + hfrag = &mp->hfrag; + mp->next = NULL; + ohp = &hfrag->off_heap; + start_hp = hp; + + ref_cpy = STORE_NC(&hp, ohp, ref); + msg = TUPLE2(hp, ref_cpy, am_false); /* default res 'false' */ + hp += 3; + + hfrag->used_size = hp - start_hp; + + alive_req = (ErtsIsAliveRequest *) (char *) hp; + alive_req->message = msg; + alive_req->requester = c_p->common.id; + + ERL_MESSAGE_TERM(mp) = ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_IS_ALIVE, + ERTS_SIG_Q_TYPE_UNDEFINED, + 0); + ERL_MESSAGE_TOKEN(mp) = NIL; + ERL_MESSAGE_FROM(mp) = am_system; +#ifdef USE_VM_PROBES + ERL_MESSAGE_DT_UTAG(mp) = NIL; +#endif + + if (proc_queue_signal(c_p, to, (ErtsSignal *) mp, ERTS_SIG_Q_OP_IS_ALIVE)) + (void) maybe_elevate_sig_handling_prio(c_p, to); + else { + /* It wasn't alive; reply to ourselves... */ + mp->next = NULL; + mp->data.attached = ERTS_MSG_COMBINED_HFRAG; + erts_queue_message(c_p, ERTS_PROC_LOCK_MAIN, + mp, msg, am_system); + } +} + +static void +is_alive_response(Process *c_p, ErtsMessage *mp, int is_alive) +{ + /* + * Sender prepared the message for us. Just patch + * the result if necessary. The default prepared + * result is 'false'. + */ + Process *rp; + ErtsIsAliveRequest *alive_req; + + alive_req = (ErtsIsAliveRequest *) (char *) (&mp->hfrag.mem[0] + + mp->hfrag.used_size); + + + ASSERT(ERTS_SIG_IS_NON_MSG(mp)); + ASSERT(ERTS_PROC_SIG_OP(((ErtsSignal *) mp)->common.tag) + == ERTS_SIG_Q_OP_IS_ALIVE); + ASSERT(mp->hfrag.alloc_size > mp->hfrag.used_size); + ASSERT((mp->hfrag.alloc_size - mp->hfrag.used_size)*sizeof(UWord) + >= sizeof(ErtsIsAliveRequest)); + ASSERT(is_internal_pid(alive_req->requester)); + ASSERT(alive_req->requester != c_p->common.id); + ASSERT(is_tuple_arity(alive_req->message, 2)); + ASSERT(is_internal_ordinary_ref(tuple_val(alive_req->message)[1])); + ASSERT(tuple_val(alive_req->message)[2] == am_false); + + ERL_MESSAGE_TERM(mp) = alive_req->message; + mp->data.attached = ERTS_MSG_COMBINED_HFRAG; + mp->next = NULL; + + rp = erts_proc_lookup(alive_req->requester); + if (!rp) + erts_cleanup_messages(mp); + else { + if (is_alive) { /* patch result... */ + Eterm *tp = tuple_val(alive_req->message); + tp[2] = am_true; + } + erts_queue_message(rp, 0, mp, alive_req->message, am_system); + } +} + +static ERTS_INLINE void +adjust_tracing_state(Process *c_p, ErtsSigRecvTracing *tracing, int setup) +{ + if (!IS_TRACED(c_p) || (ERTS_TRACE_FLAGS(c_p) & F_SENSITIVE)) { + tracing->messages.active = 0; + tracing->messages.receive_trace = 0; + tracing->messages.event = NULL; + tracing->messages.next = NULL; + tracing->procs = 0; + tracing->active = 0; + } + else { + Uint flgs = ERTS_TRACE_FLAGS(c_p); + int procs_trace = !!(flgs & F_TRACE_PROCS); + int recv_trace = !!(flgs & F_TRACE_RECEIVE); + /* procs tracing enabled? */ + + tracing->procs = procs_trace; + + /* message receive tracing enabled? */ + tracing->messages.receive_trace = recv_trace; + if (!recv_trace) + tracing->messages.event = NULL; + else { + if (tracing->messages.bp_ix < 0) + tracing->messages.bp_ix = erts_active_bp_ix(); + tracing->messages.event = &erts_receive_tracing[tracing->messages.bp_ix]; + } + if (setup) { + if (recv_trace) + tracing->messages.next = &c_p->sig_qs.cont; + else + tracing->messages.next = NULL; + } + tracing->messages.active = recv_trace; + tracing->active = recv_trace | procs_trace; + } + +#if defined(USE_VM_PROBES) + /* vm probe message_queued enabled? */ + + tracing->messages.vm_probes = DTRACE_ENABLED(message_queued); + if (tracing->messages.vm_probes) { + dtrace_proc_str(c_p, tracing->messages.receiver_name); + tracing->messages.active = !0; + tracing->active = !0; + if (setup && !tracing->messages.next) + tracing->messages.next = &c_p->sig_qs.cont; + } + +#endif +} + +static ERTS_INLINE void +setup_tracing_state(Process *c_p, ErtsSigRecvTracing *tracing) +{ + tracing->messages.bp_ix = -1; + adjust_tracing_state(c_p, tracing, !0); +} + +static ERTS_INLINE void +remove_iq_sig(Process *c_p, ErtsMessage *sig, ErtsMessage **next_sig) +{ + /* + * Remove signal from inner queue. + */ + ASSERT(c_p->sig_qs.cont_last != &sig->next); + ASSERT(c_p->sig_qs.nmsigs.next != &sig->next); + ASSERT(c_p->sig_qs.nmsigs.last != &sig->next); + + if (c_p->sig_qs.save == &sig->next) + c_p->sig_qs.save = next_sig; + if (c_p->sig_qs.last == &sig->next) + c_p->sig_qs.last = next_sig; + if (c_p->sig_qs.saved_last == &sig->next) + c_p->sig_qs.saved_last = next_sig; + + *next_sig = sig->next; +} + +static ERTS_INLINE void +remove_mq_sig(Process *c_p, ErtsMessage *sig, + ErtsMessage **next_sig, ErtsMessage ***next_nm_sig) +{ + /* + * Remove signal from middle queue. + */ + ASSERT(c_p->sig_qs.save != &sig->next); + ASSERT(c_p->sig_qs.last != &sig->next); + + if (c_p->sig_qs.cont_last == &sig->next) + c_p->sig_qs.cont_last = next_sig; + if (c_p->sig_qs.saved_last == &sig->next) + c_p->sig_qs.saved_last = next_sig; + if (*next_nm_sig == &sig->next) + *next_nm_sig = next_sig; + if (c_p->sig_qs.nmsigs.last == &sig->next) + c_p->sig_qs.nmsigs.last = next_sig; + + *next_sig = sig->next; +} + +static ERTS_INLINE void +remove_nm_sig(Process *c_p, ErtsMessage *sig, ErtsMessage ***next_nm_sig) +{ + ErtsMessage **next_sig = *next_nm_sig; + ASSERT(ERTS_SIG_IS_NON_MSG(sig)); + ASSERT(*next_sig == sig); + *next_nm_sig = ((ErtsSignal *) sig)->common.specific.next; + remove_mq_sig(c_p, sig, next_sig, next_nm_sig); +} + +static ERTS_INLINE void +convert_to_msg(Process *c_p, ErtsMessage *sig, ErtsMessage *msg, + ErtsMessage ***next_nm_sig) +{ + ErtsMessage **next_sig = *next_nm_sig; + ASSERT(ERTS_SIG_IS_NON_MSG(sig)); + *next_nm_sig = ((ErtsSignal *) sig)->common.specific.next; + c_p->sig_qs.len++; + *next_sig = msg; + remove_mq_sig(c_p, sig, &msg->next, next_nm_sig); +} + +static ERTS_INLINE void +convert_to_msgs(Process *c_p, ErtsMessage *sig, Uint no_msgs, + ErtsMessage *first_msg, ErtsMessage *last_msg, + ErtsMessage ***next_nm_sig) +{ + ErtsMessage **next_sig = *next_nm_sig; + ASSERT(ERTS_SIG_IS_NON_MSG(sig)); + *next_nm_sig = ((ErtsSignal *) sig)->common.specific.next; + c_p->sig_qs.len += no_msgs; + *next_sig = first_msg; + remove_mq_sig(c_p, sig, &last_msg->next, next_nm_sig); +} + +static ERTS_INLINE void +insert_messages(Process *c_p, ErtsMessage **next, ErtsMessage *first, + ErtsMessage *last, Uint no_msgs, ErtsMessage ***next_nm_sig) +{ + last->next = *next; + if (c_p->sig_qs.cont_last == next) + c_p->sig_qs.cont_last = &last->next; + if (*next_nm_sig == next) + *next_nm_sig = &last->next; + if (c_p->sig_qs.nmsigs.last == next) + c_p->sig_qs.nmsigs.last = &last->next; + c_p->sig_qs.len += no_msgs; + *next = first; +} + +static ERTS_INLINE void +remove_mq_m_sig(Process *c_p, ErtsMessage *sig, ErtsMessage **next_sig, ErtsMessage ***next_nm_sig) +{ + /* Removing message... */ + ASSERT(!ERTS_SIG_IS_NON_MSG(sig)); + ASSERT(c_p->sig_qs.len > 0); + c_p->sig_qs.len--; + remove_mq_sig(c_p, sig, next_sig, next_nm_sig); +} + +static ERTS_INLINE void +remove_iq_m_sig(Process *c_p, ErtsMessage *sig, ErtsMessage **next_sig) +{ + /* Removing message... */ + ASSERT(!ERTS_SIG_IS_NON_MSG(sig)); + ASSERT(c_p->sig_qs.len > 0); + c_p->sig_qs.len--; + remove_iq_sig(c_p, sig, next_sig); +} + +static ERTS_INLINE void +convert_prepared_sig_to_msg(Process *c_p, ErtsMessage *sig, Eterm msg, + ErtsMessage ***next_nm_sig) +{ + /* + * Everything is already there except for the reference to + * the message and the combined hfrag marker that needs to be + * restored... + */ + *next_nm_sig = ((ErtsSignal *) sig)->common.specific.next; + sig->data.attached = ERTS_MSG_COMBINED_HFRAG; + ERL_MESSAGE_TERM(sig) = msg; + c_p->sig_qs.len++; +} + +static ERTS_INLINE int +handle_exit_signal(Process *c_p, ErtsSigRecvTracing *tracing, + ErtsMessage *sig, ErtsMessage ***next_nm_sig, + int *exited) +{ + ErtsMessage *conv_msg = NULL; + ErtsExitSignalData *xsigd = NULL; + ErtsLinkData *ldp = NULL; /* Avoid erroneous warning... */ + ErtsLink *dlnk = NULL; /* Avoid erroneous warning... */ + Eterm tag = ((ErtsSignal *) sig)->common.tag; + Uint16 type = ERTS_PROC_SIG_TYPE(tag); + int op = ERTS_PROC_SIG_OP(tag); + int destroy = 0; + int ignore = 0; + int save = 0; + int exit = 0; + int cnt = 1; + Eterm reason; + Eterm from; + + if (type == ERTS_SIG_Q_TYPE_GEN_EXIT) { + xsigd = get_exit_signal_data(sig); + from = xsigd->from; + reason = xsigd->reason; + if (op != ERTS_SIG_Q_OP_EXIT_LINKED) + ignore = 0; + else { + ErtsLink *llnk = erts_link_tree_lookup(ERTS_P_LINKS(c_p), from); + if (!llnk) { + /* Link no longer active; ignore... */ + ignore = !0; + destroy = !0; + } + else { + ignore = 0; + erts_link_tree_delete(&ERTS_P_LINKS(c_p), llnk); + if (llnk->type != ERTS_LNK_TYPE_DIST_PROC) + erts_link_release(llnk); + else { + dlnk = erts_link_to_other(llnk, &ldp); + if (erts_link_dist_delete(dlnk)) + erts_link_release_both(ldp); + else + erts_link_release(llnk); + } + } + } + + if (!ignore) { + + if ((op != ERTS_SIG_Q_OP_EXIT || reason != am_kill) + && (c_p->flags & F_TRAP_EXIT)) { + convert_prepared_sig_to_msg(c_p, sig, + xsigd->message, next_nm_sig); + conv_msg = sig; + } + else if (reason == am_normal && !xsigd->u.normal_kills) { + /* Ignore it... */ + destroy = !0; + ignore = !0; + } + else { + /* Terminate... */ + save = !0; + exit = !0; + if (op == ERTS_SIG_Q_OP_EXIT && reason == am_kill) + reason = am_killed; + } + } + } + else { /* Link exit */ + ErtsLink *slnk = (ErtsLink *) sig; + ErtsLink *llnk = erts_link_to_other(slnk, &ldp); + + ASSERT(type == ERTS_LNK_TYPE_PROC + || type == ERTS_LNK_TYPE_PORT + || type == ERTS_LNK_TYPE_DIST_PROC); + + from = llnk->other.item; + reason = slnk->other.item; /* reason in other.item ... */ + ASSERT(is_pid(from) || is_internal_port(from)); + ASSERT(is_immed(reason)); + ASSERT(op == ERTS_SIG_Q_OP_EXIT_LINKED); + dlnk = erts_link_tree_key_delete(&ERTS_P_LINKS(c_p), llnk); + if (!dlnk) { + ignore = !0; /* Link no longer active; ignore... */ + ldp = NULL; + } + else { + Eterm pid; + ErtsMessage *mp; + ErtsProcLocks locks; + Uint hsz; + Eterm *hp; + ErlOffHeap *ohp; + ignore = 0; + if (dlnk == llnk) + dlnk = NULL; + else + ldp = NULL; + + ASSERT(is_immed(reason)); + + if (!(c_p->flags & F_TRAP_EXIT)) { + if (reason == am_normal) + ignore = !0; /* Ignore it... */ + else + exit = !0; /* Terminate... */ + } + else { + + /* + * Create and EXIT message and replace + * the original signal with the message... + */ + + locks = ERTS_PROC_LOCK_MAIN; + + hsz = 4 + NC_HEAP_SIZE(from); + + mp = erts_alloc_message_heap(c_p, &locks, hsz, &hp, &ohp); + + if (locks != ERTS_PROC_LOCK_MAIN) + erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN); + + pid = STORE_NC(&hp, ohp, from); + + ERL_MESSAGE_TERM(mp) = TUPLE3(hp, am_EXIT, pid, reason); + ERL_MESSAGE_TOKEN(mp) = NIL; +#ifdef USE_VM_PROBES + ERL_MESSAGE_DT_UTAG(mp) = NIL; +#endif + if (is_immed(pid)) + ERL_MESSAGE_FROM(mp) = pid; + else { + DistEntry *dep; + ASSERT(is_external_pid(pid)); + dep = external_pid_dist_entry(pid); + ERL_MESSAGE_FROM(mp) = dep->sysname; + } + + /* Replace original signal with the exit message... */ + convert_to_msg(c_p, sig, mp, next_nm_sig); + + cnt += 4; + + conv_msg = mp; + } + } + destroy = !0; + } + + if (ignore|exit) { + remove_nm_sig(c_p, sig, next_nm_sig); + if (exit) { + if (save) { + sig->data.attached = ERTS_MSG_COMBINED_HFRAG; + ERL_MESSAGE_TERM(sig) = xsigd->message; + erts_save_message_in_proc(c_p, sig); + } + /* Exit process... */ + erts_set_self_exiting(c_p, reason); + + cnt++; + } + } + + if (!exit) { + if (conv_msg) + erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN); + if (op == ERTS_SIG_Q_OP_EXIT_LINKED && tracing->procs) + getting_unlinked(c_p, from); + } + + if (destroy) { + cnt++; + if (type == ERTS_SIG_Q_TYPE_GEN_EXIT) { + sig->next = NULL; + erts_cleanup_messages(sig); + } + else { + if (ldp) + erts_link_release_both(ldp); + else { + if (dlnk) + erts_link_release(dlnk); + erts_link_release((ErtsLink *) sig); + } + } + } + + *exited = exit; + + return cnt; +} + +static ERTS_INLINE int +convert_prepared_down_message(Process *c_p, ErtsMessage *sig, + Eterm msg, ErtsMessage ***next_nm_sig) +{ + convert_prepared_sig_to_msg(c_p, sig, msg, next_nm_sig); + erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN); + return 1; +} + +static int +convert_to_down_message(Process *c_p, + ErtsMessage *sig, + ErtsMonitorData *mdp, + Uint16 mon_type, + ErtsMessage ***next_nm_sig) +{ + /* + * Create a 'DOWN' message and replace the signal + * with it... + */ + int cnt = 0; + Eterm node = am_undefined; + ErtsMessage *mp; + ErtsProcLocks locks; + Uint hsz; + Eterm *hp, ref, from, type, reason; + ErlOffHeap *ohp; + + ASSERT(mdp); + ASSERT((mdp->origin.flags & ERTS_ML_FLGS_SAME) + == (mdp->target.flags & ERTS_ML_FLGS_SAME)); + + hsz = 6; /* 5-tuple */ + + if (mdp->origin.flags & ERTS_ML_FLG_NAME) + hsz += 3; /* reg name 2-tuple */ + else { + ASSERT(is_pid(mdp->origin.other.item) + || is_internal_port(mdp->origin.other.item)); + hsz += NC_HEAP_SIZE(mdp->origin.other.item); + } + + ASSERT(is_ref(mdp->ref)); + hsz += NC_HEAP_SIZE(mdp->ref); + + locks = ERTS_PROC_LOCK_MAIN; + + /* reason is mdp->target.other.item */ + reason = mdp->target.other.item; + ASSERT(is_immed(reason)); + + mp = erts_alloc_message_heap(c_p, &locks, hsz, &hp, &ohp); + + if (locks != ERTS_PROC_LOCK_MAIN) + erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN); + + cnt += 4; + + ref = STORE_NC(&hp, ohp, mdp->ref); + + if (!(mdp->origin.flags & ERTS_ML_FLG_NAME)) { + from = STORE_NC(&hp, ohp, mdp->origin.other.item); + } + else { + ErtsMonitorDataExtended *mdep; + ASSERT(mdp->origin.flags & ERTS_ML_FLG_EXTENDED); + mdep = (ErtsMonitorDataExtended *) mdp; + ASSERT(is_atom(mdep->u.name)); + if (mdep->dist) + node = mdep->dist->nodename; + else + node = erts_this_dist_entry->sysname; + from = TUPLE2(hp, mdep->u.name, node); + hp += 3; + } + + ASSERT(mdp->origin.type == mon_type); + switch (mon_type) { + case ERTS_MON_TYPE_PORT: + type = am_port; + if (mdp->origin.other.item == am_undefined) { + /* failed by name... */ + ERL_MESSAGE_FROM(mp) = am_system; + } + else { + ASSERT(is_internal_port(mdp->origin.other.item)); + ERL_MESSAGE_FROM(mp) = mdp->origin.other.item; + } + break; + case ERTS_MON_TYPE_PROC: + type = am_process; + if (mdp->origin.other.item == am_undefined) { + /* failed by name... */ + ERL_MESSAGE_FROM(mp) = am_system; + } + else { + ASSERT(is_internal_pid(mdp->origin.other.item)); + ERL_MESSAGE_FROM(mp) = mdp->origin.other.item; + } + break; + case ERTS_MON_TYPE_DIST_PROC: + type = am_process; + if (node == am_undefined) { + ErtsMonitorDataExtended *mdep; + ASSERT(mdp->origin.flags & ERTS_ML_FLG_EXTENDED); + mdep = (ErtsMonitorDataExtended *) mdp; + ASSERT(mdep->dist); + node = mdep->dist->nodename; + } + ASSERT(is_atom(node) && node != am_undefined); + ERL_MESSAGE_FROM(mp) = node; + break; + default: + ERTS_INTERNAL_ERROR("Unexpected monitor type"); + type = am_undefined; + ERL_MESSAGE_FROM(mp) = am_undefined; + break; + } + + ERL_MESSAGE_TERM(mp) = TUPLE5(hp, am_DOWN, ref, + type, from, reason); + hp += 6; + + ERL_MESSAGE_TOKEN(mp) = NIL; +#ifdef USE_VM_PROBES + ERL_MESSAGE_DT_UTAG(mp) = NIL; +#endif + /* Replace original signal with the exit message... */ + convert_to_msg(c_p, sig, mp, next_nm_sig); + + cnt += 4; + + erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN); + + return cnt; +} + +static ERTS_INLINE int +convert_to_nodedown_messages(Process *c_p, + ErtsMessage *sig, + ErtsMonitorData *mdp, + ErtsMessage ***next_nm_sig) +{ + int cnt = 1; + Uint n; + ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp; + + ASSERT((mdp->origin.flags & ERTS_ML_FLGS_SAME) + == (mdp->target.flags & ERTS_ML_FLGS_SAME)); + ASSERT(mdp->origin.flags & ERTS_ML_FLG_EXTENDED); + + n = mdep->u.refc; + + if (n == 0) + remove_nm_sig(c_p, sig, next_nm_sig); + else { + Uint i; + ErtsMessage *nd_first = NULL; + ErtsMessage *nd_last = NULL; + ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN; + Eterm node = mdep->dist->nodename; + + ASSERT(is_atom(node)); + ASSERT(n > 0); + + for (i = 0; i < n; i++) { + ErtsMessage *mp; + ErlOffHeap *ohp; + Eterm *hp; + + mp = erts_alloc_message_heap(c_p, &locks, 3, &hp, &ohp); + + ERL_MESSAGE_TERM(mp) = TUPLE2(hp, am_nodedown, node); + ERL_MESSAGE_FROM(mp) = am_system; + ERL_MESSAGE_TOKEN(mp) = NIL; +#ifdef USE_VM_PROBES + ERL_MESSAGE_DT_UTAG(mp) = NIL; +#endif + mp->next = nd_first; + nd_first = mp; + if (!nd_last) + nd_last = mp; + cnt++; + } + + if (locks != ERTS_PROC_LOCK_MAIN) + erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN); + + /* Replace signal with 'nodedown' messages */ + convert_to_msgs(c_p, sig, n, nd_first, nd_last, next_nm_sig); + + erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN); + } + return cnt; +} + +static int +handle_nodedown(Process *c_p, + ErtsMessage *sig, + ErtsMonitorData *mdp, + ErtsMessage ***next_nm_sig) +{ + ErtsMonitorDataExtended *mdep = (ErtsMonitorDataExtended *) mdp; + ErtsMonitor *omon = &mdp->origin; + int not_in_subtab = !(omon->flags & ERTS_ML_FLG_IN_SUBTABLE); + int cnt = 1; + + ASSERT(erts_monitor_is_in_table(omon)); + + if (not_in_subtab & !mdep->uptr.node_monitors) + erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), omon); + else if (not_in_subtab) { + ErtsMonitor *sub_mon; + ErtsMonitorDataExtended *sub_mdep; + sub_mon = erts_monitor_list_last(mdep->uptr.node_monitors); + ASSERT(sub_mon); + erts_monitor_list_delete(&mdep->uptr.node_monitors, sub_mon); + sub_mon->flags &= ~ERTS_ML_FLG_IN_SUBTABLE; + sub_mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(sub_mon); + ASSERT(!sub_mdep->uptr.node_monitors); + sub_mdep->uptr.node_monitors = mdep->uptr.node_monitors; + mdep->uptr.node_monitors = NULL; + erts_monitor_tree_replace(&ERTS_P_MONITORS(c_p), omon, sub_mon); + cnt += 2; + } + else { + ErtsMonitorDataExtended *top_mdep; + ErtsMonitor *top_mon; + ASSERT(is_atom(omon->other.item)); + ASSERT(!mdep->uptr.node_monitors); + top_mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p), + omon->other.item); + ASSERT(top_mon); + top_mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(top_mon); + ASSERT(top_mdep->uptr.node_monitors); + erts_monitor_list_delete(&top_mdep->uptr.node_monitors, omon); + omon->flags &= ~ERTS_ML_FLG_IN_SUBTABLE; + cnt += 3; + } + + return cnt + convert_to_nodedown_messages(c_p, sig, mdp, next_nm_sig); +} + +static void +handle_persistent_mon_msg(Process *c_p, Uint16 type, + ErtsMonitor *mon, ErtsMessage *sig, + Eterm msg, ErtsMessage ***next_nm_sig) +{ + convert_prepared_sig_to_msg(c_p, sig, msg, next_nm_sig); + + switch (type) { + + case ERTS_MON_TYPE_TIME_OFFSET: + ASSERT(mon->type == ERTS_MON_TYPE_TIME_OFFSET); + break; + + case ERTS_MON_TYPE_NODES: { + ErtsMonitorDataExtended *mdep; + Uint n; + ASSERT(mon->type == ERTS_MON_TYPE_NODES); + mdep = (ErtsMonitorDataExtended *) erts_monitor_to_data(mon); + ERTS_ML_ASSERT(mdep->u.refc > 0); + n = mdep->u.refc; + n--; + if (n > 0) { + ErtsProcLocks locks = ERTS_PROC_LOCK_MAIN; + ErtsMessage *first = NULL, *prev, *last; + Uint hsz = size_object(msg); + Uint i; + + for (i = 0; i < n; i++) { + Eterm *hp; + ErlOffHeap *ohp; + + last = erts_alloc_message_heap(c_p, &locks, hsz, &hp, &ohp); + + if (!first) + first = last; + else + prev->next = last; + prev = last; + + ERL_MESSAGE_TERM(last) = copy_struct(msg, hsz, &hp, ohp); + +#ifdef USE_VM_PROBES + ASSERT(is_immed(ERL_MESSAGE_DT_UTAG(sig))); + ERL_MESSAGE_DT_UTAG(last) = ERL_MESSAGE_DT_UTAG(sig); +#endif + ASSERT(is_immed(ERL_MESSAGE_TOKEN(sig))); + ERL_MESSAGE_TOKEN(last) = ERL_MESSAGE_TOKEN(sig); + ASSERT(is_immed(ERL_MESSAGE_FROM(sig))); + ERL_MESSAGE_FROM(last) = ERL_MESSAGE_FROM(sig); + + } + if (locks != ERTS_PROC_LOCK_MAIN) + erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN); + insert_messages(c_p, &sig->next, first, last, n, next_nm_sig); + } + break; + } + + default: + ERTS_INTERNAL_ERROR("Invalid type"); + break; + } + + erts_proc_notify_new_message(c_p, ERTS_PROC_LOCK_MAIN); +} + +static void +group_leader_reply(Process *c_p, Eterm to, Eterm ref, int success) +{ + Process *rp = erts_proc_lookup(to); + + if (rp) { + ErtsProcLocks locks; + Uint sz; + Eterm *hp, msg, ref_cpy, result; + ErlOffHeap *ohp; + ErtsMessage *mp; + + ASSERT(is_internal_ref(ref)); + + locks = c_p == rp ? ERTS_PROC_LOCK_MAIN : 0; + sz = size_object(ref); + + mp = erts_alloc_message_heap(rp, &locks, sz+3, + &hp, &ohp); + + ref_cpy = copy_struct(ref, sz, &hp, ohp); + result = success ? am_true : am_badarg; + msg = TUPLE2(hp, ref_cpy, result); + + erts_queue_message(rp, locks, mp, msg, am_system); + + if (c_p == rp) + locks &= ~ERTS_PROC_LOCK_MAIN; + + if (locks) + erts_proc_unlock(rp, locks); + } +} + +static void +handle_group_leader(Process *c_p, ErtsSigGroupLeader *sgl) +{ + erts_aint_t flags; + + flags = erts_atomic_read_band_nob(&sgl->flags, ~ERTS_SIG_GL_FLG_ACTIVE); + if (flags & ERTS_SIG_GL_FLG_ACTIVE) { + int res = erts_set_group_leader(c_p, sgl->group_leader); + if (is_internal_pid(sgl->reply_to)) + group_leader_reply(c_p, sgl->reply_to, sgl->ref, res); + } + + flags = erts_atomic_read_band_nob(&sgl->flags, ~ERTS_SIG_GL_FLG_RECEIVER); + if ((flags & ~ERTS_SIG_GL_FLG_RECEIVER) == 0) + destroy_sig_group_leader(sgl); +} + + +/* + * Called in order to handle incoming signals. + */ + +int +erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep, + int *redsp, int max_reds, int local_only) +{ + Eterm tag; + erts_aint32_t state; + int cnt, limit, abs_lim, msg_tracing; + ErtsMessage *sig, ***next_nm_sig; + ErtsSigRecvTracing tracing; + + ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p); + ERTS_LC_ASSERT(ERTS_PROC_LOCK_MAIN == erts_proc_lc_my_proc_locks(c_p)); + + if (local_only) + state = -1; /* can never be a valid state... */ + else { + state = erts_atomic32_read_nob(&c_p->state); + if (ERTS_PSFLG_SIG_IN_Q & state) { + erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ); + erts_proc_sig_fetch(c_p); + erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ); + } + } + + limit = *redsp; + *redsp = 0; + + if (!c_p->sig_qs.cont) { + if (state == -1) + *statep = erts_atomic32_read_nob(&c_p->state); + else + *statep = state; + return !0; + } + + next_nm_sig = &c_p->sig_qs.nmsigs.next; + + setup_tracing_state(c_p, &tracing); + msg_tracing = tracing.messages.active; + + limit *= ERTS_SIG_REDS_CNT_FACTOR; + abs_lim = ERTS_SIG_REDS_CNT_FACTOR*max_reds; + if (limit > abs_lim) + limit = abs_lim; + + cnt = 0; + + do { + + if (msg_tracing) { + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + if (handle_msg_tracing(c_p, &tracing, next_nm_sig) != 0) { + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + break; /* tracing limit or end... */ + } + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + } + + if (!*next_nm_sig) + break; + + sig = **next_nm_sig; + + ASSERT(sig); + ASSERT(ERTS_SIG_IS_NON_MSG(sig)); + + tag = ((ErtsSignal *) sig)->common.tag; + + switch (ERTS_PROC_SIG_OP(tag)) { + + case ERTS_SIG_Q_OP_EXIT: + case ERTS_SIG_Q_OP_EXIT_LINKED: { + int exited; + + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + + cnt += handle_exit_signal(c_p, &tracing, sig, + next_nm_sig, &exited); + + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + + if (exited) + goto stop; /* terminated by signal */ + /* ignored or converted to exit message... */ + break; + } + + case ERTS_SIG_Q_OP_MONITOR_DOWN: { + Uint16 type = ERTS_PROC_SIG_TYPE(tag); + ErtsExitSignalData *xsigd = NULL; + ErtsMonitorData *mdp = NULL; + ErtsMonitor *omon = NULL, *tmon = NULL; + + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + + switch (type) { + case ERTS_MON_TYPE_DIST_PROC: + case ERTS_MON_TYPE_PROC: + case ERTS_MON_TYPE_PORT: + tmon = (ErtsMonitor *) sig; + ASSERT(erts_monitor_is_target(tmon)); + ASSERT(!erts_monitor_is_in_table(tmon)); + mdp = erts_monitor_to_data(tmon); + if (erts_monitor_is_in_table(&mdp->origin)) { + omon = &mdp->origin; + erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), + omon); + cnt += convert_to_down_message(c_p, sig, mdp, + type, next_nm_sig); + } + break; + case ERTS_SIG_Q_TYPE_GEN_EXIT: + xsigd = get_exit_signal_data(sig); + omon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p), + xsigd->u.ref); + if (omon) { + ASSERT(erts_monitor_is_origin(omon)); + if (omon->type == ERTS_MON_TYPE_DIST_PROC) { + mdp = erts_monitor_to_data(omon); + if (erts_monitor_dist_delete(&mdp->target)) + tmon = &mdp->target; + } + erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), + omon); + cnt += convert_prepared_down_message(c_p, sig, + xsigd->message, + next_nm_sig); + } + break; + case ERTS_MON_TYPE_NODE: + tmon = (ErtsMonitor *) sig; + ASSERT(erts_monitor_is_target(tmon)); + ASSERT(!erts_monitor_is_in_table(tmon)); + mdp = erts_monitor_to_data(tmon); + if (erts_monitor_is_in_table(&mdp->origin)) { + omon = &mdp->origin; + cnt += handle_nodedown(c_p, sig, mdp, next_nm_sig); + } + break; + default: + ERTS_INTERNAL_ERROR("invalid monitor type"); + break; + } + + if (omon) { + if (tmon) + erts_monitor_release_both(mdp); + else + erts_monitor_release(omon); + } + else { + remove_nm_sig(c_p, sig, next_nm_sig); + if (xsigd) { + sig->next = NULL; + erts_cleanup_messages(sig); + } + if (tmon) + erts_monitor_release(tmon); + } + + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + break; + } + + case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG: { + Uint16 type = ERTS_PROC_SIG_TYPE(tag); + ErtsMonitor *mon; + Eterm msg; + Eterm key; + + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + + key = get_persist_mon_msg(sig, &msg); + + cnt++; + mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p), key); + if (mon) { + ASSERT(erts_monitor_is_origin(mon)); + handle_persistent_mon_msg(c_p, type, mon, sig, + msg, next_nm_sig); + } + else { + cnt++; + remove_nm_sig(c_p, sig, next_nm_sig); + sig->next = NULL; + erts_cleanup_messages(sig); + } + + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + break; + } + + case ERTS_SIG_Q_OP_MONITOR: { + ErtsMonitor *mon = (ErtsMonitor *) sig; + + ASSERT(erts_monitor_is_target(mon)); + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + + remove_nm_sig(c_p, sig, next_nm_sig); + + if (mon->type == ERTS_MON_TYPE_DIST_PROC) + erts_monitor_tree_insert(&ERTS_P_MONITORS(c_p), mon); + else + erts_monitor_list_insert(&ERTS_P_LT_MONITORS(c_p), mon); + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + break; + } + + case ERTS_SIG_Q_OP_DEMONITOR: { + Uint16 type = ERTS_PROC_SIG_TYPE(tag); + + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + + remove_nm_sig(c_p, sig, next_nm_sig); + + if (type == ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR) { + ErtsMonitor *tmon; + ErtsSigDistProcDemonitor *dmon; + dmon = (ErtsSigDistProcDemonitor *) sig; + tmon = erts_monitor_tree_lookup(ERTS_P_MONITORS(c_p), dmon->ref); + destroy_dist_proc_demonitor(dmon); + cnt++; + if (tmon) { + ErtsMonitorData *mdp = erts_monitor_to_data(tmon); + ASSERT(erts_monitor_is_target(tmon)); + erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), tmon); + if (!erts_monitor_dist_delete(&mdp->origin)) + erts_monitor_release(tmon); + else + erts_monitor_release_both(mdp); + cnt += 2; + } + } + else { + ErtsMonitor *omon = (ErtsMonitor *) sig; + ErtsMonitorData *mdp = erts_monitor_to_data(omon); + ASSERT(omon->type == type); + ASSERT(erts_monitor_is_origin(omon)); + ASSERT(!erts_monitor_is_in_table(omon)); + if (!erts_monitor_is_in_table(&mdp->target)) + erts_monitor_release(omon); + else { + ErtsMonitor *tmon = &mdp->target; + ASSERT(tmon->type == type); + if (type == ERTS_MON_TYPE_DIST_PROC) + erts_monitor_tree_delete(&ERTS_P_MONITORS(c_p), tmon); + else { + erts_monitor_list_delete(&ERTS_P_LT_MONITORS(c_p), tmon); + if (type == ERTS_MON_TYPE_RESOURCE) { + erts_nif_demonitored((ErtsResource *) tmon->other.ptr); + cnt++; + } + } + erts_monitor_release_both(mdp); + cnt++; + } + cnt++; + } + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + break; + } + + case ERTS_SIG_Q_OP_LINK: { + ErtsLink *rlnk, *lnk = (ErtsLink *) sig; + + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + + remove_nm_sig(c_p, sig, next_nm_sig); + rlnk = erts_link_tree_insert_addr_replace(&ERTS_P_LINKS(c_p), + lnk); + if (!rlnk) { + if (tracing.procs) + getting_linked(c_p, lnk->other.item); + } + else { + if (rlnk->type != ERTS_LNK_TYPE_DIST_PROC) + erts_link_release(rlnk); + else { + ErtsLinkData *ldp; + ErtsLink *dlnk = erts_link_to_other(rlnk, &ldp); + if (erts_link_dist_delete(dlnk)) + erts_link_release_both(ldp); + else + erts_link_release(rlnk); + } + } + + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + break; + } + + case ERTS_SIG_Q_OP_UNLINK: { + Uint16 type = ERTS_PROC_SIG_TYPE(tag); + ErtsLinkData *ldp; + ErtsLink *llnk; + + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + + remove_nm_sig(c_p, sig, next_nm_sig); + if (type == ERTS_SIG_Q_TYPE_DIST_LINK) { + ErtsSigDistLinkOp *sdlnk = (ErtsSigDistLinkOp *) sig; + ASSERT(type == ERTS_SIG_Q_TYPE_DIST_LINK); + ASSERT(is_external_pid(sdlnk->remote)); + llnk = erts_link_tree_lookup(ERTS_P_LINKS(c_p), sdlnk->remote); + if (llnk) { + ErtsLink *dlnk = erts_link_to_other(llnk, &ldp); + erts_link_tree_delete(&ERTS_P_LINKS(c_p), llnk); + if (erts_link_dist_delete(dlnk)) + erts_link_release_both(ldp); + else + erts_link_release(llnk); + cnt += 8; + if (tracing.procs) + getting_unlinked(c_p, sdlnk->remote); + } + destroy_sig_dist_link_op(sdlnk); + cnt++; + } + else { + ErtsLinkData *ldp; + ErtsLink *dlnk, *slnk; + slnk = (ErtsLink *) sig; + llnk = erts_link_to_other(slnk, &ldp); + dlnk = erts_link_tree_key_delete(&ERTS_P_LINKS(c_p), llnk); + if (!dlnk) + erts_link_release(slnk); + else { + if (tracing.procs) + getting_unlinked(c_p, llnk->other.item); + if (dlnk == llnk) + erts_link_release_both(ldp); + else { + erts_link_release(slnk); + erts_link_release(dlnk); + } + } + cnt += 2; + } + + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + break; + } + + case ERTS_SIG_Q_OP_GROUP_LEADER: { + ErtsSigGroupLeader *sgl = (ErtsSigGroupLeader *) sig; + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + remove_nm_sig(c_p, sig, next_nm_sig); + handle_group_leader(c_p, sgl); + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + break; + } + + case ERTS_SIG_Q_OP_IS_ALIVE: + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + remove_nm_sig(c_p, sig, next_nm_sig); + is_alive_response(c_p, sig, !0); + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + break; + + case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE: { + Uint16 type = ERTS_PROC_SIG_TYPE(tag); + + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + msg_tracing = handle_trace_change_state(c_p, &tracing, + type, sig, + next_nm_sig); + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + + break; + } + + default: + ERTS_INTERNAL_ERROR("Unknown signal"); + break; + } + + cnt++; + + } while (cnt <= limit || stretch_limit(c_p, &tracing, abs_lim, &limit)); + +stop: { + int deferred_save, deferred_saved_last, res; + + deferred_saved_last = !!(c_p->flags & F_DEFERRED_SAVED_LAST); + deferred_save = 0; + + if (!deferred_saved_last) + deferred_save = 0; + else { + if (c_p->sig_qs.saved_last == &c_p->sig_qs.cont) { + c_p->sig_qs.saved_last = c_p->sig_qs.last; + c_p->flags &= ~F_DEFERRED_SAVED_LAST; + deferred_saved_last = deferred_save = 0; + } + else { + if (c_p->sig_qs.save == c_p->sig_qs.last) + deferred_save = !0; + else + deferred_save = 0; + } + } + + ASSERT(c_p->sig_qs.saved_last != &c_p->sig_qs.cont); + + if (ERTS_UNLIKELY(msg_tracing != 0)) { + /* + * All messages that has been traced should + * be moved to inner queue. Next signal in + * middle queue should either be next message + * to trace or next non-message signal. + */ + ASSERT(tracing.messages.next); + if (*next_nm_sig) { + if (*next_nm_sig == tracing.messages.next) + *next_nm_sig = &c_p->sig_qs.cont; + if (c_p->sig_qs.nmsigs.last == tracing.messages.next) + c_p->sig_qs.nmsigs.last = &c_p->sig_qs.cont; + *statep = erts_atomic32_read_nob(&c_p->state); + } + else { + ASSERT(!c_p->sig_qs.nmsigs.next); + c_p->sig_qs.nmsigs.last = NULL; + state = erts_atomic32_read_band_nob(&c_p->state, + ~ERTS_PSFLG_SIG_Q); + state &= ~ERTS_PSFLG_SIG_Q; + *statep = state; + } + + if (tracing.messages.next != &c_p->sig_qs.cont) { + *c_p->sig_qs.last = c_p->sig_qs.cont; + c_p->sig_qs.last = tracing.messages.next; + + c_p->sig_qs.cont = *tracing.messages.next; + if (!c_p->sig_qs.cont) + c_p->sig_qs.cont_last = &c_p->sig_qs.cont; + *c_p->sig_qs.last = NULL; + } + + res = !c_p->sig_qs.cont; + } + else if (*next_nm_sig) { + /* + * All messages prior to next non-message + * signal should be moved to inner queue. + * Next non-message signal to handle should + * be first in middle queue. + */ + ASSERT(**next_nm_sig); + if (*next_nm_sig != &c_p->sig_qs.cont) { + *c_p->sig_qs.last = c_p->sig_qs.cont; + c_p->sig_qs.last = *next_nm_sig; + + c_p->sig_qs.cont = **next_nm_sig; + if (c_p->sig_qs.nmsigs.last == *next_nm_sig) + c_p->sig_qs.nmsigs.last = &c_p->sig_qs.cont; + *next_nm_sig = &c_p->sig_qs.cont; + *c_p->sig_qs.last = NULL; + } + + ASSERT(c_p->sig_qs.cont); + + *statep = erts_atomic32_read_nob(&c_p->state); + + res = 0; + } + else { + /* + * All non-message signals handled. All + * messages should be moved to inner queue. + * Middle queue should be empty. + */ + ASSERT(!c_p->sig_qs.nmsigs.next); + c_p->sig_qs.nmsigs.last = NULL; + + if (c_p->sig_qs.cont_last != &c_p->sig_qs.cont) { + ASSERT(!*c_p->sig_qs.last); + *c_p->sig_qs.last = c_p->sig_qs.cont; + c_p->sig_qs.last = c_p->sig_qs.cont_last; + ASSERT(!*c_p->sig_qs.last); + + c_p->sig_qs.cont_last = &c_p->sig_qs.cont; + c_p->sig_qs.cont = NULL; + } + + ASSERT(!c_p->sig_qs.cont); + + state = erts_atomic32_read_band_nob(&c_p->state, + ~ERTS_PSFLG_SIG_Q); + state &= ~ERTS_PSFLG_SIG_Q; + *statep = state; + res = !0; + } + + if (deferred_saved_last + && (c_p->sig_qs.saved_last == &c_p->sig_qs.cont)) { + c_p->sig_qs.saved_last = c_p->sig_qs.last; + c_p->flags &= ~F_DEFERRED_SAVED_LAST; + if (deferred_save) + c_p->sig_qs.save = c_p->sig_qs.saved_last; + } + else if (!res) { + if (deferred_save) { + c_p->sig_qs.save = c_p->sig_qs.last; + ASSERT(!PEEK_MESSAGE(c_p)); + } + } + else { + c_p->flags &= ~F_DEFERRED_SAVED_LAST; + if (deferred_save) + c_p->sig_qs.save = c_p->sig_qs.saved_last; + } + + ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p); + + *redsp = cnt/4 + 1; + + return res; + } +} + +static int +stretch_limit(Process *c_p, ErtsSigRecvTracing *tp, + int abs_lim, int *limp) +{ + int lim; + /* + * Stretch limit up to a maximum of 'abs_lim' if + * there currently are no messages available to + * inspect by 'receive' and it might be possible + * to get messages available by processing + * signals (or trace messages). + */ + + lim = *limp; + ASSERT(abs_lim >= lim); + if (abs_lim == lim) + return 0; + + if (!(c_p->flags & F_DEFERRED_SAVED_LAST)) { + ErtsSignal *sig; + + if (PEEK_MESSAGE(c_p)) + return 0; + sig = (ErtsSignal *) c_p->sig_qs.cont; + if (!sig) + return 0; /* No signals to process available... */ + if (ERTS_SIG_IS_MSG(sig) && tp->messages.next != &c_p->sig_qs.cont) + return 0; + } + + lim += ERTS_SIG_REDS_CNT_FACTOR*100; + if (lim > abs_lim) + lim = abs_lim; + *limp = lim; + return !0; +} + + +int +erts_proc_sig_handle_exit(Process *c_p, int *redsp) +{ + int cnt, limit; + ErtsMessage *sig, ***next_nm_sig; + + ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p); + ERTS_LC_ASSERT(!erts_proc_lc_my_proc_locks(c_p)); + + ASSERT(!(ERTS_PSFLG_SIG_IN_Q & erts_atomic32_read_nob(&c_p->state))); + + limit = *redsp; + limit *= ERTS_SIG_REDS_CNT_FACTOR; + + *redsp = 1; + + next_nm_sig = &c_p->sig_qs.nmsigs.next; + + if (!*next_nm_sig) { + ASSERT(!c_p->sig_qs.nmsigs.last); + return !0; /* done... */ + } + + cnt = 0; + + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, NULL, next_nm_sig); + + do { + Eterm tag; + Uint16 type; + int op; + + sig = **next_nm_sig; + + ASSERT(sig); + ASSERT(ERTS_SIG_IS_NON_MSG(sig)); + + tag = ((ErtsSignal *) sig)->common.tag; + type = ERTS_PROC_SIG_TYPE(tag); + op = ERTS_PROC_SIG_OP(tag); + + remove_nm_sig(c_p, sig, next_nm_sig); + + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, NULL, next_nm_sig); + + cnt++; + + switch (op) { + + case ERTS_SIG_Q_OP_EXIT: + case ERTS_SIG_Q_OP_EXIT_LINKED: + case ERTS_SIG_Q_OP_MONITOR_DOWN: + switch (type) { + case ERTS_SIG_Q_TYPE_GEN_EXIT: + sig->next = NULL; + erts_cleanup_messages(sig); + break; + case ERTS_LNK_TYPE_PORT: + case ERTS_LNK_TYPE_PROC: + case ERTS_LNK_TYPE_DIST_PROC: + erts_link_release((ErtsLink *) sig); + break; + case ERTS_MON_TYPE_PORT: + case ERTS_MON_TYPE_PROC: + case ERTS_MON_TYPE_DIST_PROC: + case ERTS_MON_TYPE_NODE: + erts_monitor_release((ErtsMonitor *) sig); + break; + default: + ERTS_INTERNAL_ERROR("Unexpected sig type"); + break; + } + break; + + case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG: + sig->next = NULL; + erts_cleanup_messages(sig); + break; + + case ERTS_SIG_Q_OP_MONITOR: { + ErtsProcExitContext pectxt = {c_p, am_noproc}; + erts_proc_exit_handle_monitor((ErtsMonitor *) sig, + (void *) &pectxt); + cnt += 4; + break; + } + + case ERTS_SIG_Q_OP_DEMONITOR: + if (type == ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR) + destroy_dist_proc_demonitor((ErtsSigDistProcDemonitor *) sig); + else + erts_monitor_release((ErtsMonitor *) sig); + break; + + case ERTS_SIG_Q_OP_LINK: { + ErtsProcExitContext pectxt = {c_p, am_noproc}; + erts_proc_exit_handle_link((ErtsLink *) sig, (void *) &pectxt); + break; + } + + case ERTS_SIG_Q_OP_UNLINK: + if (type == ERTS_SIG_Q_TYPE_DIST_LINK) + destroy_sig_dist_link_op((ErtsSigDistLinkOp *) sig); + else + erts_link_release((ErtsLink *) sig); + break; + + case ERTS_SIG_Q_OP_GROUP_LEADER: { + ErtsSigGroupLeader *sgl = (ErtsSigGroupLeader *) sig; + handle_group_leader(c_p, sgl); + break; + } + + case ERTS_SIG_Q_OP_IS_ALIVE: + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + is_alive_response(c_p, sig, 0); + ERTS_PROC_SIG_HDBG_PRIV_CHKQ(c_p, &tracing, next_nm_sig); + break; + + case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE: + destroy_trace_info((ErtsSigTraceInfo *) sig); + break; + + default: + ERTS_INTERNAL_ERROR("Unknown signal"); + break; + } + + } while (cnt >= limit && *next_nm_sig); + + *redsp += cnt / ERTS_SIG_REDS_CNT_FACTOR; + + if (*next_nm_sig) + return 0; + + ASSERT(!c_p->sig_qs.nmsigs.next); + c_p->sig_qs.nmsigs.last = NULL; + (void) erts_atomic32_read_band_nob(&c_p->state, + ~ERTS_PSFLG_SIG_Q); + return !0; +} + +#ifdef USE_VM_PROBES +# define ERTS_CLEAR_SEQ_TOKEN(MP) \ + ERL_MESSAGE_TOKEN((MP)) = ((ERL_MESSAGE_DT_UTAG((MP)) != NIL) \ + ? am_have_dt_utag : NIL) +#else +# define ERTS_CLEAR_SEQ_TOKEN(MP) \ + ERL_MESSAGE_TOKEN((MP)) = NIL +#endif + +static ERTS_INLINE void +clear_seq_trace_token(ErtsMessage *sig) +{ + if (ERTS_SIG_IS_MSG((ErtsSignal *) sig)) + ERTS_CLEAR_SEQ_TOKEN(sig); + else { + Uint tag; + Uint16 op, type; + + tag = ((ErtsSignal *) sig)->common.tag; + type = ERTS_PROC_SIG_TYPE(tag); + op = ERTS_PROC_SIG_OP(tag); + + switch (op) { + + case ERTS_SIG_Q_OP_EXIT: + case ERTS_SIG_Q_OP_EXIT_LINKED: + case ERTS_SIG_Q_OP_MONITOR_DOWN: + switch (type) { + case ERTS_SIG_Q_TYPE_GEN_EXIT: + ERTS_CLEAR_SEQ_TOKEN(sig); + break; + case ERTS_LNK_TYPE_PORT: + case ERTS_LNK_TYPE_PROC: + case ERTS_LNK_TYPE_DIST_PROC: + case ERTS_MON_TYPE_PORT: + case ERTS_MON_TYPE_PROC: + case ERTS_MON_TYPE_DIST_PROC: + case ERTS_MON_TYPE_NODE: + break; + default: + ERTS_INTERNAL_ERROR("Unexpected sig type"); + break; + } + break; + + case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG: + ERTS_CLEAR_SEQ_TOKEN(sig); + break; + + case ERTS_SIG_Q_OP_MONITOR: + case ERTS_SIG_Q_OP_DEMONITOR: + case ERTS_SIG_Q_OP_LINK: + case ERTS_SIG_Q_OP_UNLINK: + case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE: + break; + + default: + ERTS_INTERNAL_ERROR("Unknown signal"); + break; + } + } +} + +void +erts_proc_sig_clear_seq_trace_tokens(Process *c_p) +{ + ASSERT(erts_thr_progress_is_blocking()); + erts_proc_sig_fetch(c_p); + ERTS_FOREACH_SIG_PRIVQS(c_p, sig, clear_seq_trace_token(sig)); +} + +Uint +erts_proc_sig_signal_size(ErtsSignal *sig) +{ + Eterm tag; + Uint16 type; + int op; + Uint size = 0; + + ASSERT(sig); + ASSERT(ERTS_SIG_IS_NON_MSG(sig)); + + tag = sig->common.tag; + type = ERTS_PROC_SIG_TYPE(tag); + op = ERTS_PROC_SIG_OP(tag); + + switch (op) { + case ERTS_SIG_Q_OP_EXIT: + case ERTS_SIG_Q_OP_EXIT_LINKED: + case ERTS_SIG_Q_OP_MONITOR_DOWN: + switch (type) { + case ERTS_SIG_Q_TYPE_GEN_EXIT: + size = ((ErtsMessage *) sig)->hfrag.alloc_size; + size *= sizeof(Eterm); + size += sizeof(ErtsMessage) - sizeof(Eterm); + break; + case ERTS_LNK_TYPE_PORT: + case ERTS_LNK_TYPE_PROC: + case ERTS_LNK_TYPE_DIST_PROC: + size = erts_link_size((ErtsLink *) sig); + break; + case ERTS_MON_TYPE_PORT: + case ERTS_MON_TYPE_PROC: + case ERTS_MON_TYPE_DIST_PROC: + case ERTS_MON_TYPE_NODE: + size = erts_monitor_size((ErtsMonitor *) sig); + default: + ERTS_INTERNAL_ERROR("Unexpected sig type"); + break; + } + break; + + case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG: + case ERTS_SIG_Q_OP_IS_ALIVE: + size = ((ErtsMessage *) sig)->hfrag.alloc_size; + size *= sizeof(Eterm); + size += sizeof(ErtsMessage) - sizeof(Eterm); + break; + + case ERTS_SIG_Q_OP_DEMONITOR: + if (type == ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR) { + size = NC_HEAP_SIZE(((ErtsSigDistProcDemonitor *) sig)->ref); + size--; + size *= sizeof(Eterm); + size += sizeof(ErtsSigDistProcDemonitor); + break; + } + /* Fall through... */ + + case ERTS_SIG_Q_OP_MONITOR: + size = erts_monitor_size((ErtsMonitor *) sig); + break; + + case ERTS_SIG_Q_OP_UNLINK: + if (type == ERTS_SIG_Q_TYPE_DIST_LINK) { + size = NC_HEAP_SIZE(((ErtsSigDistLinkOp *) sig)->remote); + size--; + size *= sizeof(Eterm); + size += sizeof(ErtsSigDistLinkOp); + break; + } + /* Fall through... */ + + case ERTS_SIG_Q_OP_LINK: + size = erts_link_size((ErtsLink *) sig); + break; + + case ERTS_SIG_Q_OP_GROUP_LEADER: { + ErtsSigGroupLeader *sgl = (ErtsSigGroupLeader *) sig; + size = size_object(sgl->group_leader); + size += size_object(sgl->ref); + size *= sizeof(Eterm); + size += sizeof(ErtsSigGroupLeader) - sizeof(Eterm); + break; + } + + case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE: + size = sizeof(ErtsSigTraceInfo); + break; + + default: + ERTS_INTERNAL_ERROR("Unknown signal"); + break; + } + + return size; +} + +int +erts_proc_sig_receive_helper(Process *c_p, + int fcalls, + int neg_o_reds, + ErtsMessage **msgpp, + int *get_outp) +{ + ErtsMessage *msgp; + int reds, consumed_reds, left_reds, max_reds; + + /* + * Called from the loop-rec instruction when receive + * has reached end of inner (private) queue. This function + * tries to move more messages into the inner queue + * for the receive to handle. This by, processing the + * middle (private) queue and/or moving signals from + * the outer (public) queue into the middle queue. + * + * If this function succeeds in making more messages + * available in the inner queue, *msgpp points to next + * message. If *msgpp is set to NULL when: + * -- process became exiting. *get_outp is set to a + * value greater than zero. + * -- process needs to yield. *get_outp is set to a + * value less than zero. + * -- no more messages exist in any of the queues. + * *get_outp is set to zero and the message queue + * lock remains locked. This so the process can + * make its way to the appropriate wait instruction + * without racing with new incoming messages. + */ + + ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN); + ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + ASSERT(!*msgpp); + + left_reds = fcalls - neg_o_reds; + consumed_reds = 0; + + while (!0) { + erts_aint32_t state; + + if (!c_p->sig_qs.cont) { + + consumed_reds += 4; + left_reds -= 4; + erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ); + if (c_p->sig_inq.first) + erts_proc_sig_fetch(c_p); + /* + * Messages may have been moved directly to + * inner queue... + */ + msgp = PEEK_MESSAGE(c_p); + if (msgp) { + erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ); + *get_outp = 0; + *msgpp = msgp; + return consumed_reds; + } + + if (!c_p->sig_qs.cont) { + /* + * No messages! Return with message queue + * locked and let the process continue + * to wait instruction... + */ + *get_outp = 0; + *msgpp = NULL; + return consumed_reds; + } + erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ); + + if (left_reds <= 0) { + *get_outp = -1; /* yield */ + *msgpp = NULL; + + ASSERT(consumed_reds >= (fcalls - neg_o_reds)); + return consumed_reds; + } + + /* handle newly arrived signals... */ + } + + reds = ERTS_SIG_HANDLE_REDS_MAX_PREFERED; +#ifdef DEBUG + /* test that it works also with very few reds */ + max_reds = left_reds; + if (reds > left_reds) + reds = left_reds; +#else + /* At least work preferred amount of reds... */ + max_reds = left_reds; + if (max_reds < reds) + max_reds = reds; +#endif + (void) erts_proc_sig_handle_incoming(c_p, &state, &reds, + max_reds, !0); + consumed_reds += reds; + left_reds -= reds; + /* we may have exited by an incoming signal... */ + if (state & ERTS_PSFLG_EXITING) { + /* + * Process need to schedule out in order + * to terminate. Prepare this a bit... + */ + ASSERT(c_p->flags & F_DELAY_GC); + + c_p->flags &= ~F_DELAY_GC; + c_p->arity = 0; + c_p->current = NULL; + *get_outp = 1; + *msgpp = NULL; + return consumed_reds; + } + + msgp = PEEK_MESSAGE(c_p); + if (msgp) { + *get_outp = 0; + *msgpp = msgp; + return consumed_reds; + } + + if (left_reds <= 0) { + *get_outp = -1; /* yield */ + *msgpp = NULL; + + ASSERT(consumed_reds >= (fcalls - neg_o_reds)); + return consumed_reds; + } + + ASSERT(!c_p->sig_qs.cont); + /* Go fetch again... */ + } +} + +static int +handle_trace_change_state(Process *c_p, + ErtsSigRecvTracing *tracing, + Uint16 type, + ErtsMessage *sig, + ErtsMessage ***next_nm_sig) +{ + ErtsSigTraceInfo *trace_info = (ErtsSigTraceInfo *) sig; + ErtsMessage **next = *next_nm_sig; + int msgs_active, old_msgs_active = !!tracing->messages.active; + + ASSERT(sig == *next); + + erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); + + ERTS_TRACE_FLAGS(c_p) |= trace_info->flags_on; + ERTS_TRACE_FLAGS(c_p) &= ~trace_info->flags_off; + if (is_value(trace_info->tracer)) + erts_tracer_replace(&c_p->common, trace_info->tracer); + + erts_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); + + remove_nm_sig(c_p, sig, next_nm_sig); + destroy_trace_info(trace_info); + /* + * Adjust tracing state according to modifications made by + * the trace info signal... + */ + adjust_tracing_state(c_p, tracing, 0); + msgs_active = !!tracing->messages.active; + + if (old_msgs_active ^ msgs_active) { + if (msgs_active) { + ASSERT(!tracing->messages.next); + tracing->messages.next = next; + } + else { + ASSERT(tracing->messages.next); + tracing->messages.next = NULL; + } + } + + ASSERT(!msgs_active || tracing->messages.next); + + return msgs_active; +} + +static void +getting_unlinked(Process *c_p, Eterm unlinker) +{ + trace_proc(c_p, ERTS_PROC_LOCK_MAIN, c_p, + am_getting_unlinked, unlinker); +} + +static void +getting_linked(Process *c_p, Eterm linker) +{ + trace_proc(c_p, ERTS_PROC_LOCK_MAIN, c_p, + am_getting_linked, linker); +} + +static ERTS_INLINE void +handle_message_enqueued_tracing(Process *c_p, + ErtsSigRecvTracing *tracing, + ErtsMessage *msg) +{ + ASSERT(ERTS_SIG_IS_INTERNAL_MSG(msg)); + +#if defined(USE_VM_PROBES) + if (tracing->messages.vm_probes && DTRACE_ENABLED(message_queued)) { + Sint tok_label = 0; + Sint tok_lastcnt = 0; + Sint tok_serial = 0; + Eterm seq_trace_token = ERL_MESSAGE_TOKEN(msg); + + if (seq_trace_token != NIL && is_tuple(seq_trace_token)) { + tok_label = SEQ_TRACE_T_DTRACE_LABEL(seq_trace_token); + tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(seq_trace_token)); + tok_serial = signed_val(SEQ_TRACE_T_SERIAL(seq_trace_token)); + } + /* Message intentionally not passed... */ + DTRACE6(message_queued, + tracing->messages.receiver_name, + size_object(ERL_MESSAGE_TERM(msg)), + c_p->sig_qs.len, + tok_label, tok_lastcnt, tok_serial); + } +#endif + + if (tracing->messages.receive_trace && tracing->messages.event->on) { + ASSERT(IS_TRACED(c_p)); + trace_receive(c_p, + ERL_MESSAGE_FROM(msg), + ERL_MESSAGE_TERM(msg), + tracing->messages.event); + } +} + +static int +handle_msg_tracing(Process *c_p, ErtsSigRecvTracing *tracing, + ErtsMessage ***next_nm_sig) +{ + ErtsMessage **next_sig, *sig; + int cnt = 0, limit = ERTS_PROC_SIG_TRACE_COUNT_LIMIT; + + ASSERT(tracing->messages.next); + next_sig = tracing->messages.next; + sig = *next_sig; + + /* + * Receive tracing active. Handle all messages + * until next non-message signal... + */ + + while (sig && ERTS_SIG_IS_MSG(sig)) { + if (cnt > limit) { + tracing->messages.next = next_sig; + return -1; /* Yield... */ + } + if (ERTS_SIG_IS_EXTERNAL_MSG(sig)) { + cnt++; + if (!erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN, + sig, 0)) { + /* Bad dist message; remove it... */ + remove_mq_m_sig(c_p, sig, next_sig, next_nm_sig); + sig = *next_sig; + continue; + } + } + handle_message_enqueued_tracing(c_p, tracing, sig); + cnt++; + + next_sig = &(*next_sig)->next; + sig = *next_sig; + } + + tracing->messages.next = next_sig; + + if (!sig) { + ASSERT(!*next_nm_sig); + return 1; /* end... */ + } + + ASSERT(*next_nm_sig); + ASSERT(**next_nm_sig == sig); + + /* Next signal a non-message signal... */ + ASSERT(ERTS_SIG_IS_NON_MSG(sig)); + + /* + * Return and handle the non-message signal... + */ + + return 0; +} + +/* + * ERTS_INSPECT_MSGQ_KEEP_OH_MSGS == 0 will move off heap messages + * into the heap of the inspected process if off_heap_message_queue + * is false when process_info(_, messages) is called. That is, the + * following GC will have more data in the rootset compared to the + * scenario when process_info(_, messages) had not been called. + * + * ERTS_INSPECT_MSGQ_KEEP_OH_MSGS != 0 will keep off heap messages + * off heap when process_info(_, messages) is called regardless of + * the off_heap_message_queue setting of the process. That is, it + * will change the following execution of the process as little as + * possible. + */ +#define ERTS_INSPECT_MSGQ_KEEP_OH_MSGS 1 + +Uint +erts_proc_sig_prep_msgq_for_inspection(Process *c_p, Process *rp, + ErtsProcLocks rp_locks, + ErtsMessageInfo *mip) +{ + Uint tot_heap_size; + ErtsMessage *mp, **mpp; + Sint i; + int self_on_heap; + + /* + * Prepare the message queue for inspection + * by process_info(). + * + * + * - Decode all messages on external format + * - Remove all corrupt dist messages from queue + * - Save pointer to, and heap size need of each + * message in the mip array. + * - Return total heap size need for all messages + * that needs to be copied. + * + * If ERTS_INSPECT_MSGQ_KEEP_OH_MSGS == 0: + * - In case off heap messages is disabled and + * we are inspecting our own queue, move all + * off heap data into the heap. + */ + + /* + * All non-message signals *need* to have been + * handled before calling this functions... + */ + ASSERT(!rp->sig_qs.cont); + ASSERT(!rp->sig_qs.nmsigs.next && !rp->sig_qs.nmsigs.last); + + self_on_heap = c_p == rp && !(c_p->flags & F_OFF_HEAP_MSGQ); + + tot_heap_size = 0; + i = 0; + mpp = &rp->sig_qs.first; + mp = rp->sig_qs.first; + while (mp) { + Eterm msg = ERL_MESSAGE_TERM(mp); + + mip[i].size = 0; + + if (ERTS_SIG_IS_EXTERNAL_MSG(mp)) { + /* decode it... */ + if (mp->data.attached) + erts_decode_dist_message(rp, rp_locks, mp, + ERTS_INSPECT_MSGQ_KEEP_OH_MSGS); + + msg = ERL_MESSAGE_TERM(mp); + + if (is_non_value(msg)) { + ErtsMessage *bad_mp = mp; + /* + * Bad distribution message; remove + * it from the queue... + */ + ASSERT(!mp->data.attached); + + ASSERT(*mpp == bad_mp); + + remove_iq_m_sig(rp, mp, mpp); + + mp = *mpp; + + bad_mp->next = NULL; + erts_cleanup_messages(bad_mp); + continue; + } + } + + ASSERT(is_value(msg)); + +#if ERTS_INSPECT_MSGQ_KEEP_OH_MSGS + if (is_not_immed(msg) && (!self_on_heap || mp->data.attached)) { + Uint sz = size_object(msg); + mip[i].size = sz; + tot_heap_size += sz; + } +#else + if (self_on_heap) { + if (mp->data.attached) { + ErtsMessage *tmp = NULL; + if (mp->data.attached != ERTS_MSG_COMBINED_HFRAG) { + erts_link_mbuf_to_proc(rp, mp->data.heap_frag); + mp->data.attached = NULL; + } + else { + /* + * Need to replace the message reference since + * we will get references to the message data + * from the heap... + */ + ErtsMessage **mpp; + tmp = erts_alloc_message(0, NULL); + sys_memcpy((void *) tmp->m, (void *) mp->m, + sizeof(Eterm)*ERL_MESSAGE_REF_ARRAY_SZ); + mpp = i == 0 ? &rp->sig_qs.first : &mip[i-1].msgp->next; + erts_msgq_replace_msg_ref(&rp->msg, tmp, mpp); + erts_save_message_in_proc(rp, mp); + mp = tmp; + } + } + } + else if (is_not_immed(msg)) { + Uint sz = size_object(msg); + mip[i].size = sz; + tot_heap_size += sz; + } + +#endif + + mip[i].msgp = mp; + i++; + mpp = &mp->next; + mp = mp->next; + } + + return tot_heap_size; +} + +static ERTS_INLINE void +move_msg_to_heap(Process *c_p, ErtsMessage *mp) +{ + /* + * We leave not yet decoded distribution messages + * as they are in the queue since it is not + * possible to determine a maximum size until + * actual decoding... + * + * We also leave combined messages as they are... + */ + if (ERTS_SIG_IS_INTERNAL_MSG(mp) + && mp->data.attached + && mp->data.attached != ERTS_MSG_COMBINED_HFRAG) { + ErlHeapFragment *bp; + + bp = erts_message_to_heap_frag(mp); + + if (bp->next) + erts_move_multi_frags(&c_p->htop, &c_p->off_heap, bp, + mp->m, ERL_MESSAGE_REF_ARRAY_SZ, 0); + else + erts_copy_one_frag(&c_p->htop, &c_p->off_heap, bp, + mp->m, ERL_MESSAGE_REF_ARRAY_SZ); + + mp->data.heap_frag = NULL; + free_message_buffer(bp); + } +} + +void +erts_proc_sig_move_msgs_to_heap(Process *c_p) +{ + ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p); + + ERTS_FOREACH_SIG_PRIVQS(c_p, sig, move_msg_to_heap(c_p, sig)); + + ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(c_p); +} + + +BIF_RETTYPE +erts_internal_dirty_process_handle_signals_1(BIF_ALIST_1) +{ + erts_aint32_t state, dirty, noproc; + int busy; + Process *rp; + + if (BIF_P != erts_dirty_process_signal_handler + && BIF_P != erts_dirty_process_signal_handler_high + && BIF_P != erts_dirty_process_signal_handler_max) + BIF_ERROR(BIF_P, EXC_NOTSUP); + + if (is_not_internal_pid(BIF_ARG_1)) + BIF_RET(am_false); + + rp = erts_proc_lookup_raw(BIF_ARG_1); + if (!rp) + BIF_RET(am_noproc); + + state = erts_atomic32_read_nob(&rp->state); + dirty = (state & (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)); + if (!dirty) + BIF_RET(am_normal); + + busy = erts_proc_trylock(rp, ERTS_PROC_LOCK_MAIN) == EBUSY; + + state = erts_atomic32_read_mb(&rp->state); + noproc = (state & ERTS_PSFLG_FREE); + dirty = (state & (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)); + + if (busy) { + if (noproc) + BIF_RET(am_noproc); + if (dirty) + BIF_RET(am_more); /* try again... */ + BIF_RET(am_normal); /* will handle signals itself... */ + } + else { + erts_aint32_t state; + int done; + Eterm res = am_false; + int reds = 0; + + if (noproc) + res = am_noproc; + else if (!dirty) + res = am_normal; /* will handle signals itself... */ + else { + reds = ERTS_BIF_REDS_LEFT(BIF_P); + done = erts_proc_sig_handle_incoming(rp, &state, &reds, + reds, 0); + if (done || (state & ERTS_PSFLG_EXITING)) + res = am_ok; + else + res = am_more; + } + + erts_proc_unlock(rp, ERTS_PROC_LOCK_MAIN); + + if (reds) + BUMP_REDS(BIF_P, reds); + + BIF_RET(res); + } +} + +static void +debug_foreach_sig_heap_frags(ErlHeapFragment *hfrag, + void (*oh_func)(ErlOffHeap *, void *), + void *arg) +{ + ErlHeapFragment *hf = hfrag; + while (hf) { + oh_func(&(hf->off_heap), arg); + hf = hf->next; + } +} + +static void +debug_foreach_sig_fake_oh(Eterm term, + void (*oh_func)(ErlOffHeap *, void *), + void *arg) +{ + if (is_external(term)) { + ErlOffHeap oh; + oh.overhead = 0; + oh.first = ((struct erl_off_heap_header *) + (char *) external_thing_ptr(term)); + ASSERT(!oh.first->next); + oh_func(&oh, arg); + } + +} + +void +erts_proc_sig_debug_foreach_sig(Process *c_p, + void (*msg_func)(ErtsMessage *, void *), + void (*oh_func)(ErlOffHeap *, void *), + void (*mon_func)(ErtsMonitor *, void *), + void (*lnk_func)(ErtsLink *, void *), + void *arg) +{ + ErtsMessage *queue[] = {c_p->sig_qs.first, c_p->sig_qs.cont, c_p->sig_inq.first}; + int qix; + + for (qix = 0; qix < sizeof(queue)/sizeof(queue[0]); qix++) { + ErtsMessage *sig; + for (sig = queue[qix]; sig; sig = sig->next) { + + if (ERTS_SIG_IS_MSG(sig)) + msg_func(sig, arg); + else { + Eterm tag; + Uint16 type; + int op; + + ASSERT(sig); + ASSERT(ERTS_SIG_IS_NON_MSG(sig)); + + tag = ((ErtsSignal *) sig)->common.tag; + type = ERTS_PROC_SIG_TYPE(tag); + op = ERTS_PROC_SIG_OP(tag); + + switch (op) { + + case ERTS_SIG_Q_OP_EXIT: + case ERTS_SIG_Q_OP_EXIT_LINKED: + case ERTS_SIG_Q_OP_MONITOR_DOWN: + switch (type) { + case ERTS_SIG_Q_TYPE_GEN_EXIT: + debug_foreach_sig_heap_frags(&sig->hfrag, oh_func, arg); + break; + case ERTS_LNK_TYPE_PORT: + case ERTS_LNK_TYPE_PROC: + case ERTS_LNK_TYPE_DIST_PROC: + lnk_func((ErtsLink *) sig, arg); + break; + case ERTS_MON_TYPE_PORT: + case ERTS_MON_TYPE_PROC: + case ERTS_MON_TYPE_DIST_PROC: + case ERTS_MON_TYPE_NODE: + mon_func((ErtsMonitor *) sig, arg); + break; + default: + ERTS_INTERNAL_ERROR("Unexpected sig type"); + break; + } + break; + + case ERTS_SIG_Q_OP_PERSISTENT_MON_MSG: + debug_foreach_sig_heap_frags(&sig->hfrag, oh_func, arg); + break; + + case ERTS_SIG_Q_OP_DEMONITOR: + if (type == ERTS_SIG_Q_TYPE_DIST_PROC_DEMONITOR) { + debug_foreach_sig_fake_oh(((ErtsSigDistProcDemonitor *) sig)->ref, + oh_func, arg); + break; + } + /* Fall through... */ + + case ERTS_SIG_Q_OP_MONITOR: + mon_func((ErtsMonitor *) sig, arg); + break; + + case ERTS_SIG_Q_OP_UNLINK: + if (type == ERTS_SIG_Q_TYPE_DIST_LINK) { + debug_foreach_sig_fake_oh(((ErtsSigDistLinkOp *) sig)->remote, + oh_func, arg); + break; + } + /* Fall through... */ + + case ERTS_SIG_Q_OP_LINK: + lnk_func((ErtsLink *) sig, arg); + break; + + case ERTS_SIG_Q_OP_GROUP_LEADER: { + ErtsSigGroupLeader *sgl = (ErtsSigGroupLeader *) sig; + oh_func(&sgl->oh, arg); + break; + } + + case ERTS_SIG_Q_OP_IS_ALIVE: + case ERTS_SIG_Q_OP_TRACE_CHANGE_STATE: + break; + + default: + ERTS_INTERNAL_ERROR("Unknown signal"); + break; + } + + } + } + } +} + +#ifdef ERTS_PROC_SIG_HARD_DEBUG + +static void +chk_eterm(Process *c_p, int privq, ErtsMessage *mp, Eterm term) +{ + ErlHeapFragment *bp; + Eterm *ptr = NULL; + + switch (primary_tag(term)) { + case TAG_PRIMARY_IMMED1: + return; + case TAG_PRIMARY_LIST: + ptr = list_val(term); + ERTS_ASSERT(!is_header(CAR(ptr))); + ERTS_ASSERT(!is_header(CDR(ptr))); + break; + case TAG_PRIMARY_BOXED: + ptr = boxed_val(term); + ERTS_ASSERT(is_header(*ptr)); + break; + case TAG_PRIMARY_HEADER: + default: + ERTS_INTERNAL_ERROR("Not valid term"); + break; + } + + if (erts_is_literal(term, ptr)) + return; + + for (bp = erts_message_to_heap_frag(mp); bp; bp = bp->next) { + if (bp->mem <= ptr && ptr < bp->mem + bp->used_size) + return; + bp = bp->next; + } + + ERTS_ASSERT(privq); + ASSERT(erts_dbg_within_proc(ptr, c_p, NULL)); +} + +static Sint +proc_sig_hdbg_check_queue(Process *proc, + int privq, + ErtsMessage **sig_next, + ErtsMessage **sig_last, + ErtsMessage **sig_nm_next, + ErtsMessage **sig_nm_last, + ErtsSigRecvTracing *tracing, + int *found_saved_last_p, + erts_aint32_t sig_psflg) +{ + ErtsMessage **next, *sig, **nm_next, **nm_last; + int last_nm_sig_found, nm_sigs = 0, found_next_trace = 0, + found_save = 0, last_sig_found = 0, found_saved_last = 0; + Sint msg_len = 0; + ErtsMessage **next_trace = tracing ? tracing->messages.next : NULL; + ErtsMessage **save = proc->sig_qs.save; + ErtsMessage **saved_last = proc->sig_qs.saved_last; + + + nm_next = sig_nm_next; + nm_last = sig_nm_last; + next = sig_next; + sig = *sig_next; + + last_nm_sig_found = !nm_last; + if (last_nm_sig_found) + ERTS_ASSERT(!nm_next); + else + ERTS_ASSERT(nm_next); + + while (1) { + ErtsSignal *nm_sig; + + if (next == sig_last) { + ASSERT(!*next); + last_sig_found = 1; + } + + if (next == save) + found_save = 1; + + if (next == saved_last) + found_saved_last = 1; + + if (next == next_trace) { + found_next_trace = 1; + ERTS_ASSERT(nm_sigs == 0); + } + + while (sig && ERTS_SIG_IS_MSG(sig)) { + int i; + if (ERTS_SIG_IS_EXTERNAL_MSG(sig)) + i = 1; + else + i = 0; + for (; i < ERL_MESSAGE_REF_ARRAY_SZ; i++) + chk_eterm(proc, privq, sig, sig->m[i]); + + msg_len++; + next = &sig->next; + sig = sig->next; + + if (next == sig_last) { + ASSERT(!*next); + last_sig_found = 1; + } + + if (next == save) + found_save = 1; + + if (next == saved_last) + found_saved_last = 1; + + if (next == next_trace) { + found_next_trace = 1; + ERTS_ASSERT(nm_sigs == 0); + } + } + + if (!sig) + break; + + nm_sigs++; + + ERTS_ASSERT(!last_nm_sig_found); + ERTS_ASSERT(ERTS_SIG_IS_NON_MSG(sig)); + + nm_sig = (ErtsSignal *) sig; + + ERTS_ASSERT(nm_next == next); + + if (nm_last == next) { + ASSERT(!nm_sig->common.specific.next); + last_nm_sig_found = 1; + } + + nm_next = nm_sig->common.specific.next; + next = &nm_sig->common.next; + sig = nm_sig->common.next; + + } + + if (!privq) { + /* outer queue */ + ERTS_ASSERT(!found_save); + ERTS_ASSERT(!found_saved_last); + } + else if (privq > 0) { + /* middle queue */ + ERTS_ASSERT(!next_trace || found_next_trace); + ERTS_ASSERT(!found_save); + if (!found_saved_last_p) { + ERTS_ASSERT(!found_saved_last + || (proc->flags & F_DEFERRED_SAVED_LAST)); + } + else { + if (*found_saved_last_p) { + ERTS_ASSERT(!found_saved_last); + ERTS_ASSERT(!(proc->flags & F_DEFERRED_SAVED_LAST)); + } + else if (saved_last) { + ERTS_ASSERT(found_saved_last); + ERTS_ASSERT(proc->flags & F_DEFERRED_SAVED_LAST); + } + *found_saved_last_p |= found_saved_last; + } + } + else { + /* inner queue */ + ERTS_ASSERT(!found_next_trace); + ERTS_ASSERT(nm_sigs == 0); + ERTS_ASSERT(found_save); + ERTS_ASSERT(!saved_last + || (found_saved_last + || (proc->flags & F_DEFERRED_SAVED_LAST))); + if (found_saved_last_p) + *found_saved_last_p |= found_saved_last; + } + + ERTS_ASSERT(last_nm_sig_found); + ERTS_ASSERT(last_sig_found); + + if (sig_psflg != ERTS_PSFLG_FREE) { + erts_aint32_t state = erts_atomic32_read_nob(&proc->state); + ERTS_ASSERT(nm_sigs ? !!(state & sig_psflg) : !(state & sig_psflg)); + } + + return msg_len; +} + +void +erts_proc_sig_hdbg_check_priv_queue(Process *p, char *what, char *file, int line) +{ + int found_saved_last = 0; + Sint len1, len2; + ERTS_LC_ASSERT(erts_thr_progress_is_blocking() + || ERTS_PROC_IS_EXITING(p) + || (ERTS_PROC_LOCK_MAIN + & erts_proc_lc_my_proc_locks(p))); + len1 = proc_sig_hdbg_check_queue(p, + -1, + &p->sig_qs.first, + p->sig_qs.last, + NULL, + NULL, + NULL, + &found_saved_last, + ERTS_PSFLG_FREE); + len2 = proc_sig_hdbg_check_queue(p, + 1, + &p->sig_qs.cont, + p->sig_qs.cont_last, + p->sig_qs.nmsigs.next, + p->sig_qs.nmsigs.last, + NULL, + &found_saved_last, + ERTS_PSFLG_SIG_Q); + if (p->sig_qs.saved_last) + ERTS_ASSERT(found_saved_last); + ERTS_ASSERT(p->sig_qs.len == len1 + len2); +} + +void +erts_proc_sig_hdbg_check_in_queue(Process *p, char *what, char *file, int line) +{ + Sint len; + ERTS_LC_ASSERT(erts_thr_progress_is_blocking() + || ERTS_PROC_IS_EXITING(p) + || (ERTS_PROC_LOCK_MSGQ + & erts_proc_lc_my_proc_locks(p))); + len = proc_sig_hdbg_check_queue(p, + 0, + &p->sig_inq.first, + p->sig_inq.last, + p->sig_inq.nmsigs.next, + p->sig_inq.nmsigs.last, + NULL, + NULL, + ERTS_PSFLG_SIG_IN_Q); + ASSERT(p->sig_inq.len == len); +} + +#endif diff --git a/erts/emulator/beam/erl_proc_sig_queue.h b/erts/emulator/beam/erl_proc_sig_queue.h new file mode 100644 index 0000000000..56fe3e683e --- /dev/null +++ b/erts/emulator/beam/erl_proc_sig_queue.h @@ -0,0 +1,690 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2018. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * %CopyrightEnd% + */ + +/* + * Description: Process signal queue implementation. + * + * Currently the following signals are handled: + * - Messages + * - Exit + * - Monitor + * - Demonitor + * - Monitor down + * - Persistent monitor message + * - Link + * - Unlink + * - Group leader + * - Is process alive + * - Trace change + * + * The signal queue consists of three parts: + * - Outer queue (sig_inq field in process struct) + * - Middle queue (sig_qs field in process struct) + * - Inner queue (sig_qs field in process struct) + * + * Incoming signals are placed in the outer queue + * by other processes, ports, or by the runtime system + * itself. This queue is protected by the msgq process + * lock and may be accessed by any other entity. While + * a signal is located in the outer queue, it is still + * in transit between sender and receiver. + * + * The middle and the inner queues are private to the + * receiving process and can only be accessed while + * holding the main process lock. The signal changes + * from being in transit to being received while in + * the middle queue. Non-message signals are handled + * immediately upon reception while message signals + * are moved into the inner queue. + * + * In the outer and middle queues both message signals + * and non-message signals are mixed. Signals in these + * queues are referenced using two single linked lists. + * One single linked list that go through all signals + * in the queue and another single linked list that + * goes through only non-message signals. The list + * through the non-message signals is used for fast + * access to these signals in the middle queue, since + * these should be handled immediately upon reception. + * + * The inner queue consists only of one single linked + * list through the message signals. A receive + * expression can only operate on messages once they + * have entered the inner queue. + * + * Author: Rickard Green + */ + +#ifndef ERTS_PROC_SIG_QUEUE_H_TYPE__ +#define ERTS_PROC_SIG_QUEUE_H_TYPE__ + +#if 0 +# define ERTS_PROC_SIG_HARD_DEBUG +#endif + +struct erl_mesg; + +typedef struct { + struct erl_mesg *next; + union { + struct erl_mesg **next; + void *attachment; + } specific; + Eterm tag; +} ErtsSignalCommon; + +#define ERTS_SIG_HANDLE_REDS_MAX_PREFERED (CONTEXT_REDS/40) + +#ifdef ERTS_PROC_SIG_HARD_DEBUG +# define ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(P) \ + ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__((P), "") +# define ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(P) \ + ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__((P), "") +# define ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__(P, What) \ + erts_proc_sig_hdbg_check_in_queue((P), (What), __FILE__, __LINE__) +# define ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__(P, What) \ + erts_proc_sig_hdbg_check_priv_queue((P), (What), __FILE__, __LINE__) +struct process; +void erts_proc_sig_hdbg_check_priv_queue(struct process *c_p, char *what, + char *file, int line); +void erts_proc_sig_hdbg_check_in_queue(struct process *c_p, char *what, + char *file, int line); +#else +# define ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(P) +# define ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(P) +# define ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__(P, What) +#define ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__(P, What) +#endif + +#endif + +#if !defined(ERTS_PROC_SIG_QUEUE_H__) && !defined(ERTS_PROC_SIG_QUEUE_TYPE_ONLY) +#define ERTS_PROC_SIG_QUEUE_H__ + +struct dist_entry_; + +/* + * Send operations of currently supported process signals follow... + */ + +/** + * + * @brief Send an exit signal to a process. + * + * + * @param[in] c_p Pointer to process struct of + * currently executing process. + * + * @param[in] from Identifier of sender. + * + * @param[in] to Identifier of local process + * to send signal to. + * + * @param[in] reason Exit reason. + * + * @param[in] token Seq trace token. + * + * @param[in] normal_kills If non-zero, also normal exit + * reason will kill the receiver + * if it is not trapping exit. + * + */ +void +erts_proc_sig_send_exit(Process *c_p, Eterm from, Eterm to, + Eterm reason, Eterm token, int normal_kills); + +/** + * + * @brief Send an exit signal due to broken link to a process. + * + * + * @param[in] c_p Pointer to process struct of + * currently executing process. + * + * @param[in] from Identifier of sender. + * + * @param[in] lnk Pointer to link structure + * from the sending side. It + * should contain information + * about receiver. + * + * @param[in] reason Exit reason. + * + * @param[in] token Seq trace token. + * + */ +void +erts_proc_sig_send_link_exit(Process *c_p, Eterm from, ErtsLink *lnk, + Eterm reason, Eterm token); + +/** + * + * @brief Send an link signal to a process. + * + * + * @param[in] c_p Pointer to process struct of + * currently executing process. + * + * @param[in] to Identifier of receiver. + * + * @param[in] lnk Pointer to link structure to + * insert on receiver side. + * + * @return A non-zero value if + * signal was successfully + * sent. If a zero, value + * the signal was not sent + * due to the receiver not + * existing. The sender + * needs to deallocate the + * link structure. + * + */ +int +erts_proc_sig_send_link(Process *c_p, Eterm to, ErtsLink *lnk); + +/** + * + * @brief Send an unlink signal to a process. + * + * + * @param[in] c_p Pointer to process struct of + * currently executing process. + * + * @param[in] lnk Pointer to link structure from + * the sending side. It should + * contain information about + * receiver. + */ +void +erts_proc_sig_send_unlink(Process *c_p, ErtsLink *lnk); + +/** + * + * @brief Send an exit signal due to broken link to a process. + * + * This function is used instead of erts_proc_sig_send_link_exit() + * when the signal arrives via the distribution and + * no link structure is available. + * + * @param[in] dep Distribution entry of channel + * that the signal arrived on. + * + * @param[in] from Identifier of sender. + * + * @param[in] to Identifier of receiver. + * + * @param[in] reason Exit reason. + * + * @param[in] token Seq trace token. + * + */ +void +erts_proc_sig_send_dist_link_exit(struct dist_entry_ *dep, + Eterm from, Eterm to, + Eterm reason, Eterm token); + +/** + * + * @brief Send an unlink signal to a process. + * + * This function is used instead of erts_proc_sig_send_unlink() + * when the signal arrives via the distribution and + * no link structure is available. + * + * @param[in] dep Distribution entry of channel + * that the signal arrived on. + * + * @param[in] from Identifier of sender. + * + * @param[in] to Identifier of receiver. + * + */ +void +erts_proc_sig_send_dist_unlink(struct dist_entry_ *dep, + Eterm from, Eterm to); + +/** + * + * @brief Send a monitor down signal to a process. + * + * @param[in] mon Pointer to target monitor + * structure from the sending + * side. It should contain + * information about receiver. + * + * @param[in] reason Exit reason. + * + */ +void +erts_proc_sig_send_monitor_down(ErtsMonitor *mon, Eterm reason); + +/** + * + * @brief Send a demonitor signal to a process. + * + * @param[in] mon Pointer to origin monitor + * structure from the sending + * side. It should contain + * information about receiver. + * + * @param[in] reason Exit reason. + * + */ +void +erts_proc_sig_send_demonitor(ErtsMonitor *mon); + +/** + * + * @brief Send a monitor signal to a process. + * + * @param[in] mon Pointer to target monitor + * structure to insert on + * receiver side. + * + * @param[in] to Identifier of receiver. + * + * @return A non-zero value if + * signal was successfully + * sent. If a zero, value + * the signal was not sent + * due to the receiver not + * existing. The sender + * needs to deallocate the + * monitor structure. + * + */ +int +erts_proc_sig_send_monitor(ErtsMonitor *mon, Eterm to); + +/** + * + * @brief Send a monitor down signal to a process. + * + * This function is used instead of erts_proc_sig_send_monitor_down() + * when the signal arrives via the distribution and + * no link structure is available. + * + * @param[in] dep Pointer to distribution entry + * of channel that the signal + * arrived on. + * + * @param[in] ref Reference identifying the monitor. + * + * @param[in] from Identifier of sender. + * + * @param[in] to Identifier of receiver. + * + * @param[in] reason Exit reason. + * + */ +void +erts_proc_sig_send_dist_monitor_down(DistEntry *dep, Eterm ref, + Eterm from, Eterm to, + Eterm reason); + +/** + * + * @brief Send a demonitor signal to a process. + * + * This function is used instead of erts_proc_sig_send_demonitor() + * when the signal arrives via the distribution and + * no monitor structure is available. + * + * @param[in] to Identifier of receiver. + * + * @param[in] ref Reference identifying the monitor. + * + */ +void +erts_proc_sig_send_dist_demonitor(Eterm to, Eterm ref); + +/** + * + * @brief Send a persistent monitor triggered signal to a process. + * + * Used by monitors that are not auto disabled such as for + * example 'time_offset' monitors. + * + * @param[in] type Monitor type. + * + * @param[in] key Monitor key. + * + * @param[in] from Identifier of sender. + * + * @param[in] to Identifier of receiver. + * + * @param[in] msg Message template. + * + * @param[in] msg_sz Heap size of message template. + * + */ +void +erts_proc_sig_send_persistent_monitor_msg(Uint16 type, Eterm key, + Eterm from, Eterm to, + Eterm msg, Uint msg_sz); + +/** + * + * @brief Send a trace change signal to a process. + * + * @param[in] to Identifier of receiver. + * + * @param[in] on Trace flags to enable. + * + * @param[in] off Trace flags to disable. + * + * @param[in] tracer Tracer to set. If the non-value, + * tracer will not be changed. + * + */ +void +erts_proc_sig_send_trace_change(Eterm to, Uint on, Uint off, + Eterm tracer); + +/** + * + * @brief Send a group leader signal to a process. + * + * Set group-leader of receiving process. If sent locally, + * a response message '{Ref, Result}' is sent to the original + * sender when performed where Ref is the reference passed + * as 'ref' argument, and Result is either 'true' or 'badarg'. + * + * @param[in] c_p Pointer to process struct of + * currently executing process. + * NULL if signal arrived via + * distribution. + * + * @param[in] to Identifier of receiver. + * + * @param[in] gl Identifier of new group leader. + * + * @param[in] ref Reference to use in response + * message to locally sending + * process (i.e., c_p when c_p + * is non-null). + * + */ +void +erts_proc_sig_send_group_leader(Process *c_p, Eterm to, Eterm gl, + Eterm ref); + +/** + * + * @brief Send an 'is process alive' signal to a process. + * + * A response message '{Ref, Result}' is sent to the + * sender when performed where Ref is the reference passed + * as 'ref' argument, and Result is either 'true' or 'false'. + * + * @param[in] c_p Pointer to process struct of + * currently executing process. + * NULL if signal arrived via + * distribution. + * + * @param[in] to Identifier of receiver. + * + * @param[in] ref Reference to use in response + * message to the sending + * process (i.e., c_p). + * + */ +void +erts_proc_sig_send_is_alive_request(Process *c_p, Eterm to, + Eterm ref); + +/* + * End of send operations of currently supported process signals. + */ + + +/** + * + * @brief Handle incoming signals. + * + * Called by an ordinary scheduler in order to handle incoming + * signals for a process. The work is done on the middle part + * of the signal queue. The maximum amount of signals handled + * is limited by the amount of reductions given when calling. + * Note that a reduction does not necessarily map to a signal. + * + * @param[in] c_p Pointer to process struct of + * currently executing process. + * + * @param[out] statep Pointer to process state after + * signal handling. May not be NULL. + * + * @param[in,out] redsp Pointer to an integer containing + * reductions. On input, the amount + * of preferred reductions to be + * used by the call. On output, the + * amount of reductions consumed. + * + * @param[in] max_reds Absolute maximum of reductions + * to use. If the process cannot + * make progress after the preferred + * amount of reductions has been + * consumed, signal handling may + * proceed up to a maximum of + * 'max_reds' in order to make + * the process able to proceed + * with other tasks after handling + * has finished. + * + * @param[in] local_only If is zero, new signals may be + * fetched from the outer queue and + * put in the middle queue before + * signal handling is performed. If + * non-zero, no new signals will be + * fetched before handling begins. + * + * @return Returns a non-zero value, when + * no more signals to handle in the + * middle queue remain. A zero + * return value means that there + * remains signals in the middle + * queue. + */ +int +erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep, + int *redsp, int max_reds, + int local_only); + +/** + * + * @brief Handle remaining signals for an exiting process + * + * Called as part of termination of a process. It will handle + * remaining signals. + * + * @param[in] c_p Pointer to process struct of + * currently executing process. + * + * @param[in,out] redsp Pointer to an integer containing + * reductions. On input, the amount + * of maximum reductions to be + * used by the call. On output, the + * amount of reductions consumed. + * + * @return Returns a non-zero value, when + * no more signals to handle in the + * middle queue remain. A zero + * return value means that there + * remains signals in the middle + * queue. + */ +int +erts_proc_sig_handle_exit(Process *c_p, int *redsp); + +/** + * + * @brief Helper for loop_rec instruction. + * + * This function should only be called from the loop_rec + * instruction (or equivalents). It is called when loop_rec + * reach the end of the inner queue (which is the only + * part of the signal queue that receive is allowed to + * operate on). When called, this function tries to make + * more messages available in the inner queue. This by + * fetching signals from the outer queue to the middle + * queue and/or processing signals in the middle queue. + * + * @param[in] c_p Pointer to process struct of + * currently executing process. + * + * @param[in] fcalls Content of FCALLS in + * process_main() + * + * @param[in] neg_o_reds Content of neg_o_reds in + * process_main() + * + * @param[out] msgpp Pointer to pointer to next + * available message to process. + * If *msgpp == NULL, no more + * messages are available. + * + * @param[out] get_outp Pointer to an integer + * indicating how to respond + * if no more messages are + * available (msgpp). If integer + * is set to zero, loop_rec + * should jump to an appropriate + * wait instruction. If zero, + * the message queue lock remain + * locked since the test for + * more messages was done. + * If the integer is set to a + * value larger that zero, the + * process exited. If the integer + * is set to a value less than + * zero, the process is required + * to yield. + * + * + * @return The amount of reductions + * consumed. + * + */ +int +erts_proc_sig_receive_helper(Process *c_p, int fcalls, + int neg_o_reds, ErtsMessage **msgpp, + int *get_outp); + +/** + * + * @brief Fetch signals from the outer queue + * + * Fetches signals from outer queue and places them in the + * middle queue ready for signal handling. If the middle + * queue is empty, only message signals were present in the + * outer queue, and no receive tracing has been enabled on + * the process, the middle queue is bypassed and messages + * are delivered directly to the inner queue instead. + * + * @param[in] c_p Pointer to process struct of + * currently executing process. + * + */ +void erts_proc_sig_fetch(Process *p); + +typedef struct { + Uint size; + ErtsMessage *msgp; +} ErtsMessageInfo; + +/** + * + * @brief Prepare signal queue for inspection by process_info() + * + * + * @param[in] c_p Pointer to process struct of + * currently executing process. + * + * @param[in] rp Pointer to process struct of + * process to inspect. + * + * @param[in] rp_locks Process locks held on 'rp'. + * + * @param[in] mip Pointer to array of + * ErtsMessageInfo structures. + * + */ + +Uint erts_proc_sig_prep_msgq_for_inspection(Process *c_p, + Process *rp, + ErtsProcLocks rp_locks, + ErtsMessageInfo *mip); + + +/** + * + * @brief Move message data of messages in private queues to heap + * + * Move message data of messages in private queues to the heap. + * This is part of GC of processes that uses on-heap message + * data. + * + * @param[in] c_p Pointer to process struct of + * currently executing process. + * + */ +void erts_proc_sig_move_msgs_to_heap(Process *c_p); + +/** + * + * @brief Size of signal in bytes. + * + * @param[in] sig Signal to inspect. + * + */ +Uint erts_proc_sig_signal_size(ErtsSignal *sig); + + +/** + * + * @brief Clear seq trace tokens on all signals + * + * Assumes thread progress has been blocked! + * + * @param[in] c_p Pointer to process + * + */ +void +erts_proc_sig_clear_seq_trace_tokens(Process *c_p); + +/** + * @brief Initialize this functionality + */ +void erts_proc_sig_queue_init(void); + +void +erts_proc_sig_debug_foreach_sig(Process *c_p, + void (*msg_func)(ErtsMessage *, void *), + void (*oh_func)(ErlOffHeap *, void *), + void (*mon_func)(ErtsMonitor *, void *), + void (*lnk_func)(ErtsLink *, void *), + void *arg); + +extern Process *erts_dirty_process_signal_handler; +extern Process *erts_dirty_process_signal_handler_high; +extern Process *erts_dirty_process_signal_handler_max; + +#endif /* ERTS_PROC_SIG_QUEUE_H__ */ diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c index 23fe353495..be1306cd79 100644 --- a/erts/emulator/beam/erl_process.c +++ b/erts/emulator/beam/erl_process.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2017. All Rights Reserved. + * Copyright Ericsson AB 1996-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,6 +53,7 @@ #include "erl_nfunc_sched.h" #include "erl_check_io.h" #include "erl_poll.h" +#include "erl_proc_sig_queue.h" #define ERTS_CHECK_TIME_REDS CONTEXT_REDS #define ERTS_DELAYED_WAKEUP_INFINITY (~(Uint64) 0) @@ -171,11 +172,19 @@ int erts_dio_sched_thread_suggested_stack_size = -1; ErtsLcPSDLocks erts_psd_required_locks[ERTS_PSD_SIZE]; #endif -static struct ErtsSchedBusyWait_ { +typedef struct { int aux_work; int tse; int sys_schedule; -} sched_busy_wait; +} ErtsBusyWaitParams; + +static ErtsBusyWaitParams sched_busy_wait_params[ERTS_SCHED_TYPE_LAST + 1]; + +static ERTS_INLINE ErtsBusyWaitParams * +sched_get_busy_wait_params(ErtsSchedulerData *esdp) +{ + return &sched_busy_wait_params[esdp->type]; +} int erts_disable_proc_not_running_opt; @@ -431,7 +440,8 @@ typedef enum { ERTS_PSTT_CLA, /* Copy Literal Area */ ERTS_PSTT_COHMQ, /* Change off heap message queue */ ERTS_PSTT_FTMQ, /* Flush trace msg queue */ - ERTS_PSTT_ETS_FREE_FIXATION + ERTS_PSTT_ETS_FREE_FIXATION, + ERTS_PSTT_PRIO_SIG /* Elevate prio on signal management */ } ErtsProcSysTaskType; #define ERTS_MAX_PROC_SYS_TASK_ARGS 2 @@ -579,7 +589,6 @@ dbg_chk_aux_work_val(erts_aint32_t value) valid |= ERTS_SSI_AUX_WORK_CNCLD_TMRS; valid |= ERTS_SSI_AUX_WORK_CNCLD_TMRS_THR_PRGR; valid |= ERTS_SSI_AUX_WORK_THR_PRGR_LATER_OP; - valid |= ERTS_SSI_AUX_WORK_PENDING_EXITERS; #if HAVE_ERTS_MSEG valid |= ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK; #endif @@ -601,7 +610,6 @@ dbg_chk_aux_work_val(erts_aint32_t value) #define ERTS_DBG_CHK_SSI_AUX_WORK(SSI) #endif -static void do_handle_pending_exiters(ErtsProcList *); static void wake_scheduler(ErtsRunQueue *rq); #if defined(ERTS_ENABLE_LOCK_CHECK) @@ -666,8 +674,6 @@ erts_pre_init_process(void) = "MISC_THR_PRGR"; erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_MISC_IX] = "MISC"; - erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_PENDING_EXITERS_IX] - = "PENDING_EXITERS"; erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_SET_TMO_IX] = "SET_TMO"; erts_aux_work_flag_descr[ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX] @@ -2531,27 +2537,6 @@ handle_mseg_cache_check(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiti static ERTS_INLINE erts_aint32_t -handle_pending_exiters(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting) -{ - ErtsProcList *pnd_xtrs; - ErtsRunQueue *rq; - - rq = awdp->esdp->run_queue; - unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_PENDING_EXITERS); - - erts_runq_lock(rq); - pnd_xtrs = rq->procs.pending_exiters; - rq->procs.pending_exiters = NULL; - erts_runq_unlock(rq); - - if (erts_proclist_fetch(&pnd_xtrs, NULL)) - do_handle_pending_exiters(pnd_xtrs); - - return aux_work & ~ERTS_SSI_AUX_WORK_PENDING_EXITERS; -} - - -static ERTS_INLINE erts_aint32_t handle_setup_aux_work_timer(ErtsAuxWorkData *awdp, erts_aint32_t aux_work, int waiting) { unset_aux_work_flags(awdp->ssi, ERTS_SSI_AUX_WORK_SET_TMO); @@ -2632,9 +2617,6 @@ handle_aux_work(ErtsAuxWorkData *awdp, erts_aint32_t orig_aux_work, int waiting) HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_MISC, handle_misc_aux_work); - HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_PENDING_EXITERS, - handle_pending_exiters); - HANDLE_AUX_WORK(ERTS_SSI_AUX_WORK_SET_TMO, handle_setup_aux_work_timer); @@ -3288,7 +3270,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) erts_runq_unlock(rq); - spincount = sched_busy_wait.tse; + spincount = sched_get_busy_wait_params(esdp)->tse; if (ERTS_SCHEDULER_IS_DIRTY(esdp)) dirty_sched_wall_time_change(esdp, working = 0); @@ -3390,7 +3372,7 @@ scheduler_wait(int *fcalls, ErtsSchedulerData *esdp, ErtsRunQueue *rq) } flgs = sched_prep_cont_spin_wait(ssi); - spincount = sched_busy_wait.aux_work; + spincount = sched_get_busy_wait_params(esdp)->aux_work; if (!(flgs & ERTS_SSI_FLG_WAITING)) { ASSERT(!(flgs & ERTS_SSI_FLG_SLEEPING)); @@ -5276,24 +5258,35 @@ typedef enum { #define ERTS_WAKEUP_OTHER_FIXED_INC_LEGACY (CONTEXT_REDS/10) -static struct { +typedef struct { ErtsSchedWakeupOtherThreshold threshold; ErtsSchedWakeupOtherType type; int limit; int dec_shift; int dec_mask; void (*check)(ErtsRunQueue *rq, Uint32 flags); -} wakeup_other; +} ErtsWakeupOtherParams; + +static ErtsWakeupOtherParams sched_wakeup_other_params[ERTS_SCHED_TYPE_LAST + 1]; + +static ERTS_INLINE ErtsWakeupOtherParams * +runq_get_wakeup_other_params(ErtsRunQueue *rq) +{ + ErtsSchedulerData *esdp = rq->scheduler; + return &sched_wakeup_other_params[esdp->type]; +} static void wakeup_other_check(ErtsRunQueue *rq, Uint32 flags) { + ErtsWakeupOtherParams *wo_params = runq_get_wakeup_other_params(rq); int wo_reds = rq->wakeup_other_reds; + if (wo_reds) { int left_len = erts_atomic32_read_dirty(&rq->len) - 1; if (left_len < 1) { - int wo_reduce = wo_reds << wakeup_other.dec_shift; - wo_reduce &= wakeup_other.dec_mask; + int wo_reduce = wo_reds << wo_params->dec_shift; + wo_reduce &= wo_params->dec_mask; rq->wakeup_other -= wo_reduce; if (rq->wakeup_other < 0) rq->wakeup_other = 0; @@ -5301,7 +5294,7 @@ wakeup_other_check(ErtsRunQueue *rq, Uint32 flags) else { rq->wakeup_other += (left_len*wo_reds + ERTS_WAKEUP_OTHER_FIXED_INC); - if (rq->wakeup_other > wakeup_other.limit) { + if (rq->wakeup_other > wo_params->limit) { if (ERTS_RUNQ_IX_IS_DIRTY(rq->ix)) { if (rq->waiting) { wake_dirty_scheduler(rq); @@ -5323,42 +5316,44 @@ wakeup_other_check(ErtsRunQueue *rq, Uint32 flags) } static void -wakeup_other_set_limit(void) +wakeup_other_set_limit(ErtsWakeupOtherParams *params) { - switch (wakeup_other.threshold) { + switch (params->threshold) { case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH: - wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH; - wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_VERY_HIGH; + params->limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH; + params->dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_VERY_HIGH; break; case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_HIGH: - wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_HIGH; - wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_HIGH; + params->limit = ERTS_WAKEUP_OTHER_LIMIT_HIGH; + params->dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_HIGH; break; case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM: - wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_MEDIUM; - wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_MEDIUM; + params->limit = ERTS_WAKEUP_OTHER_LIMIT_MEDIUM; + params->dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_MEDIUM; break; case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_LOW: - wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_LOW; - wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_LOW; + params->limit = ERTS_WAKEUP_OTHER_LIMIT_LOW; + params->dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_LOW; break; case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW: - wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW; - wakeup_other.dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_VERY_LOW; + params->limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW; + params->dec_shift = ERTS_WAKEUP_OTHER_DEC_SHIFT_VERY_LOW; break; } - if (wakeup_other.dec_shift < 0) - wakeup_other.dec_mask = (1 << (sizeof(wakeup_other.dec_mask)*8 - + wakeup_other.dec_shift)) - 1; + + if (params->dec_shift < 0) + params->dec_mask = (1 << (sizeof(params->dec_mask)*8 + + params->dec_shift)) - 1; else { - wakeup_other.dec_mask = 0; - wakeup_other.dec_mask = ~wakeup_other.dec_mask; + params->dec_mask = 0; + params->dec_mask = ~params->dec_mask; } } static void wakeup_other_check_legacy(ErtsRunQueue *rq, Uint32 flags) { + ErtsWakeupOtherParams *wo_params = runq_get_wakeup_other_params(rq); int wo_reds = rq->wakeup_other_reds; if (wo_reds) { erts_aint32_t len = erts_atomic32_read_dirty(&rq->len); @@ -5367,7 +5362,7 @@ wakeup_other_check_legacy(ErtsRunQueue *rq, Uint32 flags) if (rq->wakeup_other < 0) rq->wakeup_other = 0; } - else if (rq->wakeup_other < wakeup_other.limit) + else if (rq->wakeup_other < wo_params->limit) rq->wakeup_other += len*wo_reds + ERTS_WAKEUP_OTHER_FIXED_INC_LEGACY; else { if (flags & ERTS_RUNQ_FLG_PROTECTED) @@ -5383,23 +5378,23 @@ wakeup_other_check_legacy(ErtsRunQueue *rq, Uint32 flags) } static void -wakeup_other_set_limit_legacy(void) +wakeup_other_set_limit_legacy(ErtsWakeupOtherParams *params) { - switch (wakeup_other.threshold) { + switch (params->threshold) { case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH: - wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH_LEGACY; + params->limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_HIGH_LEGACY; break; case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_HIGH: - wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_HIGH_LEGACY; + params->limit = ERTS_WAKEUP_OTHER_LIMIT_HIGH_LEGACY; break; case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM: - wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_MEDIUM_LEGACY; + params->limit = ERTS_WAKEUP_OTHER_LIMIT_MEDIUM_LEGACY; break; case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_LOW: - wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_LOW_LEGACY; + params->limit = ERTS_WAKEUP_OTHER_LIMIT_LOW_LEGACY; break; case ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW: - wakeup_other.limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW_LEGACY; + params->limit = ERTS_WAKEUP_OTHER_LIMIT_VERY_LOW_LEGACY; break; } } @@ -5407,15 +5402,21 @@ wakeup_other_set_limit_legacy(void) static void set_wakeup_other_data(void) { - switch (wakeup_other.type) { - case ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT: - wakeup_other.check = wakeup_other_check; - wakeup_other_set_limit(); - break; - case ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY: - wakeup_other.check = wakeup_other_check_legacy; - wakeup_other_set_limit_legacy(); - break; + ErtsSchedType type; + + for (type = ERTS_SCHED_TYPE_FIRST; type <= ERTS_SCHED_TYPE_LAST; type++) { + ErtsWakeupOtherParams *params = &sched_wakeup_other_params[type]; + + switch (params->type) { + case ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT: + params->check = wakeup_other_check; + wakeup_other_set_limit(params); + break; + case ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY: + params->check = wakeup_other_check_legacy; + wakeup_other_set_limit_legacy(params); + break; + } } } @@ -5470,56 +5471,64 @@ runq_supervisor(void *unused) void erts_early_init_scheduling(int no_schedulers) { + ErtsSchedType type; + aux_work_timeout_early_init(no_schedulers); - wakeup_other.threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM; - wakeup_other.type = ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT; - sched_busy_wait.sys_schedule = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM; - sched_busy_wait.tse = (ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM - * ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT); - sched_busy_wait.aux_work = (ERTS_SCHED_SYS_SLEEP_SPINCOUNT_MEDIUM - * ERTS_SCHED_AUX_WORK_SLEEP_SPINCOUNT_FACT_MEDIUM); + + for (type = ERTS_SCHED_TYPE_FIRST; type <= ERTS_SCHED_TYPE_LAST; type++) { + erts_sched_set_wakeup_other_threshold(type, "medium"); + erts_sched_set_wakeup_other_type(type, "default"); + + erts_sched_set_busy_wait_threshold(type, "medium"); + } + + erts_sched_set_busy_wait_threshold(ERTS_SCHED_DIRTY_CPU, "short"); + erts_sched_set_busy_wait_threshold(ERTS_SCHED_DIRTY_IO, "short"); } int -erts_sched_set_wakeup_other_thresold(char *str) -{ - ErtsSchedWakeupOtherThreshold threshold; - if (sys_strcmp(str, "very_high") == 0) - threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH; - else if (sys_strcmp(str, "high") == 0) - threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_HIGH; - else if (sys_strcmp(str, "medium") == 0) - threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM; - else if (sys_strcmp(str, "low") == 0) - threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_LOW; - else if (sys_strcmp(str, "very_low") == 0) - threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW; - else - return EINVAL; - wakeup_other.threshold = threshold; - set_wakeup_other_data(); +erts_sched_set_wakeup_other_threshold(ErtsSchedType sched_type, char *str) +{ + ErtsWakeupOtherParams *params = &sched_wakeup_other_params[sched_type]; + + if (sys_strcmp(str, "very_high") == 0) { + params->threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_HIGH; + } else if (sys_strcmp(str, "high") == 0) { + params->threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_HIGH; + } else if (sys_strcmp(str, "medium") == 0) { + params->threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_MEDIUM; + } else if (sys_strcmp(str, "low") == 0) { + params->threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_LOW; + } else if (sys_strcmp(str, "very_low") == 0) { + params->threshold = ERTS_SCHED_WAKEUP_OTHER_THRESHOLD_VERY_LOW; + } else { + return EINVAL; + } + return 0; } int -erts_sched_set_wakeup_other_type(char *str) +erts_sched_set_wakeup_other_type(ErtsSchedType sched_type, char *str) { - ErtsSchedWakeupOtherType type; - if (sys_strcmp(str, "default") == 0) - type = ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT; - else if (sys_strcmp(str, "legacy") == 0) - type = ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY; - else - return EINVAL; - wakeup_other.type = type; + ErtsWakeupOtherParams *params = &sched_wakeup_other_params[sched_type]; + + if (sys_strcmp(str, "default") == 0) { + params->type = ERTS_SCHED_WAKEUP_OTHER_TYPE_DEFAULT; + } else if (sys_strcmp(str, "legacy") == 0) { + params->type = ERTS_SCHED_WAKEUP_OTHER_TYPE_LEGACY; + } else { + return EINVAL; + } + return 0; } int -erts_sched_set_busy_wait_threshold(char *str) +erts_sched_set_busy_wait_threshold(ErtsSchedType sched_type, char *str) { - int sys_sched; - int aux_work_fact; + ErtsBusyWaitParams *params = &sched_busy_wait_params[sched_type]; + int aux_work_fact, sys_sched; if (sys_strcmp(str, "very_long") == 0) { sys_sched = ERTS_SCHED_SYS_SLEEP_SPINCOUNT_VERY_LONG; @@ -5549,9 +5558,9 @@ erts_sched_set_busy_wait_threshold(char *str) return EINVAL; } - sched_busy_wait.sys_schedule = sys_sched; - sched_busy_wait.tse = sys_sched*ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT; - sched_busy_wait.aux_work = sys_sched*aux_work_fact; + params->sys_schedule = sys_sched; + params->tse = sys_sched * ERTS_SCHED_TSE_SLEEP_SPINCOUNT_FACT; + params->aux_work = sys_sched * aux_work_fact; return 0; } @@ -5793,7 +5802,6 @@ erts_init_scheduling(int no_schedulers, int no_schedulers_online, int no_poll_th rq->wakeup_other = 0; rq->wakeup_other_reds = 0; - rq->procs.pending_exiters = NULL; rq->procs.context_switches = 0; rq->procs.reductions = 0; @@ -6328,11 +6336,36 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, int enqueue; /* < 0 -> use proxy */ ErtsRunQueue* runq; + ASSERT(!(state & (ERTS_PSFLG_DIRTY_IO_PROC + |ERTS_PSFLG_DIRTY_CPU_PROC)) + || (BeamIsOpCode(*p->i, op_call_nif) + || BeamIsOpCode(*p->i, op_apply_bif))); + + a = state; + + /* Clear activ-sys if needed... */ + while (1) { + n = e = a; + if (a & ERTS_PSFLG_ACTIVE_SYS) { + if (a & (ERTS_PSFLG_SIG_Q + | ERTS_PSFLG_SIG_IN_Q + | ERTS_PSFLG_SYS_TASKS)) + break; + /* Clear active-sys */ + n &= ~ERTS_PSFLG_ACTIVE_SYS; + } + a = erts_atomic32_cmpxchg_nob(&p->state, n, e); + if (a == e) { + a = n; + break; + } + } + if (!is_normal_sched) running_flgs = ERTS_PSFLG_DIRTY_RUNNING|ERTS_PSFLG_DIRTY_RUNNING_SYS; else { running_flgs = ERTS_PSFLG_RUNNING|ERTS_PSFLG_RUNNING_SYS; - if (state & ERTS_PSFLG_DIRTY_ACTIVE_SYS + if ((a & ERTS_PSFLG_DIRTY_ACTIVE_SYS) && (p->flags & (F_DELAY_GC|F_DISABLE_GC))) { /* * Delay dirty GC; will be enabled automatically @@ -6345,14 +6378,12 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, */ ASSERT(!(p->flags & (F_DIRTY_CLA | F_DIRTY_GC_HIBERNATE))); - state = erts_atomic32_read_band_nob(&p->state, - ~ERTS_PSFLG_DIRTY_ACTIVE_SYS); - state &= ~ERTS_PSFLG_DIRTY_ACTIVE_SYS; + a = erts_atomic32_read_band_nob(&p->state, + ~ERTS_PSFLG_DIRTY_ACTIVE_SYS); + a &= ~ERTS_PSFLG_DIRTY_ACTIVE_SYS; } } - a = state; - while (1) { n = e = a; @@ -6360,6 +6391,11 @@ schedule_out_process(ErtsRunQueue *c_rq, erts_aint32_t state, Process *p, enqueue = ERTS_ENQUEUE_NOT; + ASSERT(((a & (ERTS_PSFLG_EXITING|ERTS_PSFLG_FREE)) + != ERTS_PSFLG_EXITING) + || ((a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) + == ERTS_PSFLG_ACTIVE)); + n &= ~running_flgs; if ((a & (ERTS_PSFLG_ACTIVE_SYS|ERTS_PSFLG_DIRTY_ACTIVE_SYS)) || (a & (ERTS_PSFLG_ACTIVE|ERTS_PSFLG_SUSPENDED)) == ERTS_PSFLG_ACTIVE) { @@ -6584,7 +6620,7 @@ change_proc_schedule_state(Process *p, } - *statep = a; + *statep = n; return enqueue; } @@ -6609,6 +6645,95 @@ erts_schedule_process(Process *p, erts_aint32_t state, ErtsProcLocks locks) schedule_process(p, state, locks); } +static ERTS_INLINE erts_aint32_t +active_sys_enqueue(Process *p, erts_aint32_t state, + erts_aint32_t enable_flags, int status_locked) +{ + /* + * This function may or may not be called with status locke held. + * It always returns without the status lock held! + */ + unsigned int prof_runnable_procs = erts_system_profile_flags.runnable_procs; + erts_aint32_t n, a = state, enq_prio = -1; + int slocked = status_locked; + int enqueue; /* < 0 -> use proxy */ + + /* Status lock prevents out of order "runnable proc" trace msgs */ + ERTS_LC_ASSERT(slocked || !(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p))); + ERTS_LC_ASSERT(!slocked || (ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p))); + + if (!prof_runnable_procs) { + if (slocked) { + erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + slocked = 0; + } + } + else { + if (!slocked) { + erts_proc_lock(p, ERTS_PROC_LOCK_STATUS); + slocked = !0; + } + } + + ASSERT(!(state & ERTS_PSFLG_PROXY)); + + while (1) { + erts_aint32_t e; + n = e = a; + + if (a & ERTS_PSFLG_FREE) + goto cleanup; /* We don't want to schedule free processes... */ + + enqueue = ERTS_ENQUEUE_NOT; + n |= enable_flags; + n |= ERTS_PSFLG_ACTIVE_SYS; + if (!(a & (ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS))) + enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a); + a = erts_atomic32_cmpxchg_mb(&p->state, n, e); + if (a == e) + break; + if (a == n && enqueue == ERTS_ENQUEUE_NOT) + goto cleanup; + } + + if (prof_runnable_procs) { + + if (!(a & (ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_RUNNING + | ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)) + && (!(a & ERTS_PSFLG_ACTIVE) || (a & ERTS_PSFLG_SUSPENDED))) { + /* We activated a prevously inactive process */ + profile_runnable_proc(p, am_active); + } + + erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + slocked = 0; + } + + add2runq(enqueue, enq_prio, p, n, NULL); + +cleanup: + + if (slocked) + erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); + + ERTS_LC_ASSERT(!(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p))); + + return n; +} + +erts_aint32_t +erts_proc_sys_schedule(Process *p, erts_aint32_t state, erts_aint32_t enable_flag) +{ + /* We are not allowed to call this function with status lock held... */ + return active_sys_enqueue(p, state, enable_flag, 0); +} + static int schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st, erts_aint32_t *fail_state_p) @@ -6616,19 +6741,16 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st, int res; int locked; ErtsProcSysTaskQs *stqs, *free_stqs; - erts_aint32_t fail_state, state, a, n, enq_prio; - int enqueue; /* < 0 -> use proxy */ - unsigned int prof_runnable_procs; + erts_aint32_t fail_state, state; fail_state = *fail_state_p; res = 1; /* prepare for success */ st->next = st->prev = st; /* Prep for empty prio queue */ state = erts_atomic32_read_nob(&p->state); - prof_runnable_procs = erts_system_profile_flags.runnable_procs; locked = 0; free_stqs = NULL; - if (state & ERTS_PSFLG_ACTIVE_SYS) + if (state & ERTS_PSFLG_SYS_TASKS) stqs = NULL; else { alloc_qs: @@ -6648,6 +6770,7 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st, state = erts_atomic32_read_nob(&p->state); if (state & fail_state) { + erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); *fail_state_p = (state & fail_state); free_stqs = stqs; res = 0; @@ -6695,69 +6818,14 @@ schedule_process_sys_task(Process *p, erts_aint32_t prio, ErtsProcSysTask *st, state = n; } - - a = state; - enq_prio = -1; - - /* Status lock prevents out of order "runnable proc" trace msgs */ - ERTS_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)); - - if (!prof_runnable_procs) { - erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - locked = 0; - } - - ASSERT(!(state & ERTS_PSFLG_PROXY)); - - while (1) { - erts_aint32_t e; - n = e = a; - - if (a & ERTS_PSFLG_FREE) - goto cleanup; /* We don't want to schedule free processes... */ - - enqueue = ERTS_ENQUEUE_NOT; - n |= ERTS_PSFLG_ACTIVE_SYS; - if (!(a & (ERTS_PSFLG_RUNNING - | ERTS_PSFLG_RUNNING_SYS - | ERTS_PSFLG_DIRTY_RUNNING - | ERTS_PSFLG_DIRTY_RUNNING_SYS))) - enqueue = check_enqueue_in_prio_queue(p, &enq_prio, &n, a); - a = erts_atomic32_cmpxchg_mb(&p->state, n, e); - if (a == e) - break; - if (a == n && enqueue == ERTS_ENQUEUE_NOT) - goto cleanup; - } - - if (prof_runnable_procs) { - - if (!(a & (ERTS_PSFLG_ACTIVE_SYS - | ERTS_PSFLG_RUNNING - | ERTS_PSFLG_RUNNING_SYS - | ERTS_PSFLG_DIRTY_RUNNING - | ERTS_PSFLG_DIRTY_RUNNING_SYS)) - && (!(a & ERTS_PSFLG_ACTIVE) || (a & ERTS_PSFLG_SUSPENDED))) { - /* We activated a prevously inactive process */ - profile_runnable_proc(p, am_active); - } - - erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - locked = 0; - } - - add2runq(enqueue, enq_prio, p, n, NULL); + /* active_sys_enqueue() always return with status lock unlocked */ + (void) active_sys_enqueue(p, state, ERTS_PSFLG_SYS_TASKS, locked); cleanup: - if (locked) - erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - if (free_stqs) proc_sys_task_queues_free(free_stqs); - ERTS_LC_ASSERT(!(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p))); - return res; } @@ -8536,11 +8604,13 @@ static ERTS_INLINE void cancel_suspend_of_suspendee(Process *p, ErtsProcLocks p_locks) { if (is_not_nil(p->suspendee)) { + ErtsMonitor *mon; + Eterm suspendee = p->suspendee; Process *rp; if (!(p_locks & ERTS_PROC_LOCK_STATUS)) erts_proc_lock(p, ERTS_PROC_LOCK_STATUS); rp = erts_pid2proc(p, p_locks|ERTS_PROC_LOCK_STATUS, - p->suspendee, ERTS_PROC_LOCK_STATUS); + suspendee, ERTS_PROC_LOCK_STATUS); if (rp) { erts_resume(rp, ERTS_PROC_LOCK_STATUS); erts_proc_unlock(rp, ERTS_PROC_LOCK_STATUS); @@ -8548,6 +8618,14 @@ cancel_suspend_of_suspendee(Process *p, ErtsProcLocks p_locks) if (!(p_locks & ERTS_PROC_LOCK_STATUS)) erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); p->suspendee = NIL; + + mon = erts_monitor_tree_lookup(p->suspend_monitors, + suspendee); + if (mon) { + erts_monitor_tree_delete(&p->suspend_monitors, + mon); + erts_monitor_suspend_destroy(erts_monitor_suspend(mon)); + } } } @@ -8706,8 +8784,7 @@ pid2proc_not_running(Process *c_p, ErtsProcLocks c_p_locks, { erts_aint32_t state; state = erts_atomic32_read_nob(&rp->state); - ASSERT((state & ERTS_PSFLG_PENDING_EXIT) - || !(state & ERTS_PSFLG_RUNNING)); + ASSERT(!(state & ERTS_PSFLG_RUNNING)); } #endif @@ -8762,7 +8839,7 @@ erts_pid2proc_nropt(Process *c_p, ErtsProcLocks c_p_locks, static ERTS_INLINE int do_bif_suspend_process(Process *c_p, - ErtsSuspendMonitor *smon, + ErtsMonitorSuspend *smon, Process *suspendee) { ASSERT(suspendee); @@ -8795,21 +8872,25 @@ handle_pend_bif_sync_suspend(Process *suspendee, suspender = erts_pid2proc(suspendee, suspendee_locks, suspender_pid, - ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS); + ERTS_PROC_LOCK_STATUS); if (suspender) { + ErtsMonitorSuspend *smon; + ErtsMonitor *mon; + mon = erts_monitor_tree_lookup(suspender->suspend_monitors, + suspendee->common.id); + smon = erts_monitor_suspend(mon); + ASSERT(is_nil(suspender->suspendee)); - if (!suspendee_alive) - erts_delete_suspend_monitor(&suspender->suspend_monitors, - suspendee->common.id); + if (!suspendee_alive) { + if (mon) { + erts_monitor_tree_delete(&suspender->suspend_monitors, + mon); + erts_monitor_suspend_destroy(smon); + } + } else { #ifdef DEBUG - int res; -#endif - ErtsSuspendMonitor *smon; - smon = erts_lookup_suspend_monitor(suspender->suspend_monitors, - suspendee->common.id); -#ifdef DEBUG - res = + int res = #endif do_bif_suspend_process(suspendee, smon, suspendee); ASSERT(!smon || res != 0); @@ -8818,9 +8899,8 @@ handle_pend_bif_sync_suspend(Process *suspendee, /* suspender is suspended waiting for suspendee to suspend; resume suspender */ ASSERT(suspender != suspendee); - resume_process(suspender, ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS); - erts_proc_unlock(suspender, - ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS); + resume_process(suspender, ERTS_PROC_LOCK_STATUS); + erts_proc_unlock(suspender, ERTS_PROC_LOCK_STATUS); } } @@ -8838,26 +8918,29 @@ handle_pend_bif_async_suspend(Process *suspendee, suspender = erts_pid2proc(suspendee, suspendee_locks, suspender_pid, - ERTS_PROC_LOCK_LINK); + ERTS_PROC_LOCK_STATUS); if (suspender) { + ErtsMonitorSuspend *smon; + ErtsMonitor *mon; + mon = erts_monitor_tree_lookup(suspender->suspend_monitors, + suspendee->common.id); + smon = erts_monitor_suspend(mon); ASSERT(is_nil(suspender->suspendee)); - if (!suspendee_alive) - erts_delete_suspend_monitor(&suspender->suspend_monitors, - suspendee->common.id); + if (!suspendee_alive) { + if (mon) { + erts_monitor_tree_delete(&suspender->suspend_monitors, + mon); + erts_monitor_suspend_destroy(smon); + } + } else { #ifdef DEBUG - int res; -#endif - ErtsSuspendMonitor *smon; - smon = erts_lookup_suspend_monitor(suspender->suspend_monitors, - suspendee->common.id); -#ifdef DEBUG - res = + int res = #endif - do_bif_suspend_process(suspendee, smon, suspendee); + do_bif_suspend_process(suspendee, smon, suspendee); ASSERT(!smon || res != 0); } - erts_proc_unlock(suspender, ERTS_PROC_LOCK_LINK); + erts_proc_unlock(suspender, ERTS_PROC_LOCK_STATUS); } } @@ -8871,8 +8954,9 @@ suspend_process_2(BIF_ALIST_2) { Eterm res; Process* suspendee = NULL; - ErtsSuspendMonitor *smon; + ErtsMonitorSuspend *smon; ErtsProcLocks xlocks = (ErtsProcLocks) 0; + int created; /* Options and default values: */ int asynchronous = 0; @@ -8905,9 +8989,7 @@ suspend_process_2(BIF_ALIST_2) goto badarg; } - xlocks = ERTS_PROC_LOCK_LINK | (asynchronous - ? (ErtsProcLocks) 0 - : ERTS_PROC_LOCK_STATUS); + xlocks = ERTS_PROC_LOCK_STATUS; erts_proc_lock(BIF_P, xlocks); @@ -8918,15 +9000,14 @@ suspend_process_2(BIF_ALIST_2) if (!suspendee) goto no_suspendee; - smon = erts_add_or_lookup_suspend_monitor(&BIF_P->suspend_monitors, - BIF_ARG_1); - - /* ... but a little trickier with SMP support ... */ + smon = erts_monitor_suspend_tree_lookup_create(&BIF_P->suspend_monitors, + &created, + BIF_ARG_1); if (asynchronous) { /* --- Asynchronous suspend begin ---------------------------------- */ - ERTS_LC_ASSERT(ERTS_PROC_LOCK_LINK + ERTS_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(BIF_P)); ERTS_LC_ASSERT(ERTS_PROC_LOCK_STATUS == erts_proc_lc_my_proc_locks(suspendee)); @@ -8968,9 +9049,9 @@ suspend_process_2(BIF_ALIST_2) else /* if (!asynchronous) */ { /* --- Synchronous suspend begin ----------------------------------- */ - ERTS_LC_ASSERT(((ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS) + ERTS_LC_ASSERT(((ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_STATUS) & erts_proc_lc_my_proc_locks(BIF_P)) - == (ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCK_STATUS)); + == (ERTS_PROC_LOCK_STATUS|ERTS_PROC_LOCK_STATUS)); ERTS_LC_ASSERT(ERTS_PROC_LOCK_STATUS == erts_proc_lc_my_proc_locks(suspendee)); @@ -9055,9 +9136,15 @@ suspend_process_2(BIF_ALIST_2) ERTS_BIF_PREP_ERROR(res, BIF_P, SYSTEM_LIMIT); goto do_return; - no_suspendee: - BIF_P->suspendee = NIL; - erts_delete_suspend_monitor(&BIF_P->suspend_monitors, BIF_ARG_1); + no_suspendee: { + ErtsMonitor *mon; + BIF_P->suspendee = NIL; + mon = erts_monitor_tree_lookup(BIF_P->suspend_monitors, BIF_ARG_1); + if (mon) { + erts_monitor_tree_delete(&BIF_P->suspend_monitors, mon); + erts_monitor_suspend_destroy(erts_monitor_suspend(mon)); + } + } badarg: ERTS_BIF_PREP_ERROR(res, BIF_P, BADARG); @@ -9084,15 +9171,17 @@ suspend_process_2(BIF_ALIST_2) BIF_RETTYPE resume_process_1(BIF_ALIST_1) { - ErtsSuspendMonitor *smon; + ErtsMonitor *mon; + ErtsMonitorSuspend *smon; Process *suspendee; int is_active; if (BIF_P->common.id == BIF_ARG_1) BIF_ERROR(BIF_P, BADARG); - erts_proc_lock(BIF_P, ERTS_PROC_LOCK_LINK); - smon = erts_lookup_suspend_monitor(BIF_P->suspend_monitors, BIF_ARG_1); + erts_proc_lock(BIF_P, ERTS_PROC_LOCK_STATUS); + mon = erts_monitor_tree_lookup(BIF_P->suspend_monitors, BIF_ARG_1); + smon = erts_monitor_suspend(mon); if (!smon) { /* No previous suspend or dead suspendee */ @@ -9109,20 +9198,17 @@ resume_process_1(BIF_ALIST_1) } else if (smon->active) { smon->active--; - ASSERT(smon->pending >= 0); + ASSERT(smon->pending == 0); is_active = 1; } else { /* No previous suspend or dead suspendee */ - goto error; + goto no_suspendee; } if (smon->active || smon->pending || !is_active) { /* Leave the suspendee as it is; just verify that it is still alive */ - suspendee = erts_pid2proc(BIF_P, - ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, - BIF_ARG_1, - 0); + suspendee = erts_proc_lookup(BIF_ARG_1); if (!suspendee) goto no_suspendee; @@ -9130,11 +9216,18 @@ resume_process_1(BIF_ALIST_1) else { /* Resume */ suspendee = erts_pid2proc(BIF_P, - ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_LINK, + ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS, BIF_ARG_1, ERTS_PROC_LOCK_STATUS); - if (!suspendee) + if (!suspendee) { + mon = erts_monitor_tree_lookup(BIF_P->suspend_monitors, BIF_ARG_1); + smon = erts_monitor_suspend(mon); + if (!mon) + goto error; goto no_suspendee; + } + + ASSERT(mon == erts_monitor_tree_lookup(BIF_P->suspend_monitors, BIF_ARG_1)); ASSERT(ERTS_PSFLG_SUSPENDED & erts_atomic32_read_nob(&suspendee->state)); @@ -9144,19 +9237,24 @@ resume_process_1(BIF_ALIST_1) erts_proc_unlock(suspendee, ERTS_PROC_LOCK_STATUS); } - if (!smon->active && !smon->pending) - erts_delete_suspend_monitor(&BIF_P->suspend_monitors, BIF_ARG_1); + if (!smon->active && !smon->pending) { + ASSERT(mon); + erts_monitor_tree_delete(&BIF_P->suspend_monitors, mon); + erts_monitor_suspend_destroy(smon); + } - erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK); + erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS); BIF_RET(am_true); no_suspendee: /* cleanup */ - erts_delete_suspend_monitor(&BIF_P->suspend_monitors, BIF_ARG_1); + ASSERT(mon); + erts_monitor_tree_delete(&BIF_P->suspend_monitors, mon); + erts_monitor_suspend_destroy(smon); error: - erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_LINK); + erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_STATUS); BIF_ERROR(BIF_P, BADARG); } @@ -9375,9 +9473,8 @@ erts_resume_processes(ErtsProcList *list) } Eterm -erts_get_process_priority(Process *p) +erts_get_process_priority(erts_aint32_t state) { - erts_aint32_t state = erts_atomic32_read_nob(&p->state); switch (ERTS_PSFLGS_GET_USR_PRIO(state)) { case PRIORITY_MAX: return am_max; case PRIORITY_HIGH: return am_high; @@ -9427,7 +9524,10 @@ erts_set_process_priority(Process *p, Eterm value) } max_qbit = 0; - if (a & ERTS_PSFLG_ACTIVE_SYS) + ASSERT((a & ERTS_PSFLG_SYS_TASKS) + ? !!p->sys_task_qs + : !p->sys_task_qs); + if (a & ERTS_PSFLG_SYS_TASKS) max_qbit |= p->sys_task_qs->qmask; if (a & ERTS_PSFLG_DELAYED_SYS) { ErtsProcSysTaskQs *qs; @@ -9450,8 +9550,8 @@ erts_set_process_priority(Process *p, Eterm value) aprio = PRIORITY_LOW; break; default: - ERTS_INTERNAL_ERROR("Invalid qmask"); - aprio = -1; + aprio = nprio; + break; } if (aprio > nprio) /* low value -> high prio */ @@ -9627,10 +9727,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) /* have to re-read state after taking lock */ state = erts_atomic32_read_nob(&p->state); - if (is_normal_sched && (state & ERTS_PSFLG_PENDING_EXIT)) - erts_handle_pending_exit(p, (ERTS_PROC_LOCK_MAIN - | ERTS_PROC_LOCK_TRACE - | ERTS_PROC_LOCK_STATUS)); if (p->pending_suspenders) handle_pending_suspend(p, (ERTS_PROC_LOCK_MAIN | ERTS_PROC_LOCK_TRACE @@ -9833,7 +9929,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) if (flags & ERTS_RUNQ_FLG_MISC_OP) exec_misc_ops(rq); - wakeup_other.check(rq, flags); + runq_get_wakeup_other_params(rq)->check(rq, flags); /* * Find a new port to run. @@ -9945,12 +10041,10 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) & ((state & (ERTS_PSFLG_SUSPENDED | ERTS_PSFLG_EXITING | ERTS_PSFLG_FREE - | ERTS_PSFLG_PENDING_EXIT | ERTS_PSFLG_ACTIVE_SYS | ERTS_PSFLG_DIRTY_ACTIVE_SYS)) != ERTS_PSFLG_SUSPENDED) - & (!(state & (ERTS_PSFLG_EXITING - | ERTS_PSFLG_PENDING_EXIT)) + & (!(state & ERTS_PSFLG_EXITING) | (!!is_normal_sched)) ); @@ -10037,8 +10131,11 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) p->scheduler_data = esdp; } else { + if (!(state & ERTS_PSFLGS_DIRTY_WORK)) { + /* Dirty work completed... */ + goto sunlock_sched_out_proc; + } if (state & (ERTS_PSFLG_ACTIVE_SYS - | ERTS_PSFLG_PENDING_EXIT | ERTS_PSFLG_EXITING)) { /* * IMPORTANT! We need to take care of @@ -10061,13 +10158,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) && (state & ERTS_PSFLG_DIRTY_IO_PROC))); } - if (state & ERTS_PSFLG_PENDING_EXIT) { - erts_handle_pending_exit(p, - ERTS_PROC_LOCK_MAIN|ERTS_PROC_LOCK_STATUS); - state = erts_atomic32_read_nob(&p->state); - } - - erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); /* Clear tracer if it has been removed */ @@ -10089,24 +10179,47 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) } if (is_normal_sched) { - if (state & ERTS_PSFLG_RUNNING_SYS) { - /* - * GC is normally never delayed when a process - * is scheduled out, but might be when executing - * hand written beam assembly in - * prim_eval:'receive'. If GC is delayed we are - * not allowed to execute system tasks. - */ - if (!(p->flags & F_DELAY_GC)) { - int cost = execute_sys_tasks(p, &state, reds); - calls += cost; - reds -= cost; - if (reds <= 0) - goto sched_out_proc; - if (state & ERTS_PSFLGS_DIRTY_WORK) - goto sched_out_proc; + if (state & (ERTS_PSFLG_SIG_Q|ERTS_PSFLG_SIG_IN_Q)) { + int local_only = !!(p->flags & F_LOCAL_SIGS_ONLY); + if (!local_only || (state & ERTS_PSFLG_SIG_Q)) { + int sig_reds; + /* + * If we have dirty work scheduled we allow + * usage of all reductions since we need to + * handle all signals before doing dirty + * work... + */ + if (state & ERTS_PSFLGS_DIRTY_WORK) + sig_reds = reds; + else + sig_reds = ERTS_SIG_HANDLE_REDS_MAX_PREFERED; + (void) erts_proc_sig_handle_incoming(p, + &state, + &sig_reds, + sig_reds, + local_only); + reds -= sig_reds; + } } + if ((state & (ERTS_PSFLG_SYS_TASKS + | ERTS_PSFLG_EXITING)) == ERTS_PSFLG_SYS_TASKS) { + /* + * GC is normally never delayed when a process + * is scheduled out, but might be when executing + * hand written beam assembly in + * prim_eval:'receive'. If GC is delayed we are + * not allowed to execute system tasks. + */ + if (!(p->flags & F_DELAY_GC)) { + int cost = execute_sys_tasks(p, &state, reds); + calls += cost; + reds -= cost; + } + } + + if (reds <= 0 || (state & ERTS_PSFLGS_DIRTY_WORK)) + goto sched_out_proc; ASSERT(state & psflg_running_sys); ASSERT(!(state & psflg_running)); @@ -10116,7 +10229,7 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) if (((state & (ERTS_PSFLG_SUSPENDED | ERTS_PSFLG_ACTIVE)) != ERTS_PSFLG_ACTIVE) - && !(state & ERTS_PSFLG_EXITING)) { + & !(state & ERTS_PSFLG_EXITING)) { goto sched_out_proc; } @@ -10158,15 +10271,6 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(p); - /* Never run a suspended process */ -#ifdef DEBUG - { - erts_aint32_t dstate = erts_atomic32_read_nob(&p->state); - ASSERT(!(ERTS_PSFLG_SUSPENDED & dstate) - || (ERTS_PSFLG_DIRTY_RUNNING_SYS & dstate)); - } -#endif - ASSERT(erts_proc_read_refc(p) > 0); if (!(state & ERTS_PSFLG_EXITING) && ERTS_PTMR_IS_TIMED_OUT(p)) { @@ -10179,6 +10283,40 @@ Process *erts_schedule(ErtsSchedulerData *esdp, Process *p, int calls) ERTS_PTMR_CLEAR(p); } + /* if exiting, we *shall* exit... */ + ASSERT(!(state & ERTS_PSFLG_EXITING) + || p->i == (BeamInstr *) beam_exit + || p->i == (BeamInstr *) beam_continue_exit); + +#ifdef DEBUG + if (is_normal_sched) { + if (state & ERTS_PSFLGS_DIRTY_WORK) + ERTS_INTERNAL_ERROR("Executing dirty code on normal scheduler"); + } + else { + if (!(state & ERTS_PSFLGS_DIRTY_WORK)) { + if (esdp->type == ERTS_SCHED_DIRTY_CPU) + ERTS_INTERNAL_ERROR("Executing normal code on dirty CPU scheduler"); + else if (esdp->type == ERTS_SCHED_DIRTY_IO) + ERTS_INTERNAL_ERROR("Executing normal code on dirty IO scheduler"); + else + ERTS_INTERNAL_ERROR("Executing normal code on dirty UNKNOWN scheduler"); + } + } + { + erts_aint32_t dstate = erts_atomic32_read_nob(&p->state); + + /* Never run a suspended process */ + ASSERT(!(ERTS_PSFLG_SUSPENDED & dstate) + || (ERTS_PSFLG_DIRTY_RUNNING_SYS & dstate)); + + /* Do not execute on the wrong type of scheduler... */ + ASSERT(is_normal_sched + ? !(dstate & ERTS_PSFLGS_DIRTY_WORK) + : !!(dstate & ERTS_PSFLGS_DIRTY_WORK)); + } +#endif + return p; } } @@ -10378,7 +10516,7 @@ fetch_sys_task(Process *c_p, erts_aint32_t state, int *qmaskp, int *priop) n |= (prio << ERTS_PSFLGS_ACT_PRIO_OFFSET); if (!qmask) - n &= ~ERTS_PSFLG_ACTIVE_SYS; + n &= ~ERTS_PSFLG_SYS_TASKS; if (a == n) break; @@ -10398,6 +10536,8 @@ done: return st; } + +static void exit_permanent_prio_elevation(Process *c_p, erts_aint32_t state); static void save_gc_task(Process *c_p, ErtsProcSysTask *st, int prio); static void save_dirty_task(Process *c_p, ErtsProcSysTask *st); @@ -10418,12 +10558,8 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) int st_prio; Eterm st_res; - if (state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT)) { - if (state & ERTS_PSFLG_PENDING_EXIT) - erts_handle_pending_exit(c_p, ERTS_PROC_LOCK_MAIN); - ASSERT(ERTS_PROC_IS_EXITING(c_p)); + if (state & ERTS_PSFLG_EXITING) break; - } st = fetch_sys_task(c_p, state, &qmask, &st_prio); if (!st) @@ -10519,6 +10655,43 @@ execute_sys_tasks(Process *c_p, erts_aint32_t *statep, int in_reds) reds -= erts_db_execute_free_fixation(c_p, (DbFixation*)st->arg[0]); st_res = am_true; break; + case ERTS_PSTT_PRIO_SIG: { + erts_aint32_t fail_state, state; + int local_only, sig_res, sig_reds = reds; + st_res = am_false; + + if (st->arg[0] == am_true) + local_only = !0; + else + local_only = 0; + + sig_reds = reds; + sig_res = erts_proc_sig_handle_incoming(c_p, &state, &sig_reds, + reds, local_only); + reds -= sig_reds; + + if (state & ERTS_PSFLG_EXITING) { + exit_permanent_prio_elevation(c_p, state); + break; + } + + if (sig_res) + break; + + st->arg[0] = am_true; + + fail_state = ERTS_PSFLG_EXITING; + + if (schedule_process_sys_task(c_p, st_prio, st, &fail_state)) { + /* Successfully rescheduled task... */ + st = NULL; + } + else { + state = erts_atomic32_read_nob(&c_p->state); + exit_permanent_prio_elevation(c_p, state); + } + break; + } default: ERTS_INTERNAL_ERROR("Invalid process sys task type"); st_res = am_false; @@ -10567,6 +10740,10 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds) } switch (st->type) { + case ERTS_PSTT_PRIO_SIG: + state = erts_atomic32_read_nob(&c_p->state); + exit_permanent_prio_elevation(c_p, state); + /* fall through... */ case ERTS_PSTT_GC_MAJOR: case ERTS_PSTT_GC_MINOR: case ERTS_PSTT_CPC: @@ -10595,6 +10772,36 @@ cleanup_sys_tasks(Process *c_p, erts_aint32_t in_state, int in_reds) return reds; } +static void +exit_permanent_prio_elevation(Process *c_p, erts_aint32_t state) +{ + erts_aint32_t a; + /* + * we are about to terminate; permanently elevate + * prio in order to ensure high prio signal + * handling... + */ + a = state; + while (1) { + erts_aint32_t aprio, uprio, n, e; + ASSERT(a & ERTS_PSFLG_EXITING); + ASSERT(!(a & ERTS_PSFLG_FREE)); + aprio = ERTS_PSFLGS_GET_ACT_PRIO(a); + uprio = ERTS_PSFLGS_GET_USR_PRIO(a); + if (aprio >= uprio) + break; /* user prio >= actual prio */ + /* + * actual prio is higher than user prio; raise + * user prio to actual prio... + */ + n = e = a; + n &= ~ERTS_PSFLGS_USR_PRIO_MASK; + n |= aprio << ERTS_PSFLGS_USR_PRIO_OFFSET; + a = erts_atomic32_cmpxchg_mb(&c_p->state, n, e); + if (a == e) + break; + } +} void erts_execute_dirty_system_task(Process *c_p) @@ -10624,8 +10831,8 @@ erts_execute_dirty_system_task(Process *c_p) if (c_p->flags & F_DIRTY_GC_HIBERNATE) { erts_proc_lock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS); - ERTS_MSGQ_MV_INQ2PRIVQ(c_p); - if (c_p->msg.len) + erts_proc_sig_fetch(c_p); + if (c_p->sig_qs.len) c_p->flags &= ~F_DIRTY_GC_HIBERNATE; /* operation aborted... */ else { erts_proc_unlock(c_p, ERTS_PROC_LOCK_MSGQ|ERTS_PROC_LOCK_STATUS); @@ -10697,7 +10904,7 @@ dispatch_system_task(Process *c_p, erts_aint_t fail_state, switch (st->type) { case ERTS_PSTT_CPC: - rp = erts_dirty_process_code_checker; + rp = erts_dirty_process_signal_handler; ASSERT(fail_state & (ERTS_PSFLG_DIRTY_RUNNING | ERTS_PSFLG_DIRTY_RUNNING_SYS)); if (c_p == rp) { @@ -10914,13 +11121,15 @@ erts_internal_request_system_task_4(BIF_ALIST_4) BIF_ARG_2, BIF_ARG_3, BIF_ARG_4); } -static void -erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type, void* arg) +static int +schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type, + int prio, Eterm arg0, Eterm arg1) { - Process *rp = erts_proc_lookup(pid); + int res = 0; + Process *rp = erts_proc_lookup_raw(pid); if (rp) { ErtsProcSysTask *st; - erts_aint32_t state, fail_state; + erts_aint32_t st_prio, fail_state; st = erts_alloc(ERTS_ALC_T_PROC_SYS_TSK, ERTS_PROC_SYS_TASK_SIZE(0)); @@ -10929,31 +11138,46 @@ erts_schedule_generic_sys_task(Eterm pid, ErtsProcSysTaskType type, void* arg) st->reply_tag = NIL; st->req_id = NIL; st->req_id_sz = 0; - st->arg[0] = (Eterm)arg; + st->arg[0] = arg0; + st->arg[1] = arg1; ERTS_INIT_OFF_HEAP(&st->off_heap); - state = erts_atomic32_read_nob(&rp->state); - - fail_state = ERTS_PSFLG_EXITING; - if (!schedule_process_sys_task(rp, ERTS_PSFLGS_GET_USR_PRIO(state), - st, &fail_state)) + if (prio >= 0) { + st_prio = (erts_aint32_t) prio; + fail_state = ERTS_PSFLG_FREE; + } + else { + erts_aint32_t state = erts_atomic32_read_nob(&rp->state); + st_prio = ERTS_PSFLGS_GET_USR_PRIO(state); + fail_state = ERTS_PSFLG_EXITING; + } + res = schedule_process_sys_task(rp, st_prio, st, &fail_state); + if (!res) erts_free(ERTS_ALC_T_PROC_SYS_TSK, st); } + return res; } - void erts_schedule_complete_off_heap_message_queue_change(Eterm pid) { - erts_schedule_generic_sys_task(pid, ERTS_PSTT_COHMQ, NULL); + schedule_generic_sys_task(pid, ERTS_PSTT_COHMQ, + -1, NIL, NIL); } void erts_schedule_ets_free_fixation(Eterm pid, DbFixation* fix) { - erts_schedule_generic_sys_task(pid, ERTS_PSTT_ETS_FREE_FIXATION, fix); + schedule_generic_sys_task(pid, ERTS_PSTT_ETS_FREE_FIXATION, + -1, (Eterm) fix, NIL); } +int +erts_sig_prio(Eterm pid, int prio) +{ + return schedule_generic_sys_task(pid, ERTS_PSTT_PRIO_SIG, + prio, am_false, NIL); +} static void flush_dirty_trace_messages(void *vpid) @@ -10993,7 +11217,7 @@ erts_schedule_flush_trace_messages(Process *proc, int force_on_proc) dhndl = erts_thr_progress_unmanaged_delay(); - erts_schedule_generic_sys_task(pid, ERTS_PSTT_FTMQ, NULL); + schedule_generic_sys_task(pid, ERTS_PSTT_FTMQ, -1, NIL, NIL); erts_thr_progress_unmanaged_continue(dhndl); @@ -11189,9 +11413,11 @@ erts_set_gc_state(Process *c_p, int enable) #endif erts_atomic32_read_bset_nob(&c_p->state, - (ERTS_PSFLG_DELAYED_SYS - | ERTS_PSFLG_ACTIVE_SYS), - ERTS_PSFLG_ACTIVE_SYS); + (ERTS_PSFLG_DELAYED_SYS + | ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_SYS_TASKS), + (ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_SYS_TASKS)); #ifdef DEBUG ASSERT(state & ERTS_PSFLG_DELAYED_SYS); @@ -11472,7 +11698,6 @@ alloc_process(ErtsRunQueue *rq, int bound, erts_aint32_t state) ASSERT(internal_pid_serial(p->common.id) <= ERTS_MAX_PID_SERIAL); - p->approx_started = erts_get_approx_time(); p->rcount = 0; p->heap = NULL; @@ -11655,7 +11880,7 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->common.u.alive.reg = NULL; ERTS_P_LINKS(p) = NULL; ERTS_P_MONITORS(p) = NULL; - p->nodes_monitors = NULL; + ERTS_P_LT_MONITORS(p) = NULL; p->suspend_monitors = NULL; ASSERT(is_pid(parent->group_leader)); @@ -11672,14 +11897,20 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). erts_get_default_proc_tracing(&ERTS_TRACE_FLAGS(p), &ERTS_TRACER(p)); - p->msg.first = NULL; - p->msg.last = &p->msg.first; - p->msg.save = &p->msg.first; - p->msg.saved_last = &p->msg.first; - p->msg.len = 0; - p->msg_inq.first = NULL; - p->msg_inq.last = &p->msg_inq.first; - p->msg_inq.len = 0; + p->sig_qs.first = NULL; + p->sig_qs.last = &p->sig_qs.first; + p->sig_qs.cont = NULL; + p->sig_qs.cont_last = &p->sig_qs.cont; + p->sig_qs.save = &p->sig_qs.first; + p->sig_qs.saved_last = NULL; + p->sig_qs.len = 0; + p->sig_qs.nmsigs.next = NULL; + p->sig_qs.nmsigs.last = NULL; + p->sig_inq.first = NULL; + p->sig_inq.last = &p->sig_inq.first; + p->sig_inq.len = 0; + p->sig_inq.nmsigs.next = NULL; + p->sig_inq.nmsigs.last = NULL; p->bif_timers = NULL; p->mbuf = NULL; p->msg_frag = NULL; @@ -11706,8 +11937,6 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). p->scheduler_data = NULL; p->suspendee = NIL; p->pending_suspenders = NULL; - p->pending_exit.reason = THE_NON_VALUE; - p->pending_exit.bp = NULL; #if !defined(NO_FPE_SIGNALS) || defined(HIPE) p->fp_exception = 0; @@ -11761,30 +11990,33 @@ erl_create_process(Process* parent, /* Parent of process (default group leader). */ if (so->flags & SPO_LINK) { -#ifdef DEBUG - int ret; -#endif -#ifdef DEBUG - ret = erts_add_link(&ERTS_P_LINKS(parent), LINK_PID, p->common.id); - ASSERT(ret == 0); - ret = erts_add_link(&ERTS_P_LINKS(p), LINK_PID, parent->common.id); - ASSERT(ret == 0); -#else - erts_add_link(&ERTS_P_LINKS(parent), LINK_PID, p->common.id); - erts_add_link(&ERTS_P_LINKS(p), LINK_PID, parent->common.id); -#endif - + ErtsLink *lnk; + ErtsLinkData *ldp = erts_link_create(ERTS_LNK_TYPE_PROC, + parent->common.id, + p->common.id); + lnk = erts_link_tree_lookup_insert(&ERTS_P_LINKS(parent), &ldp->a); + if (lnk) { + /* + * This should more or less never happen, but could + * potentially happen if pid:s wrap... + */ + erts_link_release(lnk); + } + erts_link_tree_insert(&ERTS_P_LINKS(p), &ldp->b); } /* * Test whether this process should be initially monitored by its parent. */ if (so->flags & SPO_MONITOR) { - Eterm mref; - - mref = erts_make_ref(parent); - erts_add_monitor(&ERTS_P_MONITORS(parent), MON_ORIGIN, mref, p->common.id, NIL); - erts_add_monitor(&ERTS_P_MONITORS(p), MON_TARGET, mref, parent->common.id, NIL); + Eterm mref = erts_make_ref(parent); + ErtsMonitorData *mdp = erts_monitor_create(ERTS_MON_TYPE_PROC, + mref, + parent->common.id, + p->common.id, + NIL); + erts_monitor_tree_insert(&ERTS_P_MONITORS(parent), &mdp->origin); + erts_monitor_list_insert(&ERTS_P_LT_MONITORS(p), &mdp->target); so->mref = mref; } @@ -11869,13 +12101,23 @@ void erts_init_empty_process(Process *p) p->mbuf_sz = 0; erts_atomic_init_nob(&p->psd, (erts_aint_t) NULL); ERTS_P_MONITORS(p) = NULL; + ERTS_P_LT_MONITORS(p) = NULL; ERTS_P_LINKS(p) = NULL; /* List of links */ - p->nodes_monitors = NULL; p->suspend_monitors = NULL; - p->msg.first = NULL; - p->msg.last = &p->msg.first; - p->msg.save = &p->msg.first; - p->msg.len = 0; + p->sig_qs.first = NULL; + p->sig_qs.last = &p->sig_qs.first; + p->sig_qs.cont = NULL; + p->sig_qs.cont_last = &p->sig_qs.cont; + p->sig_qs.save = &p->sig_qs.first; + p->sig_qs.saved_last = NULL; + p->sig_qs.len = 0; + p->sig_qs.nmsigs.next = NULL; + p->sig_qs.nmsigs.last = NULL; + p->sig_inq.first = NULL; + p->sig_inq.last = &p->sig_inq.first; + p->sig_inq.len = 0; + p->sig_inq.nmsigs.next = NULL; + p->sig_inq.nmsigs.last = NULL; p->bif_timers = NULL; p->dictionary = NULL; p->seq_trace_clock = 0; @@ -11903,7 +12145,6 @@ void erts_init_empty_process(Process *p) p->def_arg_reg[5] = 0; p->parent = NIL; - p->approx_started = 0; p->static_flags = 0; p->common.u.alive.started_interval = 0; @@ -11923,13 +12164,8 @@ void erts_init_empty_process(Process *p) erts_atomic32_init_nob(&p->state, (erts_aint32_t) PRIORITY_NORMAL); p->scheduler_data = NULL; - p->msg_inq.first = NULL; - p->msg_inq.last = &p->msg_inq.first; - p->msg_inq.len = 0; p->suspendee = NIL; p->pending_suspenders = NULL; - p->pending_exit.reason = THE_NON_VALUE; - p->pending_exit.bp = NULL; erts_proc_lock_init(p); erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL); erts_init_runq_proc(p, ERTS_RUNQ_IX(0), 0); @@ -11965,11 +12201,11 @@ erts_debug_verify_clean_empty_process(Process* p) ASSERT(p->old_heap == NULL); ASSERT(ERTS_P_MONITORS(p) == NULL); + ASSERT(ERTS_P_LT_MONITORS(p) == NULL); ASSERT(ERTS_P_LINKS(p) == NULL); - ASSERT(p->nodes_monitors == NULL); ASSERT(p->suspend_monitors == NULL); - ASSERT(p->msg.first == NULL); - ASSERT(p->msg.len == 0); + ASSERT(p->sig_qs.first == NULL); + ASSERT(p->sig_qs.len == 0); ASSERT(p->bif_timers == NULL); ASSERT(p->dictionary == NULL); ASSERT(p->catches == 0); @@ -11979,12 +12215,10 @@ erts_debug_verify_clean_empty_process(Process* p) ASSERT(p->parent == NIL); - ASSERT(p->msg_inq.first == NULL); - ASSERT(p->msg_inq.len == 0); + ASSERT(p->sig_inq.first == NULL); + ASSERT(p->sig_inq.len == 0); ASSERT(p->suspendee == NIL); ASSERT(p->pending_suspenders == NULL); - ASSERT(p->pending_exit.reason == THE_NON_VALUE); - ASSERT(p->pending_exit.bp == NULL); /* Thing that erts_cleanup_empty_process() cleans up */ @@ -12085,817 +12319,330 @@ delete_process(Process* p) erts_erase_dicts(p); /* free all pending messages */ - erts_cleanup_messages(p->msg.first); - p->msg.first = NULL; + erts_cleanup_messages(p->sig_qs.first); + p->sig_qs.first = NULL; + erts_cleanup_messages(p->sig_qs.cont); + p->sig_qs.cont = NULL; - ASSERT(!p->nodes_monitors); ASSERT(!p->suspend_monitors); p->fvalue = NIL; } static ERTS_INLINE void -set_proc_exiting(Process *p, - erts_aint32_t in_state, - Eterm reason, - ErlHeapFragment *bp) -{ - erts_aint32_t state = in_state, enq_prio = -1; - int enqueue; - ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(p) == ERTS_PROC_LOCKS_ALL); - - enqueue = change_proc_schedule_state(p, - (ERTS_PSFLG_SUSPENDED - | ERTS_PSFLG_PENDING_EXIT - | ERTS_PSFLGS_DIRTY_WORK), - ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE, - &state, - &enq_prio, - ERTS_PROC_LOCKS_ALL); - - p->fvalue = reason; - if (bp) - erts_link_mbuf_to_proc(p, bp); - /* - * We used to set freason to EXC_EXIT here, but there is no need to - * save the stack trace since this process irreversibly is going to - * exit. - */ - p->freason = EXTAG_EXIT; - KILL_CATCHES(p); - p->i = (BeamInstr *) beam_exit; - - - add2runq(enqueue, enq_prio, p, state, NULL); -} - -static ERTS_INLINE erts_aint32_t -set_proc_self_exiting(Process *c_p) +set_self_exiting(Process *c_p, Eterm reason, int *enqueue, + erts_aint32_t *prio, erts_aint32_t *state) { -#ifdef DEBUG - int enqueue; -#endif - erts_aint32_t state, enq_prio = -1; + erts_aint32_t st, enq_prio = -1; + int enq; ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCKS_ALL); - state = erts_atomic32_read_nob(&c_p->state); - ASSERT(state & (ERTS_PSFLG_RUNNING - |ERTS_PSFLG_RUNNING_SYS - | ERTS_PSFLG_DIRTY_RUNNING - | ERTS_PSFLG_DIRTY_RUNNING_SYS)); - -#ifdef DEBUG - enqueue = -#endif - change_proc_schedule_state(c_p, - ERTS_PSFLG_SUSPENDED|ERTS_PSFLG_PENDING_EXIT, - ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE, - &state, - &enq_prio, - ERTS_PROC_LOCKS_ALL); - - ASSERT(!enqueue); - return state; -} - + c_p->fvalue = reason; -void -erts_handle_pending_exit(Process *c_p, ErtsProcLocks locks) -{ - ErtsProcLocks xlocks; - ASSERT(is_value(c_p->pending_exit.reason)); - ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == locks); - ERTS_LC_ASSERT(locks & ERTS_PROC_LOCK_MAIN); - ERTS_LC_ASSERT(!((ERTS_PSFLG_EXITING|ERTS_PSFLG_FREE) - & erts_atomic32_read_nob(&c_p->state))); - - /* Ensure that all locks on c_p are locked before proceeding... */ - if (locks == ERTS_PROC_LOCKS_ALL) - xlocks = 0; - else { - xlocks = ~locks & ERTS_PROC_LOCKS_ALL; - if (erts_proc_trylock(c_p, xlocks) == EBUSY) { - erts_proc_unlock(c_p, locks & ~ERTS_PROC_LOCK_MAIN); - erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); - } - } + st = erts_atomic32_read_nob(&c_p->state); + ASSERT(enqueue || (st & (ERTS_PSFLG_RUNNING + |ERTS_PSFLG_RUNNING_SYS + | ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS))); - set_proc_exiting(c_p, - erts_atomic32_read_acqb(&c_p->state), - c_p->pending_exit.reason, - c_p->pending_exit.bp); - c_p->pending_exit.reason = THE_NON_VALUE; - c_p->pending_exit.bp = NULL; + enq = change_proc_schedule_state(c_p, + (ERTS_PSFLG_SUSPENDED + | ERTS_PSFLGS_DIRTY_WORK), + ERTS_PSFLG_EXITING|ERTS_PSFLG_ACTIVE, + &st, + &enq_prio, + ERTS_PROC_LOCKS_ALL); - if (xlocks) - erts_proc_unlock(c_p, xlocks); -} - -static void save_pending_exiter(Process *p, ErtsProcList *plp); - -static void -do_handle_pending_exiters(ErtsProcList *pnd_xtrs) -{ - /* 'list' is expected to have been fetched (i.e. not a ring anymore) */ - ErtsProcList *plp = pnd_xtrs; - - while (plp) { - ErtsProcList *next_plp = plp->next; - Process *p = erts_proc_lookup(plp->pid); - if (p) { - erts_aint32_t state; - /* - * If the process is running on a normal scheduler, the - * pending exit will soon be detected and handled by the - * scheduler running the process (at schedule in/out). - */ - if (erts_proc_trylock(p, ERTS_PROC_LOCKS_ALL) != EBUSY) { - if (erts_proclist_same(plp, p)) { - state = erts_atomic32_read_acqb(&p->state); - if (!(state & (ERTS_PSFLG_RUNNING - | ERTS_PSFLG_RUNNING_SYS - | ERTS_PSFLG_EXITING))) { - ASSERT(state & ERTS_PSFLG_PENDING_EXIT); - erts_handle_pending_exit(p, ERTS_PROC_LOCKS_ALL); - } - } - erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL); - } - else { - erts_proc_lock(p, ERTS_PROC_LOCK_STATUS); - if (erts_proclist_same(plp, p)) { - state = erts_atomic32_read_acqb(&p->state); - if (!(state & (ERTS_PSFLG_RUNNING - | ERTS_PSFLG_RUNNING_SYS - | ERTS_PSFLG_EXITING))) { - /* - * Save process and try to acquire all - * locks at a later time... - */ - save_pending_exiter(p, plp); - plp = NULL; - } - } - erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); - } - } - if (plp) - proclist_destroy(plp); - plp = next_plp; - } + ASSERT(enqueue || !enq); + if (enqueue) + *enqueue = enq; + if (prio) + *prio = enq_prio; + if (state) + *state = st; } -static void -save_pending_exiter(Process *p, ErtsProcList *plp) +void +erts_set_self_exiting(Process *c_p, Eterm reason) { - ErtsSchedulerSleepInfo *ssi; - ErtsRunQueue *rq; - - ERTS_LC_ASSERT(ERTS_PROC_LOCK_STATUS & erts_proc_lc_my_proc_locks(p)); - - rq = erts_get_runq_proc(p, NULL); - ASSERT(rq && !ERTS_RUNQ_IX_IS_DIRTY(rq->ix)); - - if (!plp) - plp = proclist_create(p); - - erts_runq_lock(rq); - - erts_proclist_store_last(&rq->procs.pending_exiters, plp); - - non_empty_runq(rq); - - ssi = rq->scheduler->ssi; - - erts_runq_unlock(rq); + int enqueue; + erts_aint32_t enq_prio, state; + ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN); - set_aux_work_flags_wakeup_nob(ssi, ERTS_SSI_AUX_WORK_PENDING_EXITERS); -} + erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); + set_self_exiting(c_p, reason, &enqueue, &enq_prio, &state); + c_p->freason = EXTAG_EXIT; + KILL_CATCHES(c_p); + c_p->i = (BeamInstr *) beam_exit; -/* - * This function delivers an EXIT message to a process - * which is trapping EXITs. - */ + /* Always active when exiting... */ + ASSERT(state & ERTS_PSFLG_ACTIVE); -static ERTS_INLINE void -send_exit_message(Process *to, ErtsProcLocks *to_locksp, - Eterm exit_term, Uint term_size, Eterm token) -{ - ErtsMessage *mp; - ErlOffHeap *ohp; - Eterm* hp; - Eterm mess; -#ifdef SHCOPY_SEND - erts_shcopy_t info; -#endif + /* + * If we are terminating a process that currently + * is executing on a dirty scheduler. It *should* + * be scheduled on a normal scheduler... + */ + ASSERT(!(state & (ERTS_PSFLG_DIRTY_RUNNING + | ERTS_PSFLG_DIRTY_RUNNING_SYS)) + || enqueue == ERTS_ENQUEUE_NORMAL_QUEUE + || enqueue == -ERTS_ENQUEUE_NORMAL_QUEUE); - if (!have_seqtrace(token)) { -#ifdef SHCOPY_SEND - INITIALIZE_SHCOPY(info); - term_size = copy_shared_calculate(exit_term, &info); - mp = erts_alloc_message_heap(to, to_locksp, term_size, &hp, &ohp); - mess = copy_shared_perform(exit_term, term_size, &info, &hp, ohp); - DESTROY_SHCOPY(info); -#else - mp = erts_alloc_message_heap(to, to_locksp, term_size, &hp, &ohp); - mess = copy_struct(exit_term, term_size, &hp, ohp); -#endif - erts_queue_message(to, *to_locksp, mp, mess, am_system); - } else { - Eterm temp_token; - Uint sz_token; - - ASSERT(is_tuple(token)); - sz_token = size_object(token); -#ifdef SHCOPY_SEND - INITIALIZE_SHCOPY(info); - term_size = copy_shared_calculate(exit_term, &info); - mp = erts_alloc_message_heap(to, to_locksp, term_size+sz_token, &hp, &ohp); - mess = copy_shared_perform(exit_term, term_size, &info, &hp, ohp); - DESTROY_SHCOPY(info); -#else - mp = erts_alloc_message_heap(to, to_locksp, term_size+sz_token, &hp, &ohp); - mess = copy_struct(exit_term, term_size, &hp, ohp); -#endif - /* the trace token must in this case be updated by the caller */ - seq_trace_output(token, mess, SEQ_TRACE_SEND, to->common.id, to); - temp_token = copy_struct(token, sz_token, &hp, ohp); - ERL_MESSAGE_TOKEN(mp) = temp_token; - erts_queue_message(to, *to_locksp, mp, mess, am_system); - } + erts_proc_unlock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); + if (enqueue) + add2runq(enqueue, enq_prio, c_p, state, NULL); } -/* - * - * *** Exit signal behavior *** - * - * Exit signals are asynchronous (truly asynchronous in the - * SMP emulator). When the signal is received the receiver receives an - * 'EXIT' message if it is trapping exits; otherwise, it will either - * ignore the signal if the exit reason is normal, or go into an - * exiting state (ERTS_PSFLG_EXITING). When a process has gone into the - * exiting state it will not execute any more Erlang code, but it might - * take a while before it actually exits. The exit signal is being - * received when the 'EXIT' message is put in the message queue, the - * signal is dropped, or when it changes state into exiting. The time it - * is in the exiting state before actually exiting is undefined (it - * might take a really long time under certain conditions). The - * receiver of the exit signal does not break links or trigger monitors - * until it actually exits. - * - * Exit signals and other signals, e.g. messages, have to be received - * by a receiver in the same order as sent by a sender. - * - * - * - * Exit signal implementation in the SMP emulator: - * - * If the receiver is trapping exits, the signal is transformed - * into an 'EXIT' message and sent as a normal message, if the - * reason is normal the signal is dropped; otherwise, the process - * is determined to be exited. The interesting case is when the - * process is to be exited and this is what is described below. - * - * If it is possible, the receiver is set in the exiting state straight - * away and we are done; otherwise, the sender places the exit reason - * in the pending_exit field of the process struct and if necessary - * adds the receiver to the run queue. It is typically not possible - * to set a scheduled process or a process which we cannot get all locks - * on without releasing locks on it in an exiting state straight away. - * - * The receiver will poll the pending_exit field when it reach certain - * places during it's execution. When it discovers the pending exit - * it will change state into the exiting state. If the receiver wasn't - * scheduled when the pending exit was set, the first scheduler that - * schedules a new process will set the receiving process in the exiting - * state just before it schedules next process. - * - * When the exit signal is placed in the pending_exit field, the signal - * is considered as being in transit on the Erlang level. The signal is - * actually in some kind of semi transit state, since we have already - * determined how it should be received. It will exit the process no - * matter what if it is received (the process may exit by itself before - * reception of the exit signal). The signal is received when it is - * discovered in the pending_exit field by the receiver. - * - * The receiver have to poll the pending_exit field at least before: - * - moving messages from the message in queue to the private message - * queue. This in order to preserve signal order. - * - unlink. Otherwise the process might get exited on a link that - * have been removed. - * - changing the trap_exit flag to true. This in order to simplify the - * implementation; otherwise, we would have to transform the signal - * into an 'EXIT' message when setting the trap_exit flag to true. We - * would also have to maintain a queue of exit signals in transit. - * - being scheduled in or out. - */ - -static ERTS_INLINE int -send_exit_signal(Process *c_p, /* current process if and only - if reason is stored on it */ - Eterm from, /* Id of sender of signal */ - Process *rp, /* receiving process */ - ErtsProcLocks *rp_locks,/* current locks on receiver */ - Eterm reason, /* exit reason */ - Eterm exit_tuple, /* Prebuild exit tuple - or THE_NON_VALUE */ - Uint exit_tuple_sz, /* Size of prebuilt exit tuple - (if exit_tuple != THE_NON_VALUE) */ - Eterm token, /* token */ - Process *token_update, /* token updater */ - Uint32 flags /* flags */ - ) -{ - erts_aint32_t state = erts_atomic32_read_nob(&rp->state); - Eterm rsn = reason == am_kill ? am_killed : reason; - - ERTS_LC_ASSERT(*rp_locks == erts_proc_lc_my_proc_locks(rp)); - ERTS_LC_ASSERT((*rp_locks & ERTS_PROC_LOCKS_XSIG_SEND) - == ERTS_PROC_LOCKS_XSIG_SEND); - - ASSERT(reason != THE_NON_VALUE); - -#ifdef USE_VM_PROBES - if(DTRACE_ENABLED(process_exit_signal) && is_pid(from)) { - DTRACE_CHARBUF(sender_str, DTRACE_TERM_BUF_SIZE); - DTRACE_CHARBUF(receiver_str, DTRACE_TERM_BUF_SIZE); - DTRACE_CHARBUF(reason_buf, DTRACE_TERM_BUF_SIZE); - - dtrace_pid_str(from, sender_str); - dtrace_proc_str(rp, receiver_str); - erts_snprintf(reason_buf, sizeof(DTRACE_CHARBUF_NAME(reason_buf)) - 1, "%T", reason); - DTRACE3(process_exit_signal, sender_str, receiver_str, reason_buf); +void +erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt) +{ + Process *c_p = ((ErtsProcExitContext *) vctxt)->c_p; + Eterm reason = ((ErtsProcExitContext *) vctxt)->reason; + ErtsMonitorData *mdp = NULL; + + if (erts_monitor_is_target(mon)) { + /* We are being watched... */ + switch (mon->type) { + case ERTS_MON_TYPE_PROC: + erts_proc_sig_send_monitor_down(mon, reason); + mon = NULL; + break; + case ERTS_MON_TYPE_PORT: { + Port *prt; + ASSERT(is_internal_port(mon->other.item)); + prt = erts_id2port(mon->other.item); + if (prt) { + erts_fire_port_monitor(prt, mon); + erts_port_release(prt); + mon = NULL; + } + break; + } + case ERTS_MON_TYPE_RESOURCE: + erts_fire_nif_monitor(mon); + mon = NULL; + break; + case ERTS_MON_TYPE_DIST_PROC: { + ErtsMonLnkDist *dist; + DistEntry *dep; + ErtsDSigData dsd; + int code; + Eterm watcher; + Eterm watched; + + mdp = erts_monitor_to_data(mon); + + if (mon->flags & ERTS_ML_FLG_NAME) + watched = ((ErtsMonitorDataExtended *) mdp)->u.name; + else + watched = c_p->common.id; + ASSERT(is_internal_pid(watched) || is_atom(watched)); + + watcher = mon->other.item; + ASSERT(is_external_pid(watcher)); + dep = external_pid_dist_entry(watcher); + ASSERT(dep); + dist = ((ErtsMonitorDataExtended *) mdp)->dist; + ASSERT(dist); + code = erts_dsig_prepare(&dsd, dep, NULL, 0, + ERTS_DSP_NO_LOCK, 0, 0); + switch (code) { + case ERTS_DSIG_PREP_CONNECTED: + case ERTS_DSIG_PREP_PENDING: + if (dist->connection_id == dsd.connection_id) { + code = erts_dsig_send_m_exit(&dsd, + watcher, + watched, + mdp->ref, + reason); + ASSERT(code == ERTS_DSIG_SEND_OK); + } + default: + break; + } + if (!erts_monitor_dist_delete(&mdp->origin)) + mdp = NULL; + break; + } + default: + ERTS_INTERNAL_ERROR("Invalid target monitor type"); + break; + } } -#endif - - if ((state & ERTS_PSFLG_TRAP_EXIT) - && (reason != am_kill || (flags & ERTS_XSIG_FLG_IGN_KILL))) { - /* have to release the status and trace lock in order to send the exit message */ - erts_proc_unlock(rp, *rp_locks & (ERTS_PROC_LOCKS_XSIG_SEND|ERTS_PROC_LOCK_TRACE)); - *rp_locks &= ~(ERTS_PROC_LOCKS_XSIG_SEND|ERTS_PROC_LOCK_TRACE); - if (have_seqtrace(token) && token_update) - seq_trace_update_send(token_update); - if (is_value(exit_tuple)) - send_exit_message(rp, rp_locks, exit_tuple, exit_tuple_sz, token); - else - erts_deliver_exit_message(from, rp, rp_locks, rsn, token); - return 1; /* Receiver will get a message */ - } - else if (reason != am_normal || (flags & ERTS_XSIG_FLG_NO_IGN_NORMAL)) { - if (!(state & (ERTS_PSFLG_EXITING|ERTS_PSFLG_PENDING_EXIT))) { - ASSERT(!rp->pending_exit.bp); - - if (rp == c_p && (*rp_locks & ERTS_PROC_LOCK_MAIN)) { - /* Ensure that all locks on c_p are locked before - proceeding... */ - if (*rp_locks != ERTS_PROC_LOCKS_ALL) { - ErtsProcLocks need_locks = (~(*rp_locks) - & ERTS_PROC_LOCKS_ALL); - if (erts_proc_trylock(c_p, need_locks) == EBUSY) { - erts_proc_unlock(c_p, - *rp_locks & ~ERTS_PROC_LOCK_MAIN); - erts_proc_lock(c_p, ERTS_PROC_LOCKS_ALL_MINOR); - } - *rp_locks = ERTS_PROC_LOCKS_ALL; - } - set_proc_exiting(c_p, state, rsn, NULL); - } - else if (!(state & (ERTS_PSFLG_RUNNING - | ERTS_PSFLG_RUNNING_SYS - | ERTS_PSFLG_DIRTY_RUNNING_SYS))) { - /* Process not running ... */ - ErtsProcLocks need_locks = ~(*rp_locks) & ERTS_PROC_LOCKS_ALL; - ErlHeapFragment *bp = NULL; - Eterm rsn_cpy; - if (need_locks - && erts_proc_trylock(rp, need_locks) == EBUSY) { - /* ... but we havn't got all locks on it ... */ - save_pending_exiter(rp, NULL); - /* - * The pending exit will be discovered when next - * process is scheduled in - */ - goto set_pending_exit; - } - /* ...and we have all locks on it... */ - *rp_locks = ERTS_PROC_LOCKS_ALL; - - state = erts_atomic32_read_nob(&rp->state); - - if (is_immed(rsn)) - rsn_cpy = rsn; - else { - Eterm *hp; - ErlOffHeap *ohp; - Uint rsn_sz = size_object(rsn); - if (state & ERTS_PSFLG_DIRTY_RUNNING) { - bp = new_message_buffer(rsn_sz); - ohp = &bp->off_heap; - hp = &bp->mem[0]; - } - else - { - hp = HAlloc(rp, rsn_sz); - ohp = &rp->off_heap; - } - rsn_cpy = copy_struct(rsn, rsn_sz, &hp, ohp); - } - - set_proc_exiting(rp, state, rsn_cpy, bp); - } - else { /* Process running... */ - - /* - * The pending exit will be discovered when the process - * is scheduled out if not discovered earlier. - */ - - set_pending_exit: - if (is_immed(rsn)) { - rp->pending_exit.reason = rsn; - } - else { - Eterm *hp; - Uint sz = size_object(rsn); - ErlHeapFragment *bp = new_message_buffer(sz); - - hp = &bp->mem[0]; - rp->pending_exit.reason = copy_struct(rsn, - sz, - &hp, - &bp->off_heap); - rp->pending_exit.bp = bp; - } - - /* - * If no dirty work has been scheduled, pending exit will - * be discovered when the process is scheduled. If dirty work - * has been scheduled, we may need to add it to a normal run - * queue... - */ - { - erts_aint32_t a = erts_atomic32_read_nob(&rp->state); - while (1) { - erts_aint32_t n, e; - int dwork; - n = e = a; - n |= ERTS_PSFLG_PENDING_EXIT; - dwork = !!(n & ERTS_PSFLGS_DIRTY_WORK); - n &= ~ERTS_PSFLGS_DIRTY_WORK; - a = erts_atomic32_cmpxchg_mb(&rp->state, n, e); - if (a == e) { - if (dwork) - erts_schedule_process(rp, n, *rp_locks); - break; - } - } + else { /* Origin monitor */ + /* We are watching someone else... */ + switch (mon->type) { + case ERTS_MON_TYPE_PROC: + erts_proc_sig_send_demonitor(mon); + mon = NULL; + break; + case ERTS_MON_TYPE_TIME_OFFSET: + erts_demonitor_time_offset(mon); + mon = NULL; + break; + case ERTS_MON_TYPE_NODE: + mdp = erts_monitor_to_data(mon); + if (!erts_monitor_dist_delete(&mdp->target)) + mdp = NULL; + break; + case ERTS_MON_TYPE_NODES: + erts_monitor_nodes_delete(mon); + mon = NULL; + break; + case ERTS_MON_TYPE_PORT: { + Port *prt; + ASSERT(is_internal_port(mon->other.item)); + prt = erts_port_lookup_raw(mon->other.item); + if (prt) { + erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); + if (erts_port_demonitor(c_p, prt, mon) != ERTS_PORT_OP_DROPPED) + mon = NULL; + erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); + } + break; + } + case ERTS_MON_TYPE_DIST_PROC: { + ErtsMonLnkDist *dist; + DistEntry *dep; + ErtsDSigData dsd; + int code; + Eterm watched; + + mdp = erts_monitor_to_data(mon); + dist = ((ErtsMonitorDataExtended *) mdp)->dist; + ASSERT(dist); + if (mon->flags & ERTS_ML_FLG_NAME) { + watched = ((ErtsMonitorDataExtended *) mdp)->u.name; + ASSERT(is_atom(watched)); + dep = erts_sysname_to_connected_dist_entry(dist->nodename); + } + else { + watched = mon->other.item; + ASSERT(is_external_pid(watched)); + dep = external_pid_dist_entry(watched); + } + code = erts_dsig_prepare(&dsd, dep, NULL, 0, + ERTS_DSP_NO_LOCK, 0, 0); + switch (code) { + case ERTS_DSIG_PREP_CONNECTED: + case ERTS_DSIG_PREP_PENDING: + if (dist->connection_id == dsd.connection_id) { + code = erts_dsig_send_demonitor(&dsd, + c_p->common.id, + watched, + mdp->ref, + 1); + ASSERT(code == ERTS_DSIG_SEND_OK); } - } - } - /* else: - * - * The receiver already has a pending exit (or is exiting) - * so we drop this signal. - * - * NOTE: dropping this exit signal is based on the assumption - * that the receiver *will* exit; either on the pending - * exit or by itself before seeing the pending exit. - */ - return -1; /* Receiver will exit */ + default: + break; + } + if (!erts_monitor_dist_delete(&mdp->target)) + mdp = NULL; + break; + } + default: + ERTS_INTERNAL_ERROR("Invalid origin monitor type"); + break; + } } - return 0; /* Receiver unaffected */ + if (mdp) + erts_monitor_release_both(mdp); + else if (mon) + erts_monitor_release(mon); } - -int -erts_send_exit_signal(Process *c_p, - Eterm from, - Process *rp, - ErtsProcLocks *rp_locks, - Eterm reason, - Eterm token, - Process *token_update, - Uint32 flags) -{ - return send_exit_signal(c_p, - from, - rp, - rp_locks, - reason, - THE_NON_VALUE, - 0, - token, - token_update, - flags); -} - -typedef struct { - Eterm reason; - Process *p; -} ExitMonitorContext; - -static void doit_exit_monitor(ErtsMonitor *mon, void *vpcontext) -{ - ExitMonitorContext *pcontext = vpcontext; - DistEntry *dep; - ErtsMonitor *rmon; - - switch (mon->type) { - case MON_ORIGIN: - /* We are monitoring someone else, we need to demonitor that one.. */ - if (is_atom(mon->u.pid)) { /* remote by name */ - ASSERT(is_node_name_atom(mon->u.pid)); - dep = erts_sysname_to_connected_dist_entry(mon->u.pid); - if (dep) { - erts_de_links_lock(dep); - rmon = erts_remove_monitor(&(dep->monitors), mon->ref); - erts_de_links_unlock(dep); - if (rmon) { - ErtsDSigData dsd; - int code = erts_dsig_prepare(&dsd, dep, NULL, 0, - ERTS_DSP_NO_LOCK, 0, 0); - if (code == ERTS_DSIG_PREP_CONNECTED || - code == ERTS_DSIG_PREP_PENDING) { - - code = erts_dsig_send_demonitor(&dsd, - rmon->u.pid, - mon->name, - mon->ref, - 1); - ASSERT(code == ERTS_DSIG_SEND_OK); - } - erts_destroy_monitor(rmon); - } - } - } else { - ASSERT(is_pid(mon->u.pid) || is_port(mon->u.pid)); - /* if is local by pid or name */ - if (is_internal_pid(mon->u.pid)) { - Process *rp = erts_pid2proc(NULL, 0, mon->u.pid, ERTS_PROC_LOCK_LINK); - if (!rp) { - goto done; - } - rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref); - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - if (rmon == NULL) { - goto done; - } - erts_destroy_monitor(rmon); - } else if (is_internal_port(mon->u.pid)) { - /* Is a local port */ - Port *prt = erts_port_lookup_raw(mon->u.pid); - if (!prt) { - goto done; - } - erts_port_demonitor(pcontext->p, - ERTS_PORT_DEMONITOR_ORIGIN_ON_DEATHBED, - prt, mon->ref, NULL); - } else { /* remote by pid */ - ASSERT(is_external_pid(mon->u.pid)); - dep = external_pid_dist_entry(mon->u.pid); - ASSERT(dep != NULL); - if (dep) { - erts_de_links_lock(dep); - rmon = erts_remove_monitor(&(dep->monitors), mon->ref); - erts_de_links_unlock(dep); - if (rmon) { - ErtsDSigData dsd; - int code = erts_dsig_prepare(&dsd, dep, NULL, 0, - ERTS_DSP_NO_LOCK, 0, 0); - if (code == ERTS_DSIG_PREP_CONNECTED || - code == ERTS_DSIG_PREP_PENDING) { - - code = erts_dsig_send_demonitor(&dsd, - rmon->u.pid, - mon->u.pid, - mon->ref, - 1); - ASSERT(code == ERTS_DSIG_SEND_OK); - } - erts_destroy_monitor(rmon); - } - } - } - } - break; - case MON_TARGET: - ASSERT(is_pid(mon->u.pid) || is_internal_port(mon->u.pid)); - if (is_internal_port(mon->u.pid)) { - Port *prt = erts_id2port(mon->u.pid); - if (prt == NULL) { - goto done; - } - erts_fire_port_monitor(prt, mon->ref); - erts_port_release(prt); - } else if (is_internal_pid(mon->u.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); - rp = erts_pid2proc(NULL, 0, mon->u.pid, rp_locks); - if (rp == NULL) { - goto done; - } - UseTmpHeapNoproc(3); - rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref); - if (rmon) { - erts_destroy_monitor(rmon); - watched = (is_atom(mon->name) - ? TUPLE2(lhp, mon->name, - erts_this_dist_entry->sysname) - : pcontext->p->common.id); - erts_queue_monitor_message(rp, &rp_locks, mon->ref, am_process, - watched, pcontext->reason); - } - UnUseTmpHeapNoproc(3); - /* else: demonitor while we exited, i.e. do nothing... */ - erts_proc_unlock(rp, rp_locks); - } else { /* external by pid or name */ - ASSERT(is_external_pid(mon->u.pid)); - dep = external_pid_dist_entry(mon->u.pid); - ASSERT(dep != NULL); - if (dep) { - erts_de_links_lock(dep); - rmon = erts_remove_monitor(&(dep->monitors), mon->ref); - erts_de_links_unlock(dep); - if (rmon) { - ErtsDSigData dsd; - int code = erts_dsig_prepare(&dsd, dep, NULL, 0, - ERTS_DSP_NO_LOCK, 0, 0); - if (code == ERTS_DSIG_PREP_CONNECTED) { - code = erts_dsig_send_m_exit(&dsd, - mon->u.pid, - (rmon->name != NIL - ? rmon->name - : rmon->u.pid), - mon->ref, - pcontext->reason); - ASSERT(code == ERTS_DSIG_SEND_OK); - } - erts_destroy_monitor(rmon); - } - } - } - break; - case MON_NIF_TARGET: - erts_fire_nif_monitor(mon->u.resource, - pcontext->p->common.id, - mon->ref); +void +erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt) +{ + Process *c_p = ((ErtsProcExitContext *) vctxt)->c_p; + Eterm reason = ((ErtsProcExitContext *) vctxt)->reason; + ErtsLinkData *ldp = NULL; + + switch (lnk->type) { + case ERTS_LNK_TYPE_PROC: + ASSERT(is_internal_pid(lnk->other.item)); + erts_proc_sig_send_link_exit(c_p, c_p->common.id, lnk, + reason, SEQ_TRACE_TOKEN(c_p)); + lnk = NULL; + break; + case ERTS_LNK_TYPE_PORT: { + Port *prt; + ASSERT(is_internal_port(lnk->other.item)); + prt = erts_port_lookup(lnk->other.item, + ERTS_PORT_SFLGS_INVALID_LOOKUP); + if (prt) + erts_port_exit(NULL, + (ERTS_PORT_SIG_FLG_FORCE_SCHED + | ERTS_PORT_SIG_FLG_BROKEN_LINK), + prt, + c_p->common.id, + reason, + NULL); + break; + } + case ERTS_LNK_TYPE_DIST_PROC: { + DistEntry *dep; + ErtsMonLnkDist *dist; + ErtsLink *dlnk; + ErtsDSigData dsd; + int code; + + dlnk = erts_link_to_other(lnk, &ldp); + dist = ((ErtsLinkDataExtended *) ldp)->dist; + + ASSERT(is_external_pid(lnk->other.item)); + dep = external_pid_dist_entry(lnk->other.item); + + ASSERT(dep != erts_this_dist_entry); + + if (!erts_link_dist_delete(dlnk)) + ldp = NULL; + + erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN); + code = erts_dsig_prepare(&dsd, dep, c_p, 0, ERTS_DSP_NO_LOCK, 0, 0); + switch (code) { + case ERTS_DSIG_PREP_CONNECTED: + case ERTS_DSIG_PREP_PENDING: + if (dist->connection_id == dsd.connection_id) { + code = erts_dsig_send_exit_tt(&dsd, + c_p->common.id, + lnk->other.item, + reason, + SEQ_TRACE_TOKEN(c_p)); + ASSERT(code == ERTS_DSIG_SEND_OK); + } + } + erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); break; - case MON_TIME_OFFSET: - erts_demonitor_time_offset(mon->ref); - break; - default: - ERTS_INTERNAL_ERROR("Invalid monitor type"); } - done: - /* As the monitors are previously removed from the process, - distribution operations will not cause monitors to disappear, - we can safely delete it. */ - - erts_destroy_monitor(mon); -} - -typedef struct { - Process *p; - Eterm reason; - Eterm exit_tuple; - Uint exit_tuple_sz; -} ExitLinkContext; - -static void doit_exit_link(ErtsLink *lnk, void *vpcontext) -{ - ExitLinkContext *pcontext = vpcontext; - /* Unpack context, it's readonly */ - Process *p = pcontext->p; - Eterm reason = pcontext->reason; - Eterm exit_tuple = pcontext->exit_tuple; - Uint exit_tuple_sz = pcontext->exit_tuple_sz; - Eterm item = lnk->pid; - ErtsLink *rlnk; - DistEntry *dep; - Process *rp; - - - switch(lnk->type) { - case LINK_PID: - if(is_internal_port(item)) { - Port *prt = erts_port_lookup(item, ERTS_PORT_SFLGS_INVALID_LOOKUP); - if (prt) - erts_port_exit(NULL, - (ERTS_PORT_SIG_FLG_FORCE_SCHED - | ERTS_PORT_SIG_FLG_BROKEN_LINK), - prt, - p->common.id, - reason, - NULL); - } - else if(is_external_port(item)) { - erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf(); - erts_dsprintf(dsbufp, - "Erroneous link between %T and external port %T " - "found\n", - p->common.id, - item); - erts_send_error_to_logger_nogl(dsbufp); - ASSERT(0); /* It isn't possible to setup such a link... */ - } - else if (is_internal_pid(item)) { - ErtsProcLocks rp_locks = (ERTS_PROC_LOCK_LINK - | ERTS_PROC_LOCKS_XSIG_SEND); - rp = erts_pid2proc(NULL, 0, item, rp_locks); - if (rp) { - rlnk = erts_remove_link(&ERTS_P_LINKS(rp), p->common.id); - /* If rlnk == NULL, we got unlinked while exiting, - i.e., do nothing... */ - if (rlnk) { - int xres; - erts_destroy_link(rlnk); - xres = send_exit_signal(NULL, - p->common.id, - rp, - &rp_locks, - reason, - exit_tuple, - exit_tuple_sz, - SEQ_TRACE_TOKEN(p), - p, - ERTS_XSIG_FLG_IGN_KILL); - if (xres >= 0 && IS_TRACED_FL(rp, F_TRACE_PROCS)) { - /* We didn't exit the process and it is traced */ - if (IS_TRACED_FL(rp, F_TRACE_PROCS)) { - if (rp_locks & ERTS_PROC_LOCKS_XSIG_SEND) { - erts_proc_unlock(rp, ERTS_PROC_LOCKS_XSIG_SEND); - rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND; - } - trace_proc(NULL, 0, rp, am_getting_unlinked, p->common.id); - } - } - } - ASSERT(rp != p); - erts_proc_unlock(rp, rp_locks); - } - } - else if (is_external_pid(item)) { - dep = external_pid_dist_entry(item); - if(dep != erts_this_dist_entry) { - ErtsDSigData dsd; - int code; - ErtsDistLinkData dld; - erts_remove_dist_link(&dld, p->common.id, item, dep); - if (dld.d_lnk) { - erts_proc_lock(p, ERTS_PROC_LOCK_MAIN); - code = erts_dsig_prepare(&dsd, dep, p, 0, ERTS_DSP_NO_LOCK, 0, 0); - if (code == ERTS_DSIG_PREP_CONNECTED || - code == ERTS_DSIG_PREP_PENDING) { - - code = erts_dsig_send_exit_tt(&dsd, p->common.id, item, - reason, SEQ_TRACE_TOKEN(p)); - ASSERT(code == ERTS_DSIG_SEND_OK); - } - erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN); - } - erts_destroy_dist_link(&dld); - } - } - break; - case LINK_NODE: - ASSERT(is_node_name_atom(item)); - dep = erts_sysname_to_connected_dist_entry(item); - if(dep) { - /* dist entries have node links in a separate structure to - avoid confusion */ - erts_de_links_lock(dep); - rlnk = erts_remove_link(&(dep->node_links), p->common.id); - erts_de_links_unlock(dep); - if (rlnk) - erts_destroy_link(rlnk); - } - break; - default: - erts_exit(ERTS_ERROR_EXIT, "bad type in link list\n"); - break; + ERTS_INTERNAL_ERROR("Unexpected link type"); + break; } - erts_destroy_link(lnk); + + if (ldp) + erts_link_release_both(ldp); + else if (lnk) + erts_link_release(lnk); } static void -resume_suspend_monitor(ErtsSuspendMonitor *smon, void *vc_p) +resume_suspend_monitor(ErtsMonitor *mon, void *vc_p) { + ErtsMonitorSuspend *smon = erts_monitor_suspend(mon); Process *suspendee = erts_pid2proc((Process *) vc_p, ERTS_PROC_LOCK_MAIN, - smon->pid, ERTS_PROC_LOCK_STATUS); + smon->mon.other.item, ERTS_PROC_LOCK_STATUS); if (suspendee) { ASSERT(suspendee != vc_p); if (smon->active) resume_process(suspendee, ERTS_PROC_LOCK_STATUS); erts_proc_unlock(suspendee, ERTS_PROC_LOCK_STATUS); } - erts_destroy_suspend_monitor(smon); + erts_monitor_suspend_destroy(smon); } /* this function fishishes a process and propagates exit messages - called @@ -12904,8 +12651,6 @@ void erts_do_exit_process(Process* p, Eterm reason) { p->arity = 0; /* No live registers */ - p->fvalue = reason; - #ifdef USE_VM_PROBES if (DTRACE_ENABLED(process_exit)) { @@ -12929,18 +12674,11 @@ erts_do_exit_process(Process* p, Eterm reason) process from exiting until the lock has been released. */ erts_proc_lock(p, ERTS_PROC_LOCKS_ALL_MINOR); - if (ERTS_PSFLG_PENDING_EXIT & set_proc_self_exiting(p)) { - /* Process exited before pending exit was received... */ - p->pending_exit.reason = THE_NON_VALUE; - if (p->pending_exit.bp) { - free_message_buffer(p->pending_exit.bp); - p->pending_exit.bp = NULL; - } - } + set_self_exiting(p, reason, NULL, NULL, NULL); cancel_suspend_of_suspendee(p, ERTS_PROC_LOCKS_ALL); - ERTS_MSGQ_MV_INQ2PRIVQ(p); + erts_proc_sig_fetch(p); if (IS_TRACED(p)) { if (IS_TRACED_FL(p, F_TRACE_CALLS)) @@ -12977,13 +12715,15 @@ erts_do_exit_process(Process* p, Eterm reason) void erts_continue_exit_process(Process *p) { - ErtsLink* lnk; - ErtsMonitor *mon; + ErtsLink *links; + ErtsMonitor *monitors; + ErtsMonitor *lt_monitors; ErtsProcLocks curr_locks = ERTS_PROC_LOCK_MAIN; Eterm reason = p->fvalue; DistEntry *dep = NULL; erts_aint32_t state; int delay_del_proc = 0; + ErtsProcExitContext pectxt; #ifdef DEBUG int yield_allowed = 1; @@ -13055,16 +12795,13 @@ erts_continue_exit_process(Process *p) erts_set_gc_state(p, 1); state = erts_atomic32_read_acqb(&p->state); - if (state & ERTS_PSFLG_ACTIVE_SYS - || p->dirty_sys_tasks - ) { + if ((state & ERTS_PSFLG_SYS_TASKS) || p->dirty_sys_tasks) { if (cleanup_sys_tasks(p, state, CONTEXT_REDS) >= CONTEXT_REDS/2) goto yield; } #ifdef DEBUG erts_proc_lock(p, ERTS_PROC_LOCK_STATUS); - ASSERT(p->sys_task_qs == NULL); ASSERT(ERTS_PROC_GET_DELAYED_GC_TASK_QS(p) == NULL); ASSERT(p->dirty_sys_tasks == NULL); erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); @@ -13075,18 +12812,10 @@ erts_continue_exit_process(Process *p) p->flags &= ~F_USING_DDLL; } - if (p->nodes_monitors) { - erts_delete_nodes_monitors(p, ERTS_PROC_LOCK_MAIN); - p->nodes_monitors = NULL; - } - - - if (p->suspend_monitors) { - erts_sweep_suspend_monitors(p->suspend_monitors, - resume_suspend_monitor, - p); - p->suspend_monitors = NULL; - } + if (p->suspend_monitors) + erts_monitor_tree_foreach_delete(&p->suspend_monitors, + resume_suspend_monitor, + p); /* * The registered name *should* be the last "erlang resource" to @@ -13115,8 +12844,9 @@ erts_continue_exit_process(Process *p) * Note! The monitor and link fields will be overwritten * by erts_ptab_delete_element() below. */ - mon = ERTS_P_MONITORS(p); - lnk = ERTS_P_LINKS(p); + links = ERTS_P_LINKS(p); + monitors = ERTS_P_MONITORS(p); + lt_monitors = ERTS_P_LT_MONITORS(p); { /* Do *not* use erts_get_runq_proc() */ @@ -13153,7 +12883,9 @@ erts_continue_exit_process(Process *p) n = e = a; ASSERT(a & ERTS_PSFLG_EXITING); n |= ERTS_PSFLG_FREE; - n &= ~ERTS_PSFLG_ACTIVE; + n &= ~(ERTS_PSFLG_ACTIVE + | ERTS_PSFLG_ACTIVE_SYS + | ERTS_PSFLG_DIRTY_ACTIVE_SYS); if ((n & ERTS_PSFLG_IN_RUNQ) && !refc_inced) { erts_proc_inc_refc(p); refc_inced = 1; @@ -13181,40 +12913,70 @@ erts_continue_exit_process(Process *p) ? ERTS_PROC_SET_DIST_ENTRY(p, NULL) : NULL); - erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL); + + /* + * It might show up signal prio elevation tasks until we + * have entered free state. Cleanup such tasks now. + */ + state = erts_atomic32_read_acqb(&p->state); + if (!(state & ERTS_PSFLG_SYS_TASKS)) + erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL); + else { + erts_proc_unlock(p, ERTS_PROC_LOCKS_ALL_MINOR); + + do { + (void) cleanup_sys_tasks(p, state, CONTEXT_REDS); + state = erts_atomic32_read_acqb(&p->state); + } while (state & ERTS_PSFLG_SYS_TASKS); + + erts_proc_unlock(p, ERTS_PROC_LOCK_MAIN); + } + +#ifdef DEBUG + erts_proc_lock(p, ERTS_PROC_LOCK_STATUS); + ASSERT(p->sys_task_qs == NULL); + erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS); +#endif if (dep) { erts_do_net_exits(dep, (reason == am_kill) ? am_killed : reason); erts_deref_dist_entry(dep); } - /* - * Pre-build the EXIT tuple if there are any links. - */ - if (lnk) { - DeclareTmpHeap(tmp_heap,4,p); - Eterm exit_tuple; - Uint exit_tuple_sz; - Eterm* hp; - - UseTmpHeap(4,p); - hp = &tmp_heap[0]; + pectxt.c_p = p; + pectxt.reason = reason; - exit_tuple = TUPLE3(hp, am_EXIT, p->common.id, reason); + if (links) { + erts_link_tree_foreach_delete(&links, + erts_proc_exit_handle_link, + (void *) &pectxt); + ASSERT(!links); + } - exit_tuple_sz = size_object(exit_tuple); + if (monitors) { + erts_monitor_tree_foreach_delete(&monitors, + erts_proc_exit_handle_monitor, + (void *) &pectxt); + ASSERT(!monitors); + } - { - ExitLinkContext context = {p, reason, exit_tuple, exit_tuple_sz}; - erts_sweep_links(lnk, &doit_exit_link, &context); - } - UnUseTmpHeap(4,p); + if (lt_monitors) { + erts_monitor_list_foreach_delete(<_monitors, + erts_proc_exit_handle_monitor, + (void *) &pectxt); + ASSERT(!lt_monitors); } - { - ExitMonitorContext context = {reason, p}; - erts_sweep_monitors(mon,&doit_exit_monitor,&context); /* Allocates TmpHeap, but we - have none here */ + erts_proc_sig_fetch(p); + /* + * erts_proc_sig_handle_exit() implements yielding. + * However, this function cannot handle it yet... loop + * until done... + */ + while (!0) { + int reds = CONTEXT_REDS; + if (erts_proc_sig_handle_exit(p, &reds)) + break; } erts_proc_lock(p, ERTS_PROC_LOCK_MAIN); @@ -13253,6 +13015,40 @@ erts_continue_exit_process(Process *p) BUMP_ALL_REDS(p); } +Process * +erts_try_lock_sig_free_proc(Eterm pid, ErtsProcLocks locks) +{ + Process *rp = erts_proc_lookup_raw(pid); + erts_aint32_t state; + + if (!rp) + return NULL; + + ERTS_LC_ASSERT(!erts_proc_lc_my_proc_locks(rp)); + + state = erts_atomic32_read_nob(&rp->state); + if (state & ERTS_PSFLG_EXITING) + return NULL; + + if (state & (ERTS_PSFLG_SIG_IN_Q|ERTS_PSFLG_SIG_Q)) + return ERTS_PROC_LOCK_BUSY; + if (erts_proc_trylock(rp, locks) == EBUSY) + return ERTS_PROC_LOCK_BUSY; + + state = erts_atomic32_read_nob(&rp->state); + if (state & ERTS_PSFLG_EXITING) { + erts_proc_unlock(rp, locks); + return NULL; + } + + if (state & (ERTS_PSFLG_SIG_IN_Q|ERTS_PSFLG_SIG_Q)) { + erts_proc_unlock(rp, locks); + return ERTS_PROC_LOCK_BUSY; + } + + return rp; +} + /* * Stack dump functions follow. */ diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h index 8581c6a639..f7c6c3435b 100644 --- a/erts/emulator/beam/erl_process.h +++ b/erts/emulator/beam/erl_process.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2017. All Rights Reserved. + * Copyright Ericsson AB 1996-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,7 +51,7 @@ typedef struct process Process; #include "erl_process_dict.h" #include "erl_node_container_utils.h" #include "erl_node_tables.h" -#include "erl_monitors.h" +#include "erl_monitor_link.h" #include "erl_hl_timer.h" #include "erl_time.h" #include "erl_atom_table.h" @@ -75,8 +75,6 @@ typedef struct process Process; #include "erl_thr_progress.h" #undef ERL_THR_PROGRESS_TSD_TYPE_ONLY -struct ErtsNodesMonitor_; - #define ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT_OPT 0 #define ERTS_HAVE_SCHED_UTIL_BALANCING_SUPPORT 0 @@ -311,7 +309,6 @@ typedef enum { ERTS_SSI_AUX_WORK_ASYNC_READY_CLEAN_IX, ERTS_SSI_AUX_WORK_MISC_THR_PRGR_IX, ERTS_SSI_AUX_WORK_MISC_IX, - ERTS_SSI_AUX_WORK_PENDING_EXITERS_IX, ERTS_SSI_AUX_WORK_SET_TMO_IX, ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK_IX, ERTS_SSI_AUX_WORK_YIELD_IX, @@ -345,8 +342,6 @@ typedef enum { (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_MISC_THR_PRGR_IX) #define ERTS_SSI_AUX_WORK_MISC \ (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_MISC_IX) -#define ERTS_SSI_AUX_WORK_PENDING_EXITERS \ - (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_PENDING_EXITERS_IX) #define ERTS_SSI_AUX_WORK_SET_TMO \ (((erts_aint32_t) 1) << ERTS_SSI_AUX_WORK_SET_TMO_IX) #define ERTS_SSI_AUX_WORK_MSEG_CACHE_CHECK \ @@ -405,9 +400,12 @@ typedef struct { } ErtsRunPrioQueue; typedef enum { - ERTS_SCHED_NORMAL, - ERTS_SCHED_DIRTY_CPU, - ERTS_SCHED_DIRTY_IO + ERTS_SCHED_NORMAL = 0, + ERTS_SCHED_DIRTY_CPU = 1, + ERTS_SCHED_DIRTY_IO = 2, + + ERTS_SCHED_TYPE_FIRST = ERTS_SCHED_NORMAL, + ERTS_SCHED_TYPE_LAST = ERTS_SCHED_DIRTY_IO } ErtsSchedType; typedef struct ErtsSchedulerData_ ErtsSchedulerData; @@ -491,7 +489,6 @@ struct ErtsRunQueue_ { int wakeup_other_reds; struct { - ErtsProcList *pending_exiters; Uint context_switches; Uint reductions; @@ -986,14 +983,10 @@ struct process { Process *next; /* Pointer to next process in run queue */ - struct ErtsNodesMonitor_ *nodes_monitors; - - ErtsSuspendMonitor *suspend_monitors; /* Processes suspended by - this process via - erlang:suspend_process/1 */ - - ErlMessageQueue msg; /* Message queue */ + ErtsMonitor *suspend_monitors; /* Processes suspended by this process via + erlang:suspend_process/1 */ + ErtsSignalPrivQueues sig_qs; /* Signal queues */ ErtsBifTimers *bif_timers; /* Bif timers aiming at this process */ ProcDict *dictionary; /* Process dictionary, may be NULL */ @@ -1022,7 +1015,6 @@ struct process { * Information mainly for post-mortem use (erl crash dump). */ Eterm parent; /* Pid of process that created this process. */ - erts_approx_time_t approx_started; /* Time when started. */ Uint32 static_flags; /* Flags that do *not* change */ @@ -1053,9 +1045,8 @@ struct process { erts_atomic32_t state; /* Process state flags (see ERTS_PSFLG_*) */ erts_atomic32_t dirty_state; /* Process dirty state flags (see ERTS_PDSFLG_*) */ - ErlMessageInQueue msg_inq; + ErtsSignalInQueue sig_inq; ErlTraceMessageQueue *trace_msg_q; - ErtsPendExit pending_exit; erts_proc_lock_t lock; ErtsSchedulerData *scheduler_data; Eterm suspendee; @@ -1089,6 +1080,7 @@ struct process { #endif }; +extern Eterm erts_init_process_id; /* pid of init process */ extern const Process erts_invalid_process; #ifdef CHECK_FOR_HOLES @@ -1165,20 +1157,20 @@ void erts_check_for_holes(Process* p); #define ERTS_PSFLG_IN_PRQ_LOW ERTS_PSFLG_BIT(3) #define ERTS_PSFLG_FREE ERTS_PSFLG_BIT(4) #define ERTS_PSFLG_EXITING ERTS_PSFLG_BIT(5) -#define ERTS_PSFLG_PENDING_EXIT ERTS_PSFLG_BIT(6) +#define ERTS_PSFLG_UNUSED ERTS_PSFLG_BIT(6) #define ERTS_PSFLG_ACTIVE ERTS_PSFLG_BIT(7) #define ERTS_PSFLG_IN_RUNQ ERTS_PSFLG_BIT(8) #define ERTS_PSFLG_RUNNING ERTS_PSFLG_BIT(9) #define ERTS_PSFLG_SUSPENDED ERTS_PSFLG_BIT(10) #define ERTS_PSFLG_GC ERTS_PSFLG_BIT(11) -/* #define ERTS_PSFLG_ ERTS_PSFLG_BIT(12) */ -#define ERTS_PSFLG_TRAP_EXIT ERTS_PSFLG_BIT(13) +#define ERTS_PSFLG_SYS_TASKS ERTS_PSFLG_BIT(12) +#define ERTS_PSFLG_SIG_IN_Q ERTS_PSFLG_BIT(13) #define ERTS_PSFLG_ACTIVE_SYS ERTS_PSFLG_BIT(14) #define ERTS_PSFLG_RUNNING_SYS ERTS_PSFLG_BIT(15) #define ERTS_PSFLG_PROXY ERTS_PSFLG_BIT(16) #define ERTS_PSFLG_DELAYED_SYS ERTS_PSFLG_BIT(17) #define ERTS_PSFLG_OFF_HEAP_MSGQ ERTS_PSFLG_BIT(18) -/* #define ERTS_PSFLG_ ERTS_PSFLG_BIT(19) */ +#define ERTS_PSFLG_SIG_Q ERTS_PSFLG_BIT(19) #define ERTS_PSFLG_DIRTY_CPU_PROC ERTS_PSFLG_BIT(20) #define ERTS_PSFLG_DIRTY_IO_PROC ERTS_PSFLG_BIT(21) #define ERTS_PSFLG_DIRTY_ACTIVE_SYS ERTS_PSFLG_BIT(22) @@ -1197,7 +1189,6 @@ void erts_check_for_holes(Process* p); | ERTS_PSFLG_IN_PRQ_LOW) #define ERTS_PSFLGS_VOLATILE_HEAP (ERTS_PSFLG_EXITING \ - | ERTS_PSFLG_PENDING_EXIT \ | ERTS_PSFLG_DIRTY_RUNNING \ | ERTS_PSFLG_DIRTY_RUNNING_SYS) @@ -1262,7 +1253,24 @@ void erts_check_for_holes(Process* p); #define SEQ_TRACE_T_SENDER(token) (*(tuple_val(token) + 4)) #define SEQ_TRACE_T_LASTCNT(token) (*(tuple_val(token) + 5)) +#ifdef USE_VM_PROBES +/* The dtrace probe for seq_trace only supports 'int' labels, so we represent + * all values that won't fit into a 32-bit signed integer as ERTS_SINT32_MIN + * (bigints, tuples, etc). */ + +#define SEQ_TRACE_T_DTRACE_LABEL(token) \ + DTRACE_SEQ_TRACE_LABEL__(SEQ_TRACE_T_LABEL(token)) + +#define DTRACE_SEQ_TRACE_LABEL__(label_term) \ + (is_small((label_term)) ? \ + ((signed_val((label_term)) <= ERTS_SINT32_MAX && \ + signed_val((label_term)) >= ERTS_SINT32_MIN) ? \ + signed_val((label_term)) : ERTS_SINT32_MIN) \ + : ERTS_SINT32_MIN) +#endif + /* + * Possible flags for the flags field in ErlSpawnOpts below. */ @@ -1380,6 +1388,9 @@ extern int erts_system_profile_ts_type; #define F_DIRTY_MAJOR_GC (1 << 23) /* Dirty major GC scheduled */ #define F_DIRTY_MINOR_GC (1 << 24) /* Dirty minor GC scheduled */ #define F_HIBERNATED (1 << 25) /* Hibernated */ +#define F_LOCAL_SIGS_ONLY (1 << 26) +#define F_TRAP_EXIT (1 << 27) /* Trapping exit */ +#define F_DEFERRED_SAVED_LAST (1 << 28) /* * F_DISABLE_GC and F_DELAY_GC are similar. Both will prevent @@ -1445,7 +1456,7 @@ extern int erts_system_profile_ts_type; | F_TRACE_ARITY_ONLY | F_TRACE_RETURN_TO \ | F_TRACE_SILENT | F_TRACE_SCHED_PROCS | F_TRACE_PORTS \ | F_TRACE_SCHED_PORTS | F_TRACE_SCHED_NO \ - | F_TRACE_SCHED_EXIT) + | F_TRACE_SCHED_EXIT ) #define ERTS_TRACEE_MODIFIER_FLAGS \ @@ -1458,6 +1469,14 @@ extern int erts_system_profile_ts_type; #define SEQ_TRACE_FLAG(N) (1 << (ERTS_TRACE_TS_TYPE_BITS + (N))) +#define ERTS_SIG_ENABLE_TRACE_FLAGS \ + ( F_TRACE_RECEIVE | F_TRACE_PROCS) + +/* + * F_TRACE_RECEIVE is always enabled/disable via signaling. + * F_TRACE_PROCS enable/disable F_TRACE_PROCS_SIG via signaling. + */ + /* Sequential trace flags */ /* SEQ_TRACE_TIMESTAMP_MASK is a bit-field */ @@ -1484,10 +1503,6 @@ extern int erts_system_profile_ts_type; #define DT_UTAG_FLAGS(P) ((P)->dt_utag_flags) #endif -/* Option flags to erts_send_exit_signal() */ -#define ERTS_XSIG_FLG_IGN_KILL (((Uint32) 1) << 0) -#define ERTS_XSIG_FLG_NO_IGN_NORMAL (((Uint32) 1) << 1) - #define CANCEL_TIMER(P) \ do { \ if ((P)->flags & (F_INSLPQUEUE|F_TIMO)) { \ @@ -1717,9 +1732,9 @@ ERTS_GLB_INLINE int erts_proclist_is_last(ErtsProcList *list, #endif -int erts_sched_set_wakeup_other_thresold(char *str); -int erts_sched_set_wakeup_other_type(char *str); -int erts_sched_set_busy_wait_threshold(char *str); +int erts_sched_set_wakeup_other_threshold(ErtsSchedType sched_type, char *str); +int erts_sched_set_wakeup_other_type(ErtsSchedType sched_type, char *str); +int erts_sched_set_busy_wait_threshold(ErtsSchedType sched_type, char *str); int erts_sched_set_wake_cleanup_threshold(char *); void erts_schedule_thr_prgr_later_op(void (*)(void *), @@ -1734,6 +1749,7 @@ struct db_fixation; void erts_schedule_ets_free_fixation(Eterm pid, struct db_fixation*); void erts_schedule_flush_trace_messages(Process *proc, int force_on_proc); int erts_flush_trace_messages(Process *c_p, ErtsProcLocks locks); +int erts_sig_prio(Eterm pid, int prio); #if defined(ERTS_ENABLE_LOCK_CHECK) int erts_dbg_check_halloc_lock(Process *p); @@ -1782,8 +1798,10 @@ ErtsRunQueue *erts_schedid2runq(Uint); Process *erts_schedule(ErtsSchedulerData *, Process*, int); void erts_schedule_misc_op(void (*)(void *), void *); Eterm erl_create_process(Process*, Eterm, Eterm, Eterm, ErlSpawnOpts*); +void erts_set_self_exiting(Process *, Eterm); void erts_do_exit_process(Process*, Eterm); void erts_continue_exit_process(Process *); +void erts_proc_exit_link(Process *, ErtsLink *, Uint16, Eterm, Eterm); /* Begin System profile */ Uint erts_runnable_process_count(void); /* End System profile */ @@ -1800,7 +1818,14 @@ void erts_print_run_queue_info(fmtfn_t, void *to_arg, ErtsRunQueue*); void erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg); void erts_dump_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg); -Eterm erts_get_process_priority(Process *p); +typedef struct { + Process *c_p; + Eterm reason; +} ErtsProcExitContext; +void erts_proc_exit_handle_monitor(ErtsMonitor *mon, void *vctxt); +void erts_proc_exit_handle_link(ErtsLink *lnk, void *vctxt); + +Eterm erts_get_process_priority(erts_aint32_t state); Eterm erts_set_process_priority(Process *p, Eterm prio); Uint erts_get_total_context_switches(void); @@ -1818,18 +1843,6 @@ void erts_suspend(Process*, ErtsProcLocks, Port*); void erts_resume(Process*, ErtsProcLocks); int erts_resume_processes(ErtsProcList *); -int erts_send_exit_signal(Process *, - Eterm, - Process *, - ErtsProcLocks *, - Eterm, - Eterm, - Process *, - Uint32); -void erts_handle_pending_exit(Process *, ErtsProcLocks); -#define ERTS_PROC_PENDING_EXIT(P) \ - (ERTS_PSFLG_PENDING_EXIT & erts_atomic32_read_acqb(&(P)->state)) - void erts_deep_process_dump(fmtfn_t, void *); Eterm erts_get_reader_groups_map(Process *c_p); @@ -1861,6 +1874,8 @@ do { \ ErtsSchedulerData *erts_get_scheduler_data(void); void erts_schedule_process(Process *, erts_aint32_t, ErtsProcLocks); +erts_aint32_t erts_proc_sys_schedule(Process *p, erts_aint32_t state, + erts_aint32_t enable_flag); ERTS_GLB_INLINE void erts_proc_notify_new_message(Process *p, ErtsProcLocks locks); ERTS_GLB_INLINE void erts_schedule_dirty_sys_execution(Process *c_p); @@ -1892,8 +1907,7 @@ erts_schedule_dirty_sys_execution(Process *c_p) /* Don't set dirty-active-sys if we are about to exit... */ while (!(a & (ERTS_PSFLG_DIRTY_ACTIVE_SYS - | ERTS_PSFLG_EXITING - | ERTS_PSFLG_PENDING_EXIT))) { + | ERTS_PSFLG_EXITING))) { e = a; n = a | ERTS_PSFLG_DIRTY_ACTIVE_SYS; a = erts_atomic32_cmpxchg_mb(&c_p->state, n, e); @@ -2314,6 +2328,8 @@ erts_try_change_runq_proc(Process *p, ErtsRunQueue *rq) { erts_aint_t old_rqint, new_rqint; + ASSERT(rq); + new_rqint = (erts_aint_t) rq; old_rqint = (erts_aint_t) erts_atomic_read_nob(&p->run_queue); while (1) { @@ -2578,6 +2594,8 @@ ERTS_TIME2REDS_IMPL__(ErtsMonotonicTime start, ErtsMonotonicTime end) } #endif +Process *erts_try_lock_sig_free_proc(Eterm pid, + ErtsProcLocks locks); Process *erts_pid2proc_not_running(Process *, ErtsProcLocks, diff --git a/erts/emulator/beam/erl_process_dict.c b/erts/emulator/beam/erl_process_dict.c index 3c80f0e0f6..aee88841ae 100644 --- a/erts/emulator/beam/erl_process_dict.c +++ b/erts/emulator/beam/erl_process_dict.c @@ -79,6 +79,8 @@ /* Array access macro */ #define ARRAY_GET(PDict, Index) (ASSERT((Index) < (PDict)->arraySize), \ (PDict)->data[Index]) +#define ARRAY_GET_PTR(PDict, Index) (ASSERT((Index) < (PDict)->arraySize), \ + &(PDict)->data[Index]) #define ARRAY_PUT(PDict, Index, Val) (ASSERT((Index) < (PDict)->arraySize), \ (PDict)->data[Index] = (Val)) @@ -92,7 +94,7 @@ static void pd_hash_erase_all(Process *p); static Eterm pd_hash_get_with_hval(Process *p, Eterm bucket, Eterm id); static Eterm pd_hash_get_keys(Process *p, Eterm value); static Eterm pd_hash_get_all_keys(Process *p, ProcDict *pd); -static Eterm pd_hash_get_all(Process *p, ProcDict *pd); +static Eterm pd_hash_get_all(Process *p, ProcDict *pd, int keep_dict); static Eterm pd_hash_put(Process *p, Eterm id, Eterm value); static void shrink(Process *p, Eterm* ret); @@ -281,7 +283,7 @@ BIF_RETTYPE get_0(BIF_ALIST_0) { Eterm ret; PD_CHECK(BIF_P->dictionary); - ret = pd_hash_get_all(BIF_P, BIF_P->dictionary); + ret = pd_hash_get_all(BIF_P, BIF_P->dictionary, 1); PD_CHECK(BIF_P->dictionary); BIF_RET(ret); } @@ -329,7 +331,7 @@ BIF_RETTYPE erase_0(BIF_ALIST_0) { Eterm ret; PD_CHECK(BIF_P->dictionary); - ret = pd_hash_get_all(BIF_P, BIF_P->dictionary); + ret = pd_hash_get_all(BIF_P, BIF_P->dictionary, 0); pd_hash_erase_all(BIF_P); PD_CHECK(BIF_P->dictionary); BIF_RET(ret); @@ -541,29 +543,46 @@ static Eterm pd_hash_get_keys(Process *p, Eterm value) static Eterm -pd_hash_get_all(Process *p, ProcDict *pd) +pd_hash_get_all(Process *p, ProcDict *pd, int keep_dict) { Eterm* hp; + Eterm* tp; Eterm res = NIL; Eterm tmp, tmp2; unsigned int i; unsigned int num; + Uint need; if (pd == NULL) { return res; } num = HASH_RANGE(pd); - hp = HAlloc(p, pd->numElements * 2); - + + /* + * If this is not erase/0, then must copy all key-value tuples + * as they may be mutated by put/2. + */ + need = pd->numElements * (keep_dict ? 2+3 : 2); + hp = HAlloc(p, need); + for (i = 0; i < num; ++i) { tmp = ARRAY_GET(pd, i); if (is_boxed(tmp)) { - ASSERT(is_tuple(tmp)); + if (keep_dict) { + tp = tuple_val(tmp); + tmp = TUPLE2(hp, tp[1], tp[2]); + hp += 3; + } res = CONS(hp, tmp, res); hp += 2; } else if (is_list(tmp)) { while (tmp != NIL) { tmp2 = TCAR(tmp); + if (keep_dict) { + tp = tuple_val(tmp2); + tmp2 = TUPLE2(hp, tp[1], tp[2]); + hp += 3; + } res = CONS(hp, tmp2, res); hp += 2; tmp = TCDR(tmp); @@ -577,11 +596,14 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) { unsigned int hval; Eterm *hp; + Eterm *tp; + Eterm *bucket; Eterm tpl; Eterm old; + Eterm old_val = am_undefined; Eterm tmp; int needed; - int i = 0; + int new_key = 1; #ifdef DEBUG Eterm *hp_limit; #endif @@ -595,7 +617,8 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) p->dictionary->numElements = 0; } hval = pd_hash_value(p->dictionary, id); - old = ARRAY_GET(p->dictionary, hval); + bucket = ARRAY_GET_PTR(p->dictionary, hval); + old = *bucket; /* * Calculate the number of heap words needed and garbage @@ -603,32 +626,49 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) */ needed = 3; /* {Key,Value} tuple */ if (is_boxed(old)) { - /* - * We don't want to compare keys twice, so we'll always - * reserve the space for two CONS cells. - */ - needed += 2+2; + ASSERT(is_tuple(old)); + tp = tuple_val(old); + if (EQ(tp[1], id)) { + old_val = tp[2]; + if (is_immed(value)) { + tp[2] = value; /* DESTRUCTIVE HEAP ASSIGNMENT */ + return old_val; + } + new_key = 0; + } + else { + needed += 2+2; + } } else if (is_list(old)) { - i = 0; - for (tmp = old; tmp != NIL && !EQ(tuple_val(TCAR(tmp))[1], id); tmp = TCDR(tmp)) { - ++i; - } - if (is_nil(tmp)) { - i = -1; - needed += 2; - } else { - needed += 2*(i+1); + Eterm* prev_cdr = bucket; + + needed += 2; + for (tmp = old; tmp != NIL; prev_cdr = &TCDR(tmp), tmp = *prev_cdr) { + tp = tuple_val(TCAR(tmp)); + if (EQ(tp[1], id)) { + old_val = tp[2]; + if (is_immed(value)) { + tp[2] = value; /* DESTRUCTIVE HEAP ASSIGNMENT */ + return old_val; + } + new_key = 0; + /* Unlink old {Key,Value} from list */ + *prev_cdr = TCDR(tmp); /* maybe DESTRUCTIVE HEAP ASSIGNMENT */ + break; + } } } if (HeapWordsLeft(p) < needed) { Eterm root[3]; root[0] = id; root[1] = value; - root[2] = old; + root[2] = old_val; erts_garbage_collect(p, needed, root, 3); id = root[0]; value = root[1]; - old = root[2]; + old_val = root[2]; + ASSERT(bucket == ARRAY_GET_PTR(p->dictionary, hval)); + old = *bucket; } #ifdef DEBUG hp_limit = p->htop + needed; @@ -644,66 +684,29 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) * Update the dictionary. */ if (is_nil(old)) { - ARRAY_PUT(p->dictionary, hval, tpl); - ++(p->dictionary->numElements); + *bucket = tpl; } else if (is_boxed(old)) { ASSERT(is_tuple(old)); - if (EQ(tuple_val(old)[1],id)) { - ARRAY_PUT(p->dictionary, hval, tpl); - return tuple_val(old)[2]; + if (!new_key) { + ASSERT(EQ(tuple_val(old)[1],id)); + *bucket = tpl; + return old_val; } else { hp = HeapOnlyAlloc(p, 4); tmp = CONS(hp, old, NIL); hp += 2; - ++(p->dictionary->numElements); - ARRAY_PUT(p->dictionary, hval, CONS(hp, tpl, tmp)); + *bucket = CONS(hp, tpl, tmp); hp += 2; ASSERT(hp <= hp_limit); } } else if (is_list(old)) { - if (i == -1) { - /* - * New key. Simply prepend the tuple to the beginning of the list. - */ - hp = HeapOnlyAlloc(p, 2); - ARRAY_PUT(p->dictionary, hval, CONS(hp, tpl, old)); - hp += 2; - ASSERT(hp <= hp_limit); - ++(p->dictionary->numElements); - } else { - /* - * i = Number of CDRs to skip to reach the changed element in the list. - * - * Replace old value in list. To avoid pointers from the old generation - * to the new, we must rebuild the list from the beginning up to and - * including the changed element. - */ - Eterm nlist; - int j; - - hp = HeapOnlyAlloc(p, (i+1)*2); - - /* Find the list element to change. */ - for (j = 0, nlist = old; j < i; j++, nlist = TCDR(nlist)) { - ; - } - ASSERT(EQ(tuple_val(TCAR(nlist))[1], id)); - nlist = TCDR(nlist); /* Unchanged part of list. */ - - /* Rebuild list before the updated element. */ - for (tmp = old; i-- > 0; tmp = TCDR(tmp)) { - nlist = CONS(hp, TCAR(tmp), nlist); - hp += 2; - } - ASSERT(EQ(tuple_val(TCAR(tmp))[1], id)); - - /* Put the updated element first in the new list. */ - nlist = CONS(hp, tpl, nlist); - hp += 2; - ASSERT(hp <= hp_limit); - ARRAY_PUT(p->dictionary, hval, nlist); - return tuple_val(TCAR(tmp))[2]; - } + /* + * Simply prepend the tuple to the beginning of the list. + */ + hp = HeapOnlyAlloc(p, 2); + *bucket = CONS(hp, tpl, *bucket); + hp += 2; + ASSERT(hp <= hp_limit); } else { #ifdef DEBUG erts_fprintf(stderr, @@ -714,10 +717,13 @@ static Eterm pd_hash_put(Process *p, Eterm id, Eterm value) erts_exit(ERTS_ERROR_EXIT, "Damaged process dictionary found during put/2."); } + + p->dictionary->numElements += new_key; + if (HASH_RANGE(p->dictionary) <= p->dictionary->numElements) { grow(p); } - return am_undefined; + return old_val; } /* diff --git a/erts/emulator/beam/erl_process_dump.c b/erts/emulator/beam/erl_process_dump.c index 05e7bcdea2..00659f9f49 100644 --- a/erts/emulator/beam/erl_process_dump.c +++ b/erts/emulator/beam/erl_process_dump.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2017. All Rights Reserved. + * Copyright Ericsson AB 2003-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ #include "erl_map.h" #define ERTS_WANT_EXTERNAL_TAGS #include "external.h" +#include "erl_proc_sig_queue.h" #define PTR_FMT "%bpX" #define ETERM_FMT "%beX" @@ -96,17 +97,35 @@ erts_deep_process_dump(fmtfn_t to, void *to_arg) dump_binaries(to, to_arg, all_binaries); } +static void +monitor_size(ErtsMonitor *mon, void *vsize) +{ + *((Uint *) vsize) += erts_monitor_size(mon); +} + +static void +link_size(ErtsMonitor *lnk, void *vsize) +{ + *((Uint *) vsize) += erts_link_size(lnk); +} + Uint erts_process_memory(Process *p, int incl_msg_inq) { - ErtsMessage *mp; Uint size = 0; struct saved_calls *scb; size += sizeof(Process); - if (incl_msg_inq) - ERTS_MSGQ_MV_INQ2PRIVQ(p); + if (incl_msg_inq) { + erts_proc_lock(p, ERTS_PROC_LOCK_MSGQ); + erts_proc_sig_fetch(p); + erts_proc_unlock(p, ERTS_PROC_LOCK_MSGQ); + } - erts_doforall_links(ERTS_P_LINKS(p), &erts_one_link_size, &size); - erts_doforall_monitors(ERTS_P_MONITORS(p), &erts_one_mon_size, &size); + erts_link_tree_foreach(ERTS_P_LINKS(p), + link_size, (void *) &size); + erts_monitor_tree_foreach(ERTS_P_MONITORS(p), + monitor_size, (void *) &size); + erts_monitor_list_foreach(ERTS_P_LT_MONITORS(p), + monitor_size, (void *) &size); size += (p->heap_sz + p->mbuf_sz) * sizeof(Eterm); if (p->abandoned_heap) size += (p->hend - p->heap) * sizeof(Eterm); @@ -114,11 +133,16 @@ Uint erts_process_memory(Process *p, int incl_msg_inq) { size += (p->old_hend - p->old_heap) * sizeof(Eterm); - size += p->msg.len * sizeof(ErtsMessage); + size += p->sig_qs.len * sizeof(ErtsMessage); - for (mp = p->msg.first; mp; mp = mp->next) - if (mp->data.attached) - size += erts_msg_attached_data_size(mp)*sizeof(Eterm); + ERTS_FOREACH_SIG_PRIVQS( + p, mp, + { + if (ERTS_SIG_IS_NON_MSG((ErtsSignal *) mp)) + size += erts_proc_sig_signal_size((ErtsSignal *) mp); + else if (mp->data.attached) + size += erts_msg_attached_data_size(mp) * sizeof(Eterm); + }); if (p->arg_reg != p->def_arg_reg) { size += p->arity * sizeof(p->arg_reg[0]); @@ -137,31 +161,48 @@ Uint erts_process_memory(Process *p, int incl_msg_inq) { return size; } +static ERTS_INLINE void +dump_msg(fmtfn_t to, void *to_arg, ErtsMessage *mp) +{ + if (ERTS_SIG_IS_MSG((ErtsSignal *) mp)) { + Eterm mesg = ERL_MESSAGE_TERM(mp); + if (is_value(mesg)) + dump_element(to, to_arg, mesg); + else + dump_dist_ext(to, to_arg, mp->data.dist_ext); + mesg = ERL_MESSAGE_TOKEN(mp); + erts_print(to, to_arg, ":"); + dump_element(to, to_arg, mesg); + erts_print(to, to_arg, "\n"); + } +} + +static ERTS_INLINE void +heap_dump_msg(fmtfn_t to, void *to_arg, ErtsMessage *mp) +{ + if (ERTS_SIG_IS_MSG((ErtsSignal *) mp)) { + Eterm mesg = ERL_MESSAGE_TERM(mp); + if (is_value(mesg)) + heap_dump(to, to_arg, mesg); + mesg = ERL_MESSAGE_TOKEN(mp); + heap_dump(to, to_arg, mesg); + } +} + static void dump_process_info(fmtfn_t to, void *to_arg, Process *p) { Eterm* sp; - ErtsMessage* mp; int yreg = -1; if (ERTS_TRACE_FLAGS(p) & F_SENSITIVE) return; - ERTS_MSGQ_MV_INQ2PRIVQ(p); + erts_proc_sig_fetch(p); - if (p->msg.first) { + if (p->sig_qs.first || p->sig_qs.cont) { erts_print(to, to_arg, "=proc_messages:%T\n", p->common.id); - for (mp = p->msg.first; mp != NULL; mp = mp->next) { - Eterm mesg = ERL_MESSAGE_TERM(mp); - if (is_value(mesg)) - dump_element(to, to_arg, mesg); - else - dump_dist_ext(to, to_arg, mp->data.dist_ext); - mesg = ERL_MESSAGE_TOKEN(mp); - erts_print(to, to_arg, ":"); - dump_element(to, to_arg, mesg); - erts_print(to, to_arg, "\n"); - } + ERTS_FOREACH_SIG_PRIVQS(p, mp, dump_msg(to, to_arg, mp)); } if (p->dictionary) { @@ -183,13 +224,10 @@ dump_process_info(fmtfn_t to, void *to_arg, Process *p) heap_dump(to, to_arg, term); } } - for (mp = p->msg.first; mp != NULL; mp = mp->next) { - Eterm mesg = ERL_MESSAGE_TERM(mp); - if (is_value(mesg)) - heap_dump(to, to_arg, mesg); - mesg = ERL_MESSAGE_TOKEN(mp); - heap_dump(to, to_arg, mesg); - } + + if (p->sig_qs.first || p->sig_qs.cont) + ERTS_FOREACH_SIG_PRIVQS(p, mp, heap_dump_msg(to, to_arg, mp)); + if (p->dictionary) { erts_deep_dictionary_dump(to, to_arg, p->dictionary, heap_dump); } @@ -997,8 +1035,8 @@ erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg) erts_print(to, to_arg, "FREE"); break; case ERTS_PSFLG_EXITING: erts_print(to, to_arg, "EXITING"); break; - case ERTS_PSFLG_PENDING_EXIT: - erts_print(to, to_arg, "PENDING_EXIT"); break; + case ERTS_PSFLG_UNUSED: + erts_print(to, to_arg, "UNUSED"); break; case ERTS_PSFLG_ACTIVE: erts_print(to, to_arg, "ACTIVE"); break; case ERTS_PSFLG_IN_RUNQ: @@ -1009,8 +1047,10 @@ erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg) erts_print(to, to_arg, "SUSPENDED"); break; case ERTS_PSFLG_GC: erts_print(to, to_arg, "GC"); break; - case ERTS_PSFLG_TRAP_EXIT: - erts_print(to, to_arg, "TRAP_EXIT"); break; + case ERTS_PSFLG_SYS_TASKS: + erts_print(to, to_arg, "SYS_TASKS"); break; + case ERTS_PSFLG_SIG_IN_Q: + erts_print(to, to_arg, "SIG_IN_Q"); break; case ERTS_PSFLG_ACTIVE_SYS: erts_print(to, to_arg, "ACTIVE_SYS"); break; case ERTS_PSFLG_RUNNING_SYS: @@ -1021,6 +1061,8 @@ erts_dump_extended_process_state(fmtfn_t to, void *to_arg, erts_aint32_t psflg) erts_print(to, to_arg, "DELAYED_SYS"); break; case ERTS_PSFLG_OFF_HEAP_MSGQ: erts_print(to, to_arg, "OFF_HEAP_MSGQ"); break; + case ERTS_PSFLG_SIG_Q: + erts_print(to, to_arg, "SIG_Q"); break; case ERTS_PSFLG_DIRTY_CPU_PROC: erts_print(to, to_arg, "DIRTY_CPU_PROC"); break; case ERTS_PSFLG_DIRTY_IO_PROC: diff --git a/erts/emulator/beam/erl_process_lock.c b/erts/emulator/beam/erl_process_lock.c index 431867f27e..44c7892040 100644 --- a/erts/emulator/beam/erl_process_lock.c +++ b/erts/emulator/beam/erl_process_lock.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2007-2017. All Rights Reserved. + * Copyright Ericsson AB 2007-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -101,7 +101,6 @@ static void cleanup_tse(void); #ifdef ERTS_ENABLE_LOCK_CHECK static struct { Sint16 proc_lock_main; - Sint16 proc_lock_link; Sint16 proc_lock_msgq; Sint16 proc_lock_btm; Sint16 proc_lock_status; @@ -140,7 +139,6 @@ erts_init_proc_lock(int cpus) #endif #ifdef ERTS_ENABLE_LOCK_CHECK lc_id.proc_lock_main = erts_lc_get_lock_order_id("proc_main"); - lc_id.proc_lock_link = erts_lc_get_lock_order_id("proc_link"); lc_id.proc_lock_msgq = erts_lc_get_lock_order_id("proc_msgq"); lc_id.proc_lock_btm = erts_lc_get_lock_order_id("proc_btm"); lc_id.proc_lock_status = erts_lc_get_lock_order_id("proc_status"); @@ -1055,12 +1053,6 @@ erts_proc_lock_init(Process *p) #ifdef ERTS_ENABLE_LOCK_CHECK erts_lc_trylock(1, &p->lock.main.lc); #endif - erts_mtx_init(&p->lock.link, "proc_link", p->common.id, - ERTS_LOCK_FLAGS_CATEGORY_PROCESS); - ethr_mutex_lock(&p->lock.link.mtx); -#ifdef ERTS_ENABLE_LOCK_CHECK - erts_lc_trylock(1, &p->lock.link.lc); -#endif erts_mtx_init(&p->lock.msgq, "proc_msgq", p->common.id, ERTS_LOCK_FLAGS_CATEGORY_PROCESS); ethr_mutex_lock(&p->lock.msgq.mtx); @@ -1102,7 +1094,6 @@ erts_proc_lock_fin(Process *p) { #if ERTS_PROC_LOCK_RAW_MUTEX_IMPL erts_mtx_destroy(&p->lock.main); - erts_mtx_destroy(&p->lock.link); erts_mtx_destroy(&p->lock.msgq); erts_mtx_destroy(&p->lock.btm); erts_mtx_destroy(&p->lock.status); @@ -1144,8 +1135,6 @@ void erts_lcnt_enable_proc_lock_count(Process *proc, int enable) { erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN, "proc_main", proc->common.id, ERTS_LOCK_TYPE_PROCLOCK); - erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK, - "proc_link", proc->common.id, ERTS_LOCK_TYPE_PROCLOCK); erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ, "proc_msgq", proc->common.id, ERTS_LOCK_TYPE_PROCLOCK); erts_lcnt_init_lock_info_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_BTM, @@ -1200,10 +1189,6 @@ erts_proc_lc_lock(Process *p, ErtsProcLocks locks, char *file, unsigned int line lck.id = lc_id.proc_lock_main; erts_lc_lock_x(&lck,file,line); } - if (locks & ERTS_PROC_LOCK_LINK) { - lck.id = lc_id.proc_lock_link; - erts_lc_lock_x(&lck,file,line); - } if (locks & ERTS_PROC_LOCK_MSGQ) { lck.id = lc_id.proc_lock_msgq; erts_lc_lock_x(&lck,file,line); @@ -1233,10 +1218,6 @@ erts_proc_lc_trylock(Process *p, ErtsProcLocks locks, int locked, lck.id = lc_id.proc_lock_main; erts_lc_trylock_x(locked, &lck, file, line); } - if (locks & ERTS_PROC_LOCK_LINK) { - lck.id = lc_id.proc_lock_link; - erts_lc_trylock_x(locked, &lck, file, line); - } if (locks & ERTS_PROC_LOCK_MSGQ) { lck.id = lc_id.proc_lock_msgq; erts_lc_trylock_x(locked, &lck, file, line); @@ -1277,10 +1258,6 @@ erts_proc_lc_unlock(Process *p, ErtsProcLocks locks) lck.id = lc_id.proc_lock_msgq; erts_lc_unlock(&lck); } - if (locks & ERTS_PROC_LOCK_LINK) { - lck.id = lc_id.proc_lock_link; - erts_lc_unlock(&lck); - } if (locks & ERTS_PROC_LOCK_MAIN) { lck.id = lc_id.proc_lock_main; erts_lc_unlock(&lck); @@ -1312,10 +1289,6 @@ erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks) lck.id = lc_id.proc_lock_msgq; erts_lc_might_unlock(&lck); } - if (locks & ERTS_PROC_LOCK_LINK) { - lck.id = lc_id.proc_lock_link; - erts_lc_might_unlock(&lck); - } if (locks & ERTS_PROC_LOCK_MAIN) { lck.id = lc_id.proc_lock_main; erts_lc_might_unlock(&lck); @@ -1323,8 +1296,6 @@ erts_proc_lc_might_unlock(Process *p, ErtsProcLocks locks) #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL if (locks & ERTS_PROC_LOCK_MAIN) erts_lc_might_unlock(&p->lock.main.lc); - if (locks & ERTS_PROC_LOCK_LINK) - erts_lc_might_unlock(&p->lock.link.lc); if (locks & ERTS_PROC_LOCK_MSGQ) erts_lc_might_unlock(&p->lock.msgq.lc); if (locks & ERTS_PROC_LOCK_BTM) @@ -1348,10 +1319,6 @@ erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks, char *file, lck.id = lc_id.proc_lock_main; erts_lc_require_lock(&lck, file, line); } - if (locks & ERTS_PROC_LOCK_LINK) { - lck.id = lc_id.proc_lock_link; - erts_lc_require_lock(&lck, file, line); - } if (locks & ERTS_PROC_LOCK_MSGQ) { lck.id = lc_id.proc_lock_msgq; erts_lc_require_lock(&lck, file, line); @@ -1371,8 +1338,6 @@ erts_proc_lc_require_lock(Process *p, ErtsProcLocks locks, char *file, #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL if (locks & ERTS_PROC_LOCK_MAIN) erts_lc_require_lock(&p->lock.main.lc, file, line); - if (locks & ERTS_PROC_LOCK_LINK) - erts_lc_require_lock(&p->lock.link.lc, file, line); if (locks & ERTS_PROC_LOCK_MSGQ) erts_lc_require_lock(&p->lock.msgq.lc, file, line); if (locks & ERTS_PROC_LOCK_BTM) @@ -1407,10 +1372,6 @@ erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks) lck.id = lc_id.proc_lock_msgq; erts_lc_unrequire_lock(&lck); } - if (locks & ERTS_PROC_LOCK_LINK) { - lck.id = lc_id.proc_lock_link; - erts_lc_unrequire_lock(&lck); - } if (locks & ERTS_PROC_LOCK_MAIN) { lck.id = lc_id.proc_lock_main; erts_lc_unrequire_lock(&lck); @@ -1418,8 +1379,6 @@ erts_proc_lc_unrequire_lock(Process *p, ErtsProcLocks locks) #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL if (locks & ERTS_PROC_LOCK_MAIN) erts_lc_unrequire_lock(&p->lock.main.lc); - if (locks & ERTS_PROC_LOCK_LINK) - erts_lc_unrequire_lock(&p->lock.link.lc); if (locks & ERTS_PROC_LOCK_MSGQ) erts_lc_unrequire_lock(&p->lock.msgq.lc); if (locks & ERTS_PROC_LOCK_BTM) @@ -1443,8 +1402,6 @@ erts_proc_lc_trylock_force_busy(Process *p, ErtsProcLocks locks) if (locks & ERTS_PROC_LOCK_MAIN) lck.id = lc_id.proc_lock_main; - else if (locks & ERTS_PROC_LOCK_LINK) - lck.id = lc_id.proc_lock_link; else if (locks & ERTS_PROC_LOCK_MSGQ) lck.id = lc_id.proc_lock_msgq; else if (locks & ERTS_PROC_LOCK_BTM) @@ -1487,10 +1444,6 @@ void erts_proc_lc_chk_only_proc(Process *p, ErtsProcLocks locks) have_locks[have_locks_len].id = lc_id.proc_lock_main; have_locks[have_locks_len++].extra = p->common.id; } - if (locks & ERTS_PROC_LOCK_LINK) { - have_locks[have_locks_len].id = lc_id.proc_lock_link; - have_locks[have_locks_len++].extra = p->common.id; - } if (locks & ERTS_PROC_LOCK_MSGQ) { have_locks[have_locks_len].id = lc_id.proc_lock_msgq; have_locks[have_locks_len++].extra = p->common.id; @@ -1511,8 +1464,6 @@ void erts_proc_lc_chk_only_proc(Process *p, ErtsProcLocks locks) erts_lc_lock_t have_locks[6]; if (locks & ERTS_PROC_LOCK_MAIN) have_locks[have_locks_len++] = p->lock.main.lc; - if (locks & ERTS_PROC_LOCK_LINK) - have_locks[have_locks_len++] = p->lock.link.lc; if (locks & ERTS_PROC_LOCK_MSGQ) have_locks[have_locks_len++] = p->lock.msgq.lc; if (locks & ERTS_PROC_LOCK_BTM) @@ -1540,10 +1491,6 @@ erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks) have_locks[have_locks_len].id = lc_id.proc_lock_main; have_locks[have_locks_len++].extra = p->common.id; } - if (locks & ERTS_PROC_LOCK_LINK) { - have_locks[have_locks_len].id = lc_id.proc_lock_link; - have_locks[have_locks_len++].extra = p->common.id; - } if (locks & ERTS_PROC_LOCK_MSGQ) { have_locks[have_locks_len].id = lc_id.proc_lock_msgq; have_locks[have_locks_len++].extra = p->common.id; @@ -1564,8 +1511,6 @@ erts_proc_lc_chk_have_proc_locks(Process *p, ErtsProcLocks locks) erts_lc_lock_t have_locks[6]; if (locks & ERTS_PROC_LOCK_MAIN) have_locks[have_locks_len++] = p->lock.main.lc; - if (locks & ERTS_PROC_LOCK_LINK) - have_locks[have_locks_len++] = p->lock.link.lc; if (locks & ERTS_PROC_LOCK_MSGQ) have_locks[have_locks_len++] = p->lock.msgq.lc; if (locks & ERTS_PROC_LOCK_BTM) @@ -1603,14 +1548,6 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks) have_not_locks[have_not_locks_len].id = lc_id.proc_lock_main; have_not_locks[have_not_locks_len++].extra = p->common.id; } - if (locks & ERTS_PROC_LOCK_LINK) { - have_locks[have_locks_len].id = lc_id.proc_lock_link; - have_locks[have_locks_len++].extra = p->common.id; - } - else { - have_not_locks[have_not_locks_len].id = lc_id.proc_lock_link; - have_not_locks[have_not_locks_len++].extra = p->common.id; - } if (locks & ERTS_PROC_LOCK_MSGQ) { have_locks[have_locks_len].id = lc_id.proc_lock_msgq; have_locks[have_locks_len++].extra = p->common.id; @@ -1651,10 +1588,6 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks) have_locks[have_locks_len++] = p->lock.main.lc; else have_not_locks[have_not_locks_len++] = p->lock.main.lc; - if (locks & ERTS_PROC_LOCK_LINK) - have_locks[have_locks_len++] = p->lock.link.lc; - else - have_not_locks[have_not_locks_len++] = p->lock.link.lc; if (locks & ERTS_PROC_LOCK_MSGQ) have_locks[have_locks_len++] = p->lock.msgq.lc; else @@ -1680,13 +1613,10 @@ erts_proc_lc_chk_proc_locks(Process *p, ErtsProcLocks locks) ErtsProcLocks erts_proc_lc_my_proc_locks(Process *p) { - int resv[6]; + int resv[5]; ErtsProcLocks res = 0; #if ERTS_PROC_LOCK_OWN_IMPL - erts_lc_lock_t locks[6] = {ERTS_LC_LOCK_INIT(lc_id.proc_lock_main, - p->common.id, - ERTS_LOCK_TYPE_PROCLOCK), - ERTS_LC_LOCK_INIT(lc_id.proc_lock_link, + erts_lc_lock_t locks[5] = {ERTS_LC_LOCK_INIT(lc_id.proc_lock_main, p->common.id, ERTS_LOCK_TYPE_PROCLOCK), ERTS_LC_LOCK_INIT(lc_id.proc_lock_msgq, @@ -1702,26 +1632,23 @@ erts_proc_lc_my_proc_locks(Process *p) p->common.id, ERTS_LOCK_TYPE_PROCLOCK)}; #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL - erts_lc_lock_t locks[6] = {p->lock.main.lc, - p->lock.link.lc, + erts_lc_lock_t locks[5] = {p->lock.main.lc, p->lock.msgq.lc, p->lock.btm.lc, p->lock.status.lc, p->lock.trace.lc}; #endif - erts_lc_have_locks(resv, locks, 6); + erts_lc_have_locks(resv, locks, 5); if (resv[0]) res |= ERTS_PROC_LOCK_MAIN; if (resv[1]) - res |= ERTS_PROC_LOCK_LINK; - if (resv[2]) res |= ERTS_PROC_LOCK_MSGQ; - if (resv[3]) + if (resv[2]) res |= ERTS_PROC_LOCK_BTM; - if (resv[4]) + if (resv[3]) res |= ERTS_PROC_LOCK_STATUS; - if (resv[5]) + if (resv[4]) res |= ERTS_PROC_LOCK_TRACE; return res; @@ -1730,15 +1657,14 @@ erts_proc_lc_my_proc_locks(Process *p) void erts_proc_lc_chk_no_proc_locks(char *file, int line) { - int resv[6]; - int ids[6] = {lc_id.proc_lock_main, - lc_id.proc_lock_link, + int resv[5]; + int ids[5] = {lc_id.proc_lock_main, lc_id.proc_lock_msgq, lc_id.proc_lock_btm, lc_id.proc_lock_status, lc_id.proc_lock_trace}; - erts_lc_have_lock_ids(resv, ids, 6); - if (!ERTS_IS_CRASH_DUMPING && (resv[0] || resv[1] || resv[2] || resv[3] || resv[4] || resv[5])) { + erts_lc_have_lock_ids(resv, ids, 5); + if (!ERTS_IS_CRASH_DUMPING && (resv[0] || resv[1] || resv[2] || resv[3] || resv[4])) { erts_lc_fail("%s:%d: Thread has process locks locked when expected " "not to have any process locks locked", file, line); diff --git a/erts/emulator/beam/erl_process_lock.h b/erts/emulator/beam/erl_process_lock.h index 9d5691d3c4..43f396c547 100644 --- a/erts/emulator/beam/erl_process_lock.h +++ b/erts/emulator/beam/erl_process_lock.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2007-2017. All Rights Reserved. + * Copyright Ericsson AB 2007-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,7 +66,7 @@ #endif -#define ERTS_PROC_LOCK_MAX_BIT 5 +#define ERTS_PROC_LOCK_MAX_BIT 4 typedef erts_aint32_t ErtsProcLocks; @@ -82,19 +82,17 @@ typedef struct erts_proc_lock_t_ { /* Each erts_mtx_t has its own lock counter ^ */ #define ERTS_LCNT_PROCLOCK_IDX_MAIN 0 - #define ERTS_LCNT_PROCLOCK_IDX_LINK 1 - #define ERTS_LCNT_PROCLOCK_IDX_MSGQ 2 - #define ERTS_LCNT_PROCLOCK_IDX_BTM 3 - #define ERTS_LCNT_PROCLOCK_IDX_STATUS 4 - #define ERTS_LCNT_PROCLOCK_IDX_TRACE 5 + #define ERTS_LCNT_PROCLOCK_IDX_MSGQ 1 + #define ERTS_LCNT_PROCLOCK_IDX_BTM 2 + #define ERTS_LCNT_PROCLOCK_IDX_STATUS 3 + #define ERTS_LCNT_PROCLOCK_IDX_TRACE 4 - #define ERTS_LCNT_PROCLOCK_COUNT 6 + #define ERTS_LCNT_PROCLOCK_COUNT 5 erts_lcnt_ref_t lcnt_carrier; #endif #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL erts_mtx_t main; - erts_mtx_t link; erts_mtx_t msgq; erts_mtx_t btm; erts_mtx_t status; @@ -118,27 +116,18 @@ typedef struct erts_proc_lock_t_ { #define ERTS_PROC_LOCK_MAIN (((ErtsProcLocks) 1) << 0) /* - * Link lock: - * Protects the following fields in the process structure: - * * nlinks - * * monitors - * * suspend_monitors - */ -#define ERTS_PROC_LOCK_LINK (((ErtsProcLocks) 1) << 1) - -/* * Message queue lock: * Protects the following fields in the process structure: * * msg_inq */ -#define ERTS_PROC_LOCK_MSGQ (((ErtsProcLocks) 1) << 2) +#define ERTS_PROC_LOCK_MSGQ (((ErtsProcLocks) 1) << 1) /* * Bif timer lock: * Protects the following fields in the process structure: * * bif_timers */ -#define ERTS_PROC_LOCK_BTM (((ErtsProcLocks) 1) << 3) +#define ERTS_PROC_LOCK_BTM (((ErtsProcLocks) 1) << 2) /* * Status lock: @@ -148,7 +137,7 @@ typedef struct erts_proc_lock_t_ { * * sys_tasks * * ... */ -#define ERTS_PROC_LOCK_STATUS (((ErtsProcLocks) 1) << 4) +#define ERTS_PROC_LOCK_STATUS (((ErtsProcLocks) 1) << 3) /* * Trace message lock: @@ -277,9 +266,6 @@ void erts_lcnt_proc_lock(erts_proc_lock_t *lock, ErtsProcLocks locks) { if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_lock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN); } - if (locks & ERTS_PROC_LOCK_LINK) { - erts_lcnt_lock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK); - } if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_lock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ); } @@ -307,9 +293,6 @@ void erts_lcnt_proc_lock_post_x(erts_proc_lock_t *lock, ErtsProcLocks locks, if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_lock_post_x_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN, file, line); } - if (locks & ERTS_PROC_LOCK_LINK) { - erts_lcnt_lock_post_x_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK, file, line); - } if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_lock_post_x_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ, file, line); } @@ -336,9 +319,6 @@ void erts_lcnt_proc_lock_unacquire(erts_proc_lock_t *lock, ErtsProcLocks locks) if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_lock_unacquire_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN); } - if (locks & ERTS_PROC_LOCK_LINK) { - erts_lcnt_lock_unacquire_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK); - } if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_lock_unacquire_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ); } @@ -365,9 +345,6 @@ void erts_lcnt_proc_unlock(erts_proc_lock_t *lock, ErtsProcLocks locks) { if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_unlock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN); } - if (locks & ERTS_PROC_LOCK_LINK) { - erts_lcnt_unlock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK); - } if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_unlock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ); } @@ -394,9 +371,6 @@ void erts_lcnt_proc_trylock(erts_proc_lock_t *lock, ErtsProcLocks locks, int res if (locks & ERTS_PROC_LOCK_MAIN) { erts_lcnt_trylock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MAIN, res); } - if (locks & ERTS_PROC_LOCK_LINK) { - erts_lcnt_trylock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_LINK, res); - } if (locks & ERTS_PROC_LOCK_MSGQ) { erts_lcnt_trylock_idx(carrier, ERTS_LCNT_PROCLOCK_IDX_MSGQ, res); } @@ -640,9 +614,6 @@ erts_proc_raw_trylock__(Process *p, ErtsProcLocks locks) if (locks & ERTS_PROC_LOCK_MAIN) if (erts_mtx_trylock(&p->lock.main) == EBUSY) goto busy_main; - if (locks & ERTS_PROC_LOCK_LINK) - if (erts_mtx_trylock(&p->lock.link) == EBUSY) - goto busy_link; if (locks & ERTS_PROC_LOCK_MSGQ) if (erts_mtx_trylock(&p->lock.msgq) == EBUSY) goto busy_msgq; @@ -668,9 +639,6 @@ busy_btm: if (locks & ERTS_PROC_LOCK_MSGQ) erts_mtx_unlock(&p->lock.msgq); busy_msgq: - if (locks & ERTS_PROC_LOCK_LINK) - erts_mtx_unlock(&p->lock.link); -busy_link: if (locks & ERTS_PROC_LOCK_MAIN) erts_mtx_unlock(&p->lock.main); busy_main: @@ -741,8 +709,6 @@ erts_proc_lock__(Process *p, #elif ERTS_PROC_LOCK_RAW_MUTEX_IMPL if (locks & ERTS_PROC_LOCK_MAIN) erts_mtx_lock(&p->lock.main); - if (locks & ERTS_PROC_LOCK_LINK) - erts_mtx_lock(&p->lock.link); if (locks & ERTS_PROC_LOCK_MSGQ) erts_mtx_lock(&p->lock.msgq); if (locks & ERTS_PROC_LOCK_BTM) @@ -844,8 +810,6 @@ erts_proc_unlock__(Process *p, erts_mtx_unlock(&p->lock.btm); if (locks & ERTS_PROC_LOCK_MSGQ) erts_mtx_unlock(&p->lock.msgq); - if (locks & ERTS_PROC_LOCK_LINK) - erts_mtx_unlock(&p->lock.link); if (locks & ERTS_PROC_LOCK_MAIN) erts_mtx_unlock(&p->lock.main); #endif diff --git a/erts/emulator/beam/erl_ptab.h b/erts/emulator/beam/erl_ptab.h index 4858cc8ab8..94f0247492 100644 --- a/erts/emulator/beam/erl_ptab.h +++ b/erts/emulator/beam/erl_ptab.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2012-2016. All Rights Reserved. + * Copyright Ericsson AB 2012-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,7 @@ #include "erl_thr_progress.h" #undef ERL_THR_PROGRESS_TSD_TYPE_ONLY #include "erl_alloc.h" -#include "erl_monitors.h" +#include "erl_monitor_link.h" #define ERTS_TRACER(P) ((P)->common.tracer) #define ERTS_TRACER_MODULE(T) (CAR(list_val(T))) @@ -44,6 +44,7 @@ #define ERTS_P_LINKS(P) ((P)->common.u.alive.links) #define ERTS_P_MONITORS(P) ((P)->common.u.alive.monitors) +#define ERTS_P_LT_MONITORS(P) ((P)->common.u.alive.lt_monitors) #define IS_TRACED(p) \ (ERTS_TRACER(p) != NIL) @@ -68,6 +69,7 @@ typedef struct { struct reg_proc *reg; ErtsLink *links; ErtsMonitor *monitors; + ErtsMonitor *lt_monitors; } alive; /* --- While being released --- */ diff --git a/erts/emulator/beam/erl_rbtree.h b/erts/emulator/beam/erl_rbtree.h index e59d6900b0..e50abf5cec 100644 --- a/erts/emulator/beam/erl_rbtree.h +++ b/erts/emulator/beam/erl_rbtree.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2015-2017. All Rights Reserved. + * Copyright Ericsson AB 2015-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,8 +50,14 @@ * - ERTS_RBT_GET_LEFT(T) - Get left child node. * - ERTS_RBT_SET_LEFT(T, L) - Set left child node. * - ERTS_RBT_GET_KEY(T) - Get key of node. - * - ERTS_RBT_IS_LT(KX, KY) - Is key KX less than key KY? - * - ERTS_RBT_IS_EQ(KX, KY) - Is key KX equal to key KY? + * Either: + * - ERTS_RBT_CMP_KEYS(KX, KY) - Compare keys... + * or: + * - ERTS_RBT_IS_LT(KX, KY) - Is key KX less than key KY? + * - ERTS_RBT_IS_EQ(KX, KY) - Is key KX equal to key KY? + * + * If ERTS_RBT_CMP_KEYS is defined ERTS_RBT_IS_LT and + * ERTS_RBT_IS_EQ will be redefined using ERTS_RBT_CMP_KEYS * * Optional defines: * @@ -337,6 +343,15 @@ * Should only be used for debuging. */ +#ifdef ERTS_RBT_CMP_KEYS + +# undef ERTS_RBT_IS_LT +# define ERTS_RBT_IS_LT(KX, KY) (ERTS_RBT_CMP_KEYS((KX), (KY)) < 0) + +# undef ERTS_RBT_IS_EQ +# define ERTS_RBT_IS_EQ(KX, KY) (ERTS_RBT_CMP_KEYS((KX), (KY)) == 0) + +#endif /* * Check that we have all mandatory defines @@ -396,6 +411,16 @@ # error Missing definition of ERTS_RBT_IS_EQ #endif +#undef ERTS_RBT_IS_GT__ +#ifdef ERTS_RBT_CMP_KEYS +# define ERTS_RBT_IS_GT__(KX, KY) \ + (ERTS_RBT_CMP_KEYS((KX), (KY)) > 0) +#else +# define ERTS_RBT_IS_GT__(KX, KY) \ + (!ERTS_RBT_IS_LT((KX), (KY)) && !ERTS_RBT_IS_EQ((KX), (KY))) + +#endif + #if defined(ERTS_RBT_HARD_DEBUG) || defined(DEBUG) # ifndef ERTS_RBT_DEBUG # define ERTS_RBT_DEBUG 1 @@ -1007,19 +1032,30 @@ ERTS_RBT_FUNC__(insert_aux__)(ERTS_RBT_T **root, ERTS_RBT_T *n, int lookup) ERTS_RBT_T *p, *x = *root; while (1) { - ERTS_RBT_KEY_T kx; + ERTS_RBT_KEY_T kx = ERTS_RBT_GET_KEY(x); ERTS_RBT_T *c; + int kres; +#ifdef ERTS_RBT_CMP_KEYS + int kcmp = ERTS_RBT_CMP_KEYS(kn, kx); + kres = kcmp == 0; +#else + kres = ERTS_RBT_IS_EQ(kn, kx); +#endif - kx = ERTS_RBT_GET_KEY(x); - - if (lookup && ERTS_RBT_IS_EQ(kn, kx)) { + if (lookup && kres) { ERTS_RBT_HDBG_CHECK_TREE__(*root, NULL); return x; } - if (ERTS_RBT_IS_LT(kn, kx)) { +#ifdef ERTS_RBT_CMP_KEYS + kres = kcmp < 0; +#else + kres = ERTS_RBT_IS_LT(kn, kx); +#endif + + if (kres) { c = ERTS_RBT_GET_LEFT(x); if (!c) { ERTS_RBT_SET_PARENT(n, x); @@ -1075,6 +1111,101 @@ ERTS_RBT_FUNC__(insert)(ERTS_RBT_T **root, ERTS_RBT_T *n) #endif /* ERTS_RBT_WANT_INSERT */ +#ifdef ERTS_RBT_WANT_LOOKUP_CREATE +static ERTS_INLINE ERTS_RBT_T * +ERTS_RBT_FUNC__(lookup_create)(ERTS_RBT_T **root, + ERTS_RBT_KEY_T kn, + ERTS_RBT_T *(*create)(ERTS_RBT_KEY_T, void *), + void *arg, + int *created) +{ + ERTS_RBT_T *n; + ERTS_RBT_HDBG_CHECK_TREE__(*root, NULL); + + if (!*root) { + n = (*create)(kn, arg); + ERTS_RBT_INIT_EMPTY_TNODE(n); + ERTS_RBT_ASSERT(ERTS_RBT_IS_EQ(ERTS_RBT_GET_KEY(n), kn)); + ERTS_RBT_SET_BLACK(n); + *root = n; + *created = !0; +#ifdef ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT + ERTS_RBT_UPDATE_ATTACHED_DATA_CHGROOT(NULL, n); +#endif + } + else { + ERTS_RBT_T *p, *x = *root; + + while (1) { + ERTS_RBT_KEY_T kx = ERTS_RBT_GET_KEY(x); + ERTS_RBT_T *c; + int kres; +#ifdef ERTS_RBT_CMP_KEYS + int kcmp = ERTS_RBT_CMP_KEYS(kn, kx); + kres = kcmp == 0; +#else + kres = ERTS_RBT_IS_EQ(kn, kx); +#endif + + if (kres) { + + ERTS_RBT_HDBG_CHECK_TREE__(*root, NULL); + + *created = 0; + return x; + } + +#ifdef ERTS_RBT_CMP_KEYS + kres = kcmp < 0; +#else + kres = ERTS_RBT_IS_LT(kn, kx); +#endif + + if (kres) { + c = ERTS_RBT_GET_LEFT(x); + if (!c) { + n = (*create)(kn, arg); + ERTS_RBT_INIT_EMPTY_TNODE(n); + ERTS_RBT_ASSERT(ERTS_RBT_IS_EQ(ERTS_RBT_GET_KEY(n), kn)); + *created = !0; + ERTS_RBT_SET_PARENT(n, x); + ERTS_RBT_SET_LEFT(x, n); + p = x; + break; + } + } + else { + c = ERTS_RBT_GET_RIGHT(x); + if (!c) { + n = (*create)(kn, arg); + ERTS_RBT_INIT_EMPTY_TNODE(n); + ERTS_RBT_ASSERT(ERTS_RBT_IS_EQ(ERTS_RBT_GET_KEY(n), kn)); + *created = !0; + ERTS_RBT_SET_PARENT(n, x); + ERTS_RBT_SET_RIGHT(x, n); + p = x; + break; + } + } + + x = c; + } + + ERTS_RBT_ASSERT(ERTS_RBT_IS_EQ(ERTS_RBT_GET_KEY(n), kn)); + ERTS_RBT_ASSERT(p); + + ERTS_RBT_SET_RED(n); + if (ERTS_RBT_IS_RED(p)) + ERTS_RBT_FUNC__(insert_fixup__)(root, n); + } + + ERTS_RBT_HDBG_CHECK_TREE__(*root, n); + + return n; +} + +#endif /* ERTS_RBT_WANT_LOOKUP_CREATE */ + #ifdef ERTS_RBT_WANT_LOOKUP static ERTS_RBT_API_INLINE__ ERTS_RBT_T * @@ -1088,11 +1219,24 @@ ERTS_RBT_FUNC__(lookup)(ERTS_RBT_T *root, ERTS_RBT_KEY_T key) while (1) { ERTS_RBT_KEY_T kx = ERTS_RBT_GET_KEY(x); ERTS_RBT_T *c; + int kres; +#ifdef ERTS_RBT_CMP_KEYS + int kcmp = ERTS_RBT_CMP_KEYS(key, kx); + kres = kcmp == 0; +#else + kres = ERTS_RBT_IS_EQ(key, kx); +#endif - if (ERTS_RBT_IS_EQ(key, kx)) + if (kres) return x; - if (ERTS_RBT_IS_LT(key, kx)) { +#ifdef ERTS_RBT_CMP_KEYS + kres = kcmp < 0; +#else + kres = ERTS_RBT_IS_LT(key, kx); +#endif + + if (kres) { c = ERTS_RBT_GET_LEFT(x); if (!c) return NULL; @@ -1426,14 +1570,14 @@ ERTS_RBT_FUNC__(foreach_large)(ERTS_RBT_T *root, #ifdef ERTS_RBT_WANT_FOREACH_YIELDING -static ERTS_RBT_API_INLINE__ void +static ERTS_RBT_API_INLINE__ int ERTS_RBT_FUNC__(foreach_yielding)(ERTS_RBT_T *root, void (*op)(ERTS_RBT_T *, void *), void *arg, ERTS_RBT_YIELD_STATE_T__ *ystate, Sint ylimit) { - (void) ERTS_RBT_FUNC__(foreach_unordered__)(*root, 0, op, arg, + return ERTS_RBT_FUNC__(foreach_unordered__)(&root, 0, op, arg, 1, ystate, ylimit); } @@ -1630,8 +1774,7 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n) kx = ERTS_RBT_GET_KEY(x); kc = ERTS_RBT_GET_KEY(c); - ERTS_RBT_ASSERT(ERTS_RBT_IS_LT(kc, kx) - || ERTS_RBT_IS_EQ(kc, kx)); + ERTS_RBT_ASSERT(!ERTS_RBT_IS_GT__(kc, kx)); x = c; } @@ -1649,8 +1792,8 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n) kx = ERTS_RBT_GET_KEY(x); kc = ERTS_RBT_GET_KEY(c); - ERTS_RBT_ASSERT(ERTS_RBT_IS_LT(kx, kc) - || ERTS_RBT_IS_EQ(kx, kc)); + ERTS_RBT_ASSERT(!ERTS_RBT_IS_GT__(kx, kc)); + x = c; } @@ -1672,8 +1815,8 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n) kx = ERTS_RBT_GET_KEY(x); kc = ERTS_RBT_GET_KEY(c); - ERTS_RBT_ASSERT(ERTS_RBT_IS_LT(kx, kc) - || ERTS_RBT_IS_EQ(kx, kc)); + ERTS_RBT_ASSERT(!ERTS_RBT_IS_GT__(kx, kc)); + /* Go down tree of x's sibling... */ x = c; break; @@ -1707,6 +1850,7 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n) #undef ERTS_RBT_NEED_FOREACH_ORDERED__ #undef ERTS_RBT_NEED_HDBG_CHECK_TREE__ #undef ERTS_RBT_HDBG_CHECK_TREE__ +#undef ERTS_RBT_IS_GT__ #ifdef ERTS_RBT_UNDEF # undef ERTS_RBT_PREFIX @@ -1727,6 +1871,7 @@ ERTS_RBT_FUNC__(hdbg_check_tree)(ERTS_RBT_T *root, ERTS_RBT_T *n) # undef ERTS_RBT_GET_LEFT # undef ERTS_RBT_SET_LEFT # undef ERTS_RBT_GET_KEY +# undef ERTS_RBT_CMP_KEYS # undef ERTS_RBT_IS_LT # undef ERTS_RBT_IS_EQ # undef ERTS_RBT_UNDEF diff --git a/erts/emulator/beam/erl_sched_spec_pre_alloc.c b/erts/emulator/beam/erl_sched_spec_pre_alloc.c index ab204303d7..4a6e02281a 100644 --- a/erts/emulator/beam/erl_sched_spec_pre_alloc.c +++ b/erts/emulator/beam/erl_sched_spec_pre_alloc.c @@ -47,6 +47,15 @@ erts_sspa_create(size_t blk_sz, int pa_size, int nthreads, const char* name) int cix; int no_blocks = pa_size; int no_blocks_per_chunk; + size_t aligned_blk_sz; + +#if !defined(ERTS_STRUCTURE_ALIGNED_ALLOC) + /* Force 64-bit alignment... */ + aligned_blk_sz = ((blk_sz - 1) / 8) * 8 + 8; +#else + /* Alignment of structure is enough... */ + aligned_blk_sz = blk_sz; +#endif if (!name) { /* schedulers only variant */ ASSERT(!nthreads); @@ -68,7 +77,7 @@ erts_sspa_create(size_t blk_sz, int pa_size, int nthreads, const char* name) } no_blocks = no_blocks_per_chunk * nthreads; chunk_mem_size = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(erts_sspa_chunk_header_t)); - chunk_mem_size += blk_sz * no_blocks_per_chunk; + chunk_mem_size += aligned_blk_sz * no_blocks_per_chunk; chunk_mem_size = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(chunk_mem_size); tot_size = ERTS_ALC_CACHE_LINE_ALIGN_SIZE(sizeof(erts_sspa_data_t)); tot_size += chunk_mem_size * nthreads; @@ -115,7 +124,7 @@ erts_sspa_create(size_t blk_sz, int pa_size, int nthreads, const char* name) blk = (erts_sspa_blk_t *) p; for (i = 0; i < no_blocks_per_chunk; i++) { blk = (erts_sspa_blk_t *) p; - p += blk_sz; + p += aligned_blk_sz; blk->next_ptr = (erts_sspa_blk_t *) p; } diff --git a/erts/emulator/beam/erl_term.h b/erts/emulator/beam/erl_term.h index 7d42d1b396..18483fca35 100644 --- a/erts/emulator/beam/erl_term.h +++ b/erts/emulator/beam/erl_term.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2017. All Rights Reserved. + * Copyright Ericsson AB 2000-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -313,7 +313,8 @@ _ET_DECLARE_CHECKED(Uint,header_arity,Eterm) #define MAX_ARITYVAL ((((Uint)1) << 24) - 1) #define ERTS_MAX_TUPLE_SIZE MAX_ARITYVAL -#define make_arityval(sz) _make_header((sz),_TAG_HEADER_ARITYVAL) +#define make_arityval(sz) (ASSERT((sz) <= MAX_ARITYVAL), \ + _make_header((sz),_TAG_HEADER_ARITYVAL)) #define is_arity_value(x) (((x) & _TAG_HEADER_MASK) == _TAG_HEADER_ARITYVAL) #define is_sane_arity_value(x) ((((x) & _TAG_HEADER_MASK) == _TAG_HEADER_ARITYVAL) && \ (((x) >> _HEADER_ARITY_OFFS) <= MAX_ARITYVAL)) @@ -344,6 +345,9 @@ _ET_DECLARE_CHECKED(Uint,thing_subtag,Eterm) * * To help find code which makes unwarranted assumptions about zero, * we now use a non-zero bit-pattern in debug mode. + * + * In order to be able to differentiata against values, the non-value + * needs to be tagged as a header of some sort. */ #if ET_DEBUG # ifdef HIPE @@ -354,7 +358,7 @@ _ET_DECLARE_CHECKED(Uint,thing_subtag,Eterm) # define THE_NON_VALUE _make_header(0,_TAG_HEADER_FLOAT) # endif #else -#define THE_NON_VALUE (0) +#define THE_NON_VALUE (TAG_PRIMARY_HEADER) #endif #define is_non_value(x) ((x) == THE_NON_VALUE) #define is_value(x) ((x) != THE_NON_VALUE) diff --git a/erts/emulator/beam/erl_time.h b/erts/emulator/beam/erl_time.h index 27164d50a0..968f21fd51 100644 --- a/erts/emulator/beam/erl_time.h +++ b/erts/emulator/beam/erl_time.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2017. All Rights Reserved. + * Copyright Ericsson AB 2006-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,8 @@ #ifndef ERL_TIME_H__ #define ERL_TIME_H__ +#include "erl_monitor_link.h" + #if 0 # define ERTS_TW_DEBUG #endif @@ -79,8 +81,8 @@ typedef ErtsMonotonicTime * ErtsNextTimeoutRef; extern SysTimeval erts_first_emu_time; -void erts_monitor_time_offset(Eterm id, Eterm ref); -int erts_demonitor_time_offset(Eterm ref); +void erts_monitor_time_offset(ErtsMonitor *mon); +void erts_demonitor_time_offset(ErtsMonitor *mon); int erts_init_time_sup(int, ErtsTimeWarpMode); void erts_late_init_time_sup(void); @@ -107,9 +109,6 @@ void erts_p_slpq(void); void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec); #endif -typedef UWord erts_approx_time_t; -erts_approx_time_t erts_get_approx_time(void); - int erts_has_time_correction(void); int erts_check_time_adj_support(int time_correction, ErtsTimeWarpMode time_warp_mode); diff --git a/erts/emulator/beam/erl_time_sup.c b/erts/emulator/beam/erl_time_sup.c index f2e0900fec..e5bb3cc15f 100644 --- a/erts/emulator/beam/erl_time_sup.c +++ b/erts/emulator/beam/erl_time_sup.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2017. All Rights Reserved. + * Copyright Ericsson AB 1999-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ #include "erl_time.h" #include "erl_driver.h" #include "erl_nif.h" +#include "erl_proc_sig_queue.h" static erts_mtx_t erts_get_time_mtx; @@ -191,17 +192,6 @@ static struct { ErtsTimeSupData erts_time_sup__ erts_align_attribute(ERTS_CACHE_LINE_SIZE); -/* - * erts_get_approx_time() returns an *approximate* time - * in seconds. NOTE that this time may jump backwards!!! - */ -erts_approx_time_t -erts_get_approx_time(void) -{ - ErtsSystemTime stime = erts_os_system_time(); - return (erts_approx_time_t) ERTS_MONOTONIC_TO_SEC(stime); -} - static ERTS_INLINE void init_time_offset(ErtsMonotonicTime offset) { @@ -1881,36 +1871,33 @@ void erts_get_now_cpu(Uint* megasec, Uint* sec, Uint* microsec) { #include "big.h" void -erts_monitor_time_offset(Eterm id, Eterm ref) +erts_monitor_time_offset(ErtsMonitor *mon) { erts_mtx_lock(&erts_get_time_mtx); - erts_add_monitor(&time_offset_monitors, MON_TIME_OFFSET, ref, id, NIL); + erts_monitor_list_insert(&time_offset_monitors, mon); no_time_offset_monitors++; erts_mtx_unlock(&erts_get_time_mtx); } -int -erts_demonitor_time_offset(Eterm ref) +void +erts_demonitor_time_offset(ErtsMonitor *mon) { - int res; - ErtsMonitor *mon; - ASSERT(is_internal_ref(ref)); + ErtsMonitorData *mdp = erts_monitor_to_data(mon); + ASSERT(erts_monitor_is_origin(mon)); + ASSERT(mon->type == ERTS_MON_TYPE_TIME_OFFSET); + erts_mtx_lock(&erts_get_time_mtx); - if (is_internal_ordinary_ref(ref)) - mon = erts_remove_monitor(&time_offset_monitors, ref); - else - mon = NULL; - if (!mon) - res = 0; - else { - ASSERT(no_time_offset_monitors > 0); - no_time_offset_monitors--; - res = 1; - } + + ASSERT(erts_monitor_is_in_table(&mdp->target)); + + erts_monitor_list_delete(&time_offset_monitors, &mdp->target); + + ASSERT(no_time_offset_monitors > 0); + no_time_offset_monitors--; + erts_mtx_unlock(&erts_get_time_mtx); - if (res) - erts_destroy_monitor(mon); - return res; + + erts_monitor_release_both(mdp); } typedef struct { @@ -1928,17 +1915,19 @@ static void save_time_offset_monitor(ErtsMonitor *mon, void *vcntxt) { ErtsTimeOffsetMonitorContext *cntxt; + ErtsMonitorData *mdp = erts_monitor_to_data(mon); Eterm *from_hp, *to_hp; Uint mix; int hix; cntxt = (ErtsTimeOffsetMonitorContext *) vcntxt; mix = (cntxt->ix)++; - cntxt->to_mon_info[mix].pid = mon->u.pid; + ASSERT(is_internal_pid(mon->other.item)); + cntxt->to_mon_info[mix].pid = mon->other.item; to_hp = &cntxt->to_mon_info[mix].heap[0]; - ASSERT(is_internal_ordinary_ref(mon->ref)); - from_hp = internal_ref_val(mon->ref); + ASSERT(is_internal_ordinary_ref(mdp->ref)); + from_hp = internal_ref_val(mdp->ref); ASSERT(thing_arityval(*from_hp) + 1 == ERTS_REF_THING_SIZE); for (hix = 0; hix < ERTS_REF_THING_SIZE; hix++) @@ -1983,9 +1972,9 @@ send_time_offset_changed_notifications(void *new_offsetp) cntxt.ix = 0; cntxt.to_mon_info = to_mon_info; - erts_doforall_monitors(time_offset_monitors, - save_time_offset_monitor, - &cntxt); + erts_monitor_list_foreach(time_offset_monitors, + save_time_offset_monitor, + &cntxt); ASSERT(cntxt.ix == no_monitors); } @@ -2019,26 +2008,14 @@ send_time_offset_changed_notifications(void *new_offsetp) ASSERT(*patch_refp == THE_NON_VALUE); for (mix = 0; mix < no_monitors; mix++) { - Process *rp = erts_proc_lookup(to_mon_info[mix].pid); - if (rp) { - Eterm ref = to_mon_info[mix].ref; - ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK; - erts_proc_lock(rp, ERTS_PROC_LOCK_LINK); - if (erts_lookup_monitor(ERTS_P_MONITORS(rp), ref)) { - ErtsMessage *mp; - ErlOffHeap *ohp; - Eterm message; - - mp = erts_alloc_message_heap(rp, &rp_locks, - hsz, &hp, &ohp); - *patch_refp = ref; - ASSERT(hsz == size_object(message_template)); - message = copy_struct(message_template, hsz, &hp, ohp); - erts_queue_message(rp, rp_locks, mp, message, am_clock_service); - } - erts_proc_unlock(rp, rp_locks); - } - } + *patch_refp = to_mon_info[mix].ref; + erts_proc_sig_send_persistent_monitor_msg(ERTS_MON_TYPE_TIME_OFFSET, + *patch_refp, + am_clock_service, + to_mon_info[mix].pid, + message_template, + hsz); + } erts_free(ERTS_ALC_T_TMP, tmp); } diff --git a/erts/emulator/beam/erl_trace.c b/erts/emulator/beam/erl_trace.c index d2495479ab..018894f685 100644 --- a/erts/emulator/beam/erl_trace.c +++ b/erts/emulator/beam/erl_trace.c @@ -2533,7 +2533,7 @@ load_tracer_nif(const ErtsTracer tracer) for(i = 0; i < num_of_funcs; i++) { for (j = 0; j < NIF_TRACER_TYPES; j++) { - if (strcmp(tracers[j].name, funcs[i].name) == 0 && tracers[j].arity == funcs[i].arity) { + if (sys_strcmp(tracers[j].name, funcs[i].name) == 0 && tracers[j].arity == funcs[i].arity) { tracers[j].cb = &(funcs[i]); break; } @@ -3019,7 +3019,7 @@ static void *tracer_alloc_fun(void* tmpl) ErtsTracerNif *obj = erts_alloc(ERTS_ALC_T_TRACER_NIF, sizeof(ErtsTracerNif) + sizeof(ErtsThrPrgrLaterOp)); - memcpy(obj, tmpl, sizeof(*obj)); + sys_memcpy(obj, tmpl, sizeof(*obj)); return obj; } diff --git a/erts/emulator/beam/erl_unicode.c b/erts/emulator/beam/erl_unicode.c index 604f0be127..8673e029e6 100644 --- a/erts/emulator/beam/erl_unicode.c +++ b/erts/emulator/beam/erl_unicode.c @@ -144,7 +144,7 @@ static Eterm make_magic_bin_for_restart(Process *p, RestartContext *rc) cleanup_restart_context_bin); RestartContext *restartp = ERTS_MAGIC_BIN_DATA(mbp); Eterm *hp; - memcpy(restartp,rc,sizeof(RestartContext)); + sys_memcpy(restartp,rc,sizeof(RestartContext)); hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE); return erts_mk_magic_ref(&hp, &MSO(p), mbp); } @@ -254,12 +254,12 @@ static Uint copy_utf8_bin(byte *target, byte *source, Uint size, ASSERT(need > 0); ASSERT(from_source > 0); if (size < from_source) { - memcpy(leftover + (*num_leftovers), source, size); + sys_memcpy(leftover + (*num_leftovers), source, size); *num_leftovers += size; return 0; } /* leftover has room for four bytes (see bif) */ - memcpy(leftover + (*num_leftovers),source,from_source); + sys_memcpy(leftover + (*num_leftovers),source,from_source); c = copy_utf8_bin(target, leftover, need, NULL, NULL, &tmp_err_pos, characters); if (tmp_err_pos != 0) { *err_pos = source; @@ -291,7 +291,7 @@ static Uint copy_utf8_bin(byte *target, byte *source, Uint size, size -= 2; copied += 2; } else if (((*source) & ((byte) 0xF0)) == 0xE0) { if (leftover && size < 3) { - memcpy(leftover, source, (int) size); + sys_memcpy(leftover, source, (int) size); *num_leftovers = (int) size; break; } @@ -313,7 +313,7 @@ static Uint copy_utf8_bin(byte *target, byte *source, Uint size, size -= 3; copied += 3; } else if (((*source) & ((byte) 0xF8)) == 0xF0) { if (leftover && size < 4) { - memcpy(leftover, source, (int) size); + sys_memcpy(leftover, source, (int) size); *num_leftovers = (int) size; break; } @@ -2108,7 +2108,7 @@ char *erts_convert_filename_to_encoding(Eterm name, char *statbuf, size_t statbu } else { name_buf = statbuf; } - memcpy(name_buf,bytes,size); + sys_memcpy(name_buf,bytes,size); name_buf[size]=0; } else { name_buf = erts_convert_filename_to_wchar(bytes, size, diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h index 44d8c85867..e4087e0ac8 100644 --- a/erts/emulator/beam/erl_utils.h +++ b/erts/emulator/beam/erl_utils.h @@ -91,7 +91,7 @@ Eterm erts_bld_tuple(Uint **hpp, Uint *szp, Uint arity, ...); #define erts_bld_tuple5(H,S,E1,E2,E3,E4,E5) erts_bld_tuple(H,S,5,E1,E2,E3,E4,E5) Eterm erts_bld_tuplev(Uint **hpp, Uint *szp, Uint arity, Eterm terms[]); Eterm erts_bld_string_n(Uint **hpp, Uint *szp, const char *str, Sint len); -#define erts_bld_string(hpp,szp,str) erts_bld_string_n(hpp,szp,str,strlen(str)) +#define erts_bld_string(hpp,szp,str) erts_bld_string_n(hpp,szp,str,sys_strlen(str)) Eterm erts_bld_list(Uint **hpp, Uint *szp, Sint length, Eterm terms[]); Eterm erts_bld_2tup_list(Uint **hpp, Uint *szp, Sint length, Eterm terms1[], Uint terms2[]); diff --git a/erts/emulator/beam/erl_vm.h b/erts/emulator/beam/erl_vm.h index 76980b5871..f8391fb665 100644 --- a/erts/emulator/beam/erl_vm.h +++ b/erts/emulator/beam/erl_vm.h @@ -69,7 +69,7 @@ # ifdef CHECK_FOR_HOLES # define INIT_HEAP_MEM(p,sz) erts_set_hole_marker(HEAP_TOP(p), (sz)) # else -# define INIT_HEAP_MEM(p,sz) memset(HEAP_TOP(p),0x01,(sz)*sizeof(Eterm*)) +# define INIT_HEAP_MEM(p,sz) sys_memset(HEAP_TOP(p),0x01,(sz)*sizeof(Eterm*)) # endif #else # define INIT_HEAP_MEM(p,sz) ((void)0) diff --git a/erts/emulator/beam/erlang_dtrace.d b/erts/emulator/beam/erlang_dtrace.d index d0b10e0306..c47a37eb62 100644 --- a/erts/emulator/beam/erlang_dtrace.d +++ b/erts/emulator/beam/erlang_dtrace.d @@ -55,7 +55,8 @@ provider erlang { * @param sender the PID (string form) of the sender * @param receiver the PID (string form) of the receiver * @param size the size of the message being delivered (words) - * @param token_label for the sender's sequential trace token + * @param token_label for the sender's sequential trace token. This will be + * INT_MIN if the label does not fit into a 32-bit integer. * @param token_previous count for the sender's sequential trace token * @param token_current count for the sender's sequential trace token */ @@ -73,7 +74,8 @@ provider erlang { * @param node_name the Erlang node name (string form) of the receiver * @param receiver the PID/name (string form) of the receiver * @param size the size of the message being delivered (words) - * @param token_label for the sender's sequential trace token + * @param token_label for the sender's sequential trace token. This will be + * INT_MIN if the label does not fit into a 32-bit integer. * @param token_previous count for the sender's sequential trace token * @param token_current count for the sender's sequential trace token */ @@ -98,7 +100,8 @@ provider erlang { * @param receiver the PID (string form) of the receiver * @param size the size of the message being delivered (words) * @param queue_len length of the queue of the receiving process - * @param token_label for the sender's sequential trace token + * @param token_label for the sender's sequential trace token. This will be + * INT_MIN if the label does not fit into a 32-bit integer. * @param token_previous count for the sender's sequential trace token * @param token_current count for the sender's sequential trace token */ @@ -122,7 +125,8 @@ provider erlang { * @param receiver the PID (string form) of the receiver * @param size the size of the message being delivered (words) * @param queue_len length of the queue of the receiving process - * @param token_label for the sender's sequential trace token + * @param token_label for the sender's sequential trace token. This will be + * INT_MIN if the label does not fit into a 32-bit integer. * @param token_previous count for the sender's sequential trace token * @param token_current count for the sender's sequential trace token */ @@ -273,7 +277,8 @@ provider erlang { * @param node_name the Erlang node name (string form) of the receiver * @param receiver the PID (string form) of the process receiving EXIT signal * @param reason the reason for the exit (may be truncated) - * @param token_label for the sender's sequential trace token + * @param token_label for the sender's sequential trace token. This will be + * INT_MIN if the label does not fit into a 32-bit integer. * @param token_previous count for the sender's sequential trace token * @param token_current count for the sender's sequential trace token */ diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c index b12a021e41..fb42969a8f 100644 --- a/erts/emulator/beam/external.c +++ b/erts/emulator/beam/external.c @@ -711,8 +711,8 @@ erts_prepare_dist_ext(ErtsDistExternal *edep, erts_de_rlock(dep); - if (dep->status != ERTS_DE_SFLG_CONNECTED && - dep->status != ERTS_DE_SFLG_PENDING) { + if (dep->state != ERTS_DE_STATE_CONNECTED && + dep->state != ERTS_DE_STATE_PENDING) { erts_de_runlock(dep); return ERTS_PREP_DIST_EXT_CLOSED; } @@ -1953,7 +1953,7 @@ static Eterm erts_term_to_binary_int(Process* p, Eterm Term, int level, Uint fla context_b = erts_create_magic_binary(sizeof(TTBContext), \ ttb_context_destructor); \ context = ERTS_MAGIC_BIN_DATA(context_b); \ - memcpy(context,&c_buff,sizeof(TTBContext)); \ + sys_memcpy(context,&c_buff,sizeof(TTBContext)); \ } \ } while (0) @@ -2873,6 +2873,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, ep[j] = 0; /* Zero unused bits at end of binary */ data_dst = ep; ep += j + 1; + if (ctx) + ctx->hopefull_flags |= DFLAG_BIT_BINARIES; } else { /* * Bit-level binary, but the receiver doesn't support it. @@ -2908,6 +2910,8 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep, ep = enc_atom(acmp, exp->info.mfa.function, ep, dflags); ep = enc_term(acmp, make_small(exp->info.mfa.arity), ep, dflags, off_heap); + if (ctx) + ctx->hopefull_flags |= DFLAG_EXPORT_PTR_TAG; } else { /* Tag, arity */ *ep++ = SMALL_TUPLE_EXT; @@ -4729,11 +4733,13 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob, struct transcode_context* ctx = dep->transcode_ctx; if (!ctx) { /* first call for 'ob' */ - - if (~dflags & (DFLAG_BIT_BINARIES | DFLAG_EXPORT_PTR_TAG)) { + ASSERT(!(ob->hopefull_flags & ~(Uint)(DFLAG_BIT_BINARIES | + DFLAG_EXPORT_PTR_TAG))); + if (~dflags & ob->hopefull_flags) { /* - * Receiver does not support bitstrings and/or export funs. - * We need to transcode control and message terms to use tuple fallbacks. + * Receiver does not support bitstrings and/or export funs + * and output buffer contains such message tags (hopefull_flags). + * Must transcode control and message terms to use tuple fallbacks. */ ctx = erts_alloc(ERTS_ALC_T_DIST_TRANSCODE, sizeof(struct transcode_context)); dep->transcode_ctx = ctx; @@ -4760,7 +4766,7 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob, ctx->state = TRANSCODE_ENC_CTL; } } - else { + else if (!(dflags & DFLAG_DIST_HDR_ATOM_CACHE)) { /* * No need for full transcoding, but primitive receiver (erl_/jinterface) * expects VERSION_MAGIC before both control and message terms. @@ -4777,8 +4783,10 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob, *--(ob->extp) = VERSION_MAGIC; goto done; } + else + goto done; } - else { + else { /* continue after yield */ ASSERT(ctx->dbg_ob == ob); } ctx->b2t.reds = reds * B2T_BYTES_PER_REDUCTION; @@ -4831,6 +4839,7 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob, ob->msg_start = ob->ext_endp; ctx->ttb.wstack.wstart = NULL; ctx->ttb.flags = dflags; + ctx->ttb.hopefull_flags = 0; ctx->ttb.level = 0; ctx->state = TRANSCODE_ENC_MSG; @@ -4843,7 +4852,7 @@ Sint transcode_dist_obuf(ErtsDistOutputBuf* ob, reds /= TERM_TO_BINARY_LOOP_FACTOR; ASSERT(ob->ext_endp <= ob->alloc_endp); - + ASSERT(!ctx->ttb.hopefull_flags); } transcode_free_ctx(dep); diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h index f9f8abcc27..bbd9b4bad2 100644 --- a/erts/emulator/beam/external.h +++ b/erts/emulator/beam/external.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2016. All Rights Reserved. + * Copyright Ericsson AB 1996-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -83,7 +83,10 @@ #ifndef ERL_EXTERNAL_H__ #define ERL_EXTERNAL_H__ +#define ERL_NODE_TABLES_BASIC_ONLY #include "erl_node_tables.h" +#undef ERL_NODE_TABLES_BASIC_ONLY +#include "erl_alloc.h" #define ERTS_ATOM_CACHE_SIZE 2048 diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h index d757651933..7724231cd5 100644 --- a/erts/emulator/beam/global.h +++ b/erts/emulator/beam/global.h @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2017. All Rights Reserved. + * Copyright Ericsson AB 1996-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -87,9 +87,7 @@ typedef struct { erts_mtx_t lock; ErtsMonitor* root; - int pending_failed_fire; - int is_dying; - + Uint refc; size_t user_data_sz; } ErtsResourceMonitors; @@ -116,7 +114,9 @@ extern void erts_pre_nif(struct enif_environment_t*, Process*, struct erl_module_nif*, Process* tracee); extern void erts_post_nif(struct enif_environment_t* env); extern void erts_resource_stop(ErtsResource*, ErlNifEvent, int is_direct_call); -void erts_fire_nif_monitor(ErtsResource*, Eterm pid, Eterm ref); +void erts_fire_nif_monitor(ErtsMonitor *tmon); +void erts_nif_demonitored(ErtsResource* resource); +extern void erts_add_taint(Eterm mod_atom); extern Eterm erts_nif_taints(Process* p); extern void erts_print_nif_taints(fmtfn_t to, void* to_arg); void erts_unload_nif(struct erl_module_nif* nif); @@ -368,7 +368,7 @@ do {\ UWord _wsz = ESTACK_COUNT(s);\ (dst)->start = erts_alloc((s).alloc_type,\ DEF_ESTACK_SIZE * sizeof(Eterm));\ - memcpy((dst)->start, (s).start,_wsz*sizeof(Eterm));\ + sys_memcpy((dst)->start, (s).start,_wsz*sizeof(Eterm));\ (dst)->sp = (dst)->start + _wsz;\ (dst)->end = (dst)->start + DEF_ESTACK_SIZE;\ (dst)->edefault = NULL;\ @@ -536,7 +536,7 @@ do {\ UWord _wsz = WSTACK_COUNT(s);\ (dst)->wstart = erts_alloc(s.alloc_type,\ DEF_WSTACK_SIZE * sizeof(UWord));\ - memcpy((dst)->wstart, s.wstart,_wsz*sizeof(UWord));\ + sys_memcpy((dst)->wstart, s.wstart,_wsz*sizeof(UWord));\ (dst)->wsp = (dst)->wstart + _wsz;\ (dst)->wend = (dst)->wstart + DEF_WSTACK_SIZE;\ (dst)->wdefault = NULL;\ @@ -886,6 +886,7 @@ void erts_init_trap_export(Export* ep, Eterm m, Eterm f, Uint a, Eterm (*bif)(Process*, Eterm*, BeamInstr*)); void erts_init_bif(void); Eterm erl_send(Process *p, Eterm to, Eterm msg); +int erts_set_group_leader(Process *proc, Eterm new_gl); /* erl_bif_op.c */ @@ -908,7 +909,6 @@ extern erts_atomic_t erts_copy_literal_area__; #define ERTS_COPY_LITERAL_AREA() \ ((ErtsLiteralArea *) erts_atomic_read_nob(&erts_copy_literal_area__)) extern Process *erts_literal_area_collector; -extern Process *erts_dirty_process_code_checker; extern Process *erts_code_purger; @@ -1072,7 +1072,7 @@ void erts_move_multi_frags(Eterm** hpp, ErlOffHeap*, ErlHeapFragment* first, Eterm* refs, unsigned nrefs, int literals); /* Utilities */ -extern void erts_delete_nodes_monitors(Process *, ErtsProcLocks); +void erts_monitor_nodes_delete(ErtsMonitor *); extern Eterm erts_monitor_nodes(Process *, Eterm, Eterm); extern Eterm erts_processes_monitoring_nodes(Process *); extern int erts_do_net_exits(DistEntry*, Eterm); @@ -1151,7 +1151,7 @@ typedef struct { #define ERTS_SPAWN_DRIVER 1 #define ERTS_SPAWN_EXECUTABLE 2 #define ERTS_SPAWN_ANY (ERTS_SPAWN_DRIVER | ERTS_SPAWN_EXECUTABLE) -int erts_add_driver_entry(ErlDrvEntry *drv, DE_Handle *handle, int driver_list_locked); +int erts_add_driver_entry(ErlDrvEntry *drv, DE_Handle *handle, int driver_list_locked, int taint); void erts_destroy_driver(erts_driver_t *drv); int erts_save_suspend_process_on_port(Port*, Process*); Port *erts_open_driver(erts_driver_t*, Eterm, char*, SysDriverOpts*, int *, int *); @@ -1176,8 +1176,17 @@ void erts_lcnt_update_port_locks(int enable); #endif /* driver_tab.c */ +typedef struct { + ErlDrvEntry* de; + int taint; +} ErtsStaticDriver; typedef void *(*ErtsStaticNifInitFPtr)(void); -ErtsStaticNifInitFPtr erts_static_nif_get_nif_init(const char *name, int len); +typedef struct ErtsStaticNifEntry_ { + const char *nif_name; + ErtsStaticNifInitFPtr nif_init; + int taint; +} ErtsStaticNifEntry; +ErtsStaticNifEntry* erts_static_nif_get_nif_init(const char *name, int len); int erts_is_static_nif(void *handle); void erts_init_static_drivers(void); diff --git a/erts/emulator/beam/hash.c b/erts/emulator/beam/hash.c index 8548e30e8b..6a31489473 100644 --- a/erts/emulator/beam/hash.c +++ b/erts/emulator/beam/hash.c @@ -152,7 +152,7 @@ Hash* hash_init(int type, Hash* h, char* name, int size, HashFunctions fun) h->bucket = (HashBucket**) fun.meta_alloc(h->meta_alloc_type, sz); - sys_memzero(h->bucket, sz); + memzero(h->bucket, sz); h->is_allocated = 0; h->name = name; h->fun = fun; @@ -224,7 +224,7 @@ static void rehash(Hash* h, int grow) sz = h->size*sizeof(HashBucket*); new_bucket = (HashBucket **) h->fun.meta_alloc(h->meta_alloc_type, sz); - sys_memzero(new_bucket, sz); + memzero(new_bucket, sz); for (i = 0; i < old_size; i++) { HashBucket* b = h->bucket[i]; diff --git a/erts/emulator/beam/io.c b/erts/emulator/beam/io.c index 3e8f6263bb..66b8005975 100644 --- a/erts/emulator/beam/io.c +++ b/erts/emulator/beam/io.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2017. All Rights Reserved. + * Copyright Ericsson AB 1996-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,6 +53,7 @@ #include "erl_hl_timer.h" #include "erl_time.h" #include "erl_io_queue.h" +#include "erl_proc_sig_queue.h" extern ErlDrvEntry fd_driver_entry; extern ErlDrvEntry vanilla_driver_entry; @@ -60,7 +61,7 @@ extern ErlDrvEntry spawn_driver_entry; #ifndef __WIN32__ extern ErlDrvEntry forker_driver_entry; #endif -extern ErlDrvEntry *driver_tab[]; /* table of static drivers, only used during initialization */ +extern ErtsStaticDriver driver_tab[]; /* table of static drivers, only used during initialization */ erts_driver_t *driver_list; /* List of all drivers, static and dynamic. */ erts_rwmtx_t erts_driver_list_lock; /* Mutex for driver list */ @@ -366,6 +367,7 @@ static Port *create_port(char *name, prt->drv_ptr = driver; ERTS_P_LINKS(prt) = NULL; ERTS_P_MONITORS(prt) = NULL; + ERTS_P_LT_MONITORS(prt) = NULL; prt->linebuf = NULL; prt->suspended = NULL; erts_init_port_data(prt); @@ -588,7 +590,7 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */ */ for (d = driver_list; d; d = d->next) { - if (strcmp(d->name, name) == 0 && + if (sys_strcmp(d->name, name) == 0 && erts_ddll_driver_ok(d->handle)) { driver = d; break; @@ -638,7 +640,7 @@ erts_open_driver(erts_driver_t* driver, /* Pointer to driver. */ trace_port_open(port, pid, erts_atom_put((byte *) port->name, - strlen(port->name), + sys_strlen(port->name), ERTS_ATOM_ENC_LATIN1, 1)); } @@ -748,8 +750,8 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */ Port *creator_port; Port* port; erts_driver_t *driver; - Process *rp; erts_mtx_t *driver_lock = NULL; + ErtsLinkData *ldp; ERTS_CHK_NO_PROC_LOCKS; @@ -761,17 +763,13 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */ if (creator_port == ERTS_INVALID_ERL_DRV_PORT) return ERTS_INVALID_ERL_DRV_PORT; - rp = erts_proc_lookup(pid); - if (!rp) - return ERTS_INVALID_ERL_DRV_PORT; - ERTS_LC_ASSERT(erts_lc_is_port_locked(creator_port)); driver = creator_port->drv_ptr; erts_rwmtx_rlock(&erts_driver_list_lock); if (!erts_ddll_driver_ok(driver->handle)) { erts_rwmtx_runlock(&erts_driver_list_lock); - return ERTS_INVALID_ERL_DRV_PORT; + return ERTS_INVALID_ERL_DRV_PORT; } if (driver->handle != NULL) { @@ -799,9 +797,15 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */ } ERTS_LC_ASSERT(erts_lc_is_port_locked(port)); - erts_proc_lock(rp, ERTS_PROC_LOCK_LINK); - if (ERTS_PROC_IS_EXITING(rp)) { - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); + ldp = erts_link_create(ERTS_LNK_TYPE_PORT, + port->common.id, pid); + ASSERT(ldp->a.other.item == pid); + ASSERT(ldp->b.other.item == port->common.id); + erts_link_tree_insert(&ERTS_P_LINKS(port), &ldp->a); + + if (!erts_proc_sig_send_link(NULL, pid, &ldp->b)) { + erts_link_tree_delete(&ERTS_P_LINKS(port), &ldp->a); + erts_link_release_both(ldp); if (driver->handle) { erts_rwmtx_rlock(&erts_driver_list_lock); erts_ddll_decrement_port_count(driver->handle); @@ -812,10 +816,6 @@ driver_create_port(ErlDrvPort creator_port_ix, /* Creating port */ return ERTS_INVALID_ERL_DRV_PORT; } - erts_add_link(&ERTS_P_LINKS(port), LINK_PID, pid); - erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, port->common.id); - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - if (!driver_lock) { ErtsXPortsList *xplp = xports_list_alloc(); xplp->port = port; @@ -1167,21 +1167,10 @@ erts_schedule_proc2port_signal(Process *c_p, * otherwise, next receive will *not* work * as expected! */ - erts_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); - - if (ERTS_PROC_PENDING_EXIT(c_p)) { - /* need to exit caller instead */ - erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); - KILL_CATCHES(c_p); - c_p->freason = EXC_EXIT; - return ERTS_PORT_OP_CALLER_EXIT; - } - - ERTS_MSGQ_MV_INQ2PRIVQ(c_p); - c_p->msg.save = c_p->msg.last; - erts_proc_unlock(c_p, (ERTS_PROC_LOCKS_MSG_RECEIVE - | ERTS_PROC_LOCK_MAIN)); + ERTS_RECV_MARK_SAVE(c_p); + ERTS_RECV_MARK_SET(c_p); + erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN); } @@ -1239,29 +1228,12 @@ erts_schedule_port2port_signal(Eterm port_num, ErtsProc2PortSigData *sigdp, static ERTS_INLINE void send_badsig(Port *prt) { - ErtsProcLocks rp_locks = ERTS_PROC_LOCKS_XSIG_SEND; - Process* rp; Eterm connected = ERTS_PORT_GET_CONNECTED(prt); ERTS_CHK_NO_PROC_LOCKS; ERTS_LC_ASSERT(erts_get_scheduler_id()); - ASSERT(is_internal_pid(connected)); - - rp = erts_proc_lookup_raw(connected); - if (rp) { - erts_proc_lock(rp, rp_locks); - if (!ERTS_PROC_IS_EXITING(rp)) - (void) erts_send_exit_signal(NULL, - prt->common.id, - rp, - &rp_locks, - am_badsig, - NIL, - NULL, - 0); - if (rp_locks) - erts_proc_unlock(rp, rp_locks); - } /* exit sent */ + erts_proc_sig_send_exit(NULL, prt->common.id, connected, + am_badsig, NIL, 0); } /* send_badsig */ static void @@ -2194,11 +2166,11 @@ call_deliver_port_exit(int bang_op, } if (broken_link) { - ErtsLink *lnk = erts_remove_link(&ERTS_P_LINKS(prt), from); - if (lnk) - erts_destroy_link(lnk); - else + ErtsLink *lnk = erts_link_tree_lookup(ERTS_P_LINKS(prt), from); + if (!lnk) return ERTS_PORT_OP_DROPPED; + erts_link_tree_delete(&ERTS_P_LINKS(prt), lnk); + erts_link_release(lnk); } if (IS_TRACED_FL(prt, F_TRACE_RECEIVE)) @@ -2367,27 +2339,45 @@ set_port_connected(int bang_op, #endif } else { /* Port BIF operation */ - Process *rp = erts_proc_lookup_raw(connect); - if (!rp) - return ERTS_PORT_OP_DROPPED; - erts_proc_lock(rp, ERTS_PROC_LOCK_LINK); - if (ERTS_PROC_IS_EXITING(rp)) { - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - return ERTS_PORT_OP_DROPPED; - } + int created; + ErtsLink *lnk; + + if (is_not_internal_pid(connect)) + return ERTS_PORT_OP_DROPPED; + + lnk = erts_link_tree_lookup_create(&ERTS_P_LINKS(prt), &created, + ERTS_LNK_TYPE_PORT, prt->common.id, + connect); + if (created) { + ErtsLinkData *ldp; + ErtsLink *olnk = erts_link_to_other(lnk, &ldp); + ASSERT(olnk->other.item == prt->common.id); + if (!erts_proc_sig_send_link(NULL, connect, olnk)) { + erts_link_tree_delete(&ERTS_P_LINKS(prt), lnk); + erts_link_release_both(ldp); + return ERTS_PORT_OP_DROPPED; + } + if (IS_TRACED_FL(prt, F_TRACE_PORTS)) + trace_port(prt, am_getting_linked, connect); + } - erts_add_link(&ERTS_P_LINKS(rp), LINK_PID, prt->common.id); - erts_add_link(&ERTS_P_LINKS(prt), LINK_PID, connect); +#ifdef USE_VM_PROBES + if (DTRACE_ENABLED(port_connect)) { + Eterm old_connected = ERTS_PORT_GET_CONNECTED(prt); + DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); + DTRACE_CHARBUF(newprocess_str, DTRACE_TERM_BUF_SIZE); - if (IS_TRACED_FL(rp, F_TRACE_PROCS)) - trace_proc(NULL, 0, rp, am_getting_linked, prt->common.id); + dtrace_pid_str(old_connected, process_str); + erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), + "%T", prt->common.id); + dtrace_pid_str(connect, newprocess_str); + DTRACE4(port_connect, process_str, port_str, prt->name, newprocess_str); + } +#endif ERTS_PORT_SET_CONNECTED(prt, connect); - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - - if (IS_TRACED_FL(prt, F_TRACE_PORTS)) - trace_port(prt, am_getting_linked, connect); if (IS_TRACED_FL(prt, F_TRACE_RECEIVE)) trace_port_receive(prt, from, am_connect, connect); if (IS_TRACED_FL(prt, F_TRACE_SEND)) { @@ -2395,18 +2385,6 @@ set_port_connected(int bang_op, trace_port_send(prt, from, TUPLE2(hp, prt->common.id, am_connected), 1); } -#ifdef USE_VM_PROBES - if (DTRACE_ENABLED(port_connect)) { - DTRACE_CHARBUF(process_str, DTRACE_TERM_BUF_SIZE); - DTRACE_CHARBUF(port_str, DTRACE_TERM_BUF_SIZE); - DTRACE_CHARBUF(newprocess_str, DTRACE_TERM_BUF_SIZE); - - dtrace_pid_str(connect, process_str); - erts_snprintf(port_str, sizeof(DTRACE_CHARBUF_NAME(port_str)), "%T", prt->common.id); - dtrace_proc_str(rp, newprocess_str); - DTRACE4(port_connect, process_str, port_str, prt->name, newprocess_str); - } -#endif } return ERTS_PORT_OP_DONE; @@ -2494,13 +2472,24 @@ erts_port_connect(Process *c_p, } static void -port_unlink(Port *prt, Eterm from) +port_unlink(Port *prt, ErtsLink *lnk) { - ErtsLink *lnk = erts_remove_link(&ERTS_P_LINKS(prt), from); - if (lnk) { + ErtsLinkData *ldp; + ErtsLink *dlnk, *llnk; + + llnk = erts_link_to_other(lnk, &ldp); + dlnk = erts_link_tree_key_delete(&ERTS_P_LINKS(prt), llnk); + if (!dlnk) + erts_link_release(lnk); + else { if (IS_TRACED_FL(prt, F_TRACE_PORTS)) - trace_port(prt, am_getting_unlinked, from); - erts_destroy_link(lnk); + trace_port(prt, am_getting_unlinked, llnk->other.item); + if (dlnk == llnk) + erts_link_release_both(ldp); + else { + erts_link_release(lnk); + erts_link_release(dlnk); + } } } @@ -2508,14 +2497,14 @@ static int port_sig_unlink(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigdp) { if (op == ERTS_PROC2PORT_SIG_EXEC) - port_unlink(prt, sigdp->u.unlink.from); + port_unlink(prt, sigdp->u.unlink.lnk); if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt); return ERTS_PORT_REDS_UNLINK; } ErtsPortOpResult -erts_port_unlink(Process *c_p, Port *prt, Eterm from, Eterm *refp) +erts_port_unlink(Process *c_p, Port *prt, ErtsLink *lnk, Eterm *refp) { ErtsProc2PortSigData *sigdp; ErtsTryImmDrvCallState try_call_state @@ -2528,7 +2517,7 @@ erts_port_unlink(Process *c_p, Port *prt, Eterm from, Eterm *refp) switch (try_imm_drv_call(&try_call_state)) { case ERTS_TRY_IMM_DRV_CALL_OK: - port_unlink(prt, from); + port_unlink(prt, lnk); finalize_imm_drv_call(&try_call_state); BUMP_REDS(c_p, ERTS_PORT_REDS_UNLINK); return ERTS_PORT_OP_DONE; @@ -2541,11 +2530,12 @@ erts_port_unlink(Process *c_p, Port *prt, Eterm from, Eterm *refp) sigdp = erts_port_task_alloc_p2p_sig_data(); sigdp->flags = ERTS_P2P_SIG_TYPE_UNLINK; - sigdp->u.unlink.from = from; - + sigdp->u.unlink.port_id = prt->common.id; + sigdp->u.unlink.lnk = lnk; + return erts_schedule_proc2port_signal(c_p, prt, - c_p ? c_p->common.id : from, + c_p->common.id, refp, sigdp, 0, @@ -2554,45 +2544,24 @@ erts_port_unlink(Process *c_p, Port *prt, Eterm from, Eterm *refp) } static void -port_link_failure(Eterm port_id, Eterm linker) +port_link_failure(Eterm port_id, ErtsLink *lnk) { - Process *rp; - ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCKS_XSIG_SEND; - ASSERT(is_internal_pid(linker)); - rp = erts_pid2proc(NULL, 0, linker, rp_locks); - if (rp) { - ErtsLink *rlnk = erts_remove_link(&ERTS_P_LINKS(rp), port_id); - if (rlnk) { - int xres = erts_send_exit_signal(NULL, - port_id, - rp, - &rp_locks, - am_noproc, - NIL, - NULL, - 0); - if (xres >= 0) { - /* We didn't exit the process and it is traced */ - if (IS_TRACED_FL(rp, F_TRACE_PROCS)) - trace_proc(NULL, 0, rp, am_getting_unlinked, port_id); - } - if (rp_locks) - erts_proc_unlock(rp, rp_locks); - } - } + erts_proc_sig_send_link_exit(NULL, port_id, lnk, am_noproc, NIL); } static void -port_link(Port *prt, erts_aint32_t state, Eterm to) +port_link(Port *prt, erts_aint32_t state, ErtsLink *lnk) { - if (IS_TRACED_FL(prt, F_TRACE_PORTS)) - trace_port(prt, am_getting_linked, to); - if (!(state & ERTS_PORT_SFLGS_INVALID_LOOKUP)) { - erts_add_link(&ERTS_P_LINKS(prt), LINK_PID, to); - } else { - port_link_failure(prt->common.id, to); - if (IS_TRACED_FL(prt, F_TRACE_PORTS)) - trace_port(prt, am_unlink, to); + if (state & ERTS_PORT_SFLGS_INVALID_LOOKUP) + port_link_failure(prt->common.id, lnk); + else { + ErtsLink *rlnk; + rlnk = erts_link_tree_insert_addr_replace(&ERTS_P_LINKS(prt), + lnk); + if (rlnk) + erts_link_release(rlnk); + else if (IS_TRACED_FL(prt, F_TRACE_PORTS)) + trace_port(prt, am_getting_linked, lnk->other.item); } } @@ -2600,17 +2569,16 @@ static int port_sig_link(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigdp) { if (op == ERTS_PROC2PORT_SIG_EXEC) - port_link(prt, state, sigdp->u.link.to); - else { - port_link_failure(sigdp->u.link.port, sigdp->u.link.to); - } + port_link(prt, state, sigdp->u.link.lnk); + else + port_link_failure(sigdp->u.link.port_id, sigdp->u.link.lnk); if (sigdp->flags & ERTS_P2P_SIG_DATA_FLG_REPLY) port_sched_op_reply(sigdp->caller, sigdp->ref, am_true, prt); return ERTS_PORT_REDS_LINK; } ErtsPortOpResult -erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp) +erts_port_link(Process *c_p, Port *prt, ErtsLink *lnk, Eterm *refp) { ErtsProc2PortSigData *sigdp; ErtsTryImmDrvCallState try_call_state @@ -2623,7 +2591,7 @@ erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp) switch (try_imm_drv_call(&try_call_state)) { case ERTS_TRY_IMM_DRV_CALL_OK: - port_link(prt, try_call_state.state, to); + port_link(prt, try_call_state.state, lnk); finalize_imm_drv_call(&try_call_state); BUMP_REDS(c_p, ERTS_PORT_REDS_LINK); return ERTS_PORT_OP_DONE; @@ -2636,12 +2604,12 @@ erts_port_link(Process *c_p, Port *prt, Eterm to, Eterm *refp) sigdp = erts_port_task_alloc_p2p_sig_data(); sigdp->flags = ERTS_P2P_SIG_TYPE_LINK; - sigdp->u.link.port = prt->common.id; - sigdp->u.link.to = to; + sigdp->u.link.port_id = prt->common.id; + sigdp->u.link.lnk = lnk; return erts_schedule_proc2port_signal(c_p, prt, - c_p ? c_p->common.id : to, + c_p->common.id, refp, sigdp, 0, @@ -2650,51 +2618,23 @@ 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) +port_monitor_failure(Eterm port_id, ErtsMonitor *mon) { - 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_proc_unlock(origin_p, p_locks); + erts_proc_sig_send_monitor_down(mon, am_noproc); } /* 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) +port_monitor(Port *prt, erts_aint32_t state, ErtsMonitor *mon) { - 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_ordinary_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_proc_unlock(origin_p, p_locks); - } else { -failure: - port_monitor_failure(prt->common.id, origin, ref); + ASSERT(prt); + if (state & ERTS_PORT_SFLGS_INVALID_LOOKUP) + port_monitor_failure(prt->common.id, mon); + else { + ASSERT(erts_monitor_is_target(mon)); + erts_monitor_list_insert(&ERTS_P_LT_MONITORS(prt), mon); } } @@ -2702,24 +2642,11 @@ static int port_sig_monitor(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigdp) { - Eterm hp[ERTS_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); - } + if (op == ERTS_PROC2PORT_SIG_EXEC) + port_monitor(prt, state, sigdp->u.monitor.mon); + else + port_monitor_failure(sigdp->u.monitor.port_id, + sigdp->u.monitor.mon); return ERTS_PORT_REDS_MONITOR; } @@ -2727,95 +2654,63 @@ port_sig_monitor(Port *prt, erts_aint32_t state, int op, * 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) +erts_port_monitor(Process *c_p, Port *port, ErtsMonitor *mon) { ErtsProc2PortSigData *sigdp; ErtsTryImmDrvCallState try_call_state = ERTS_INIT_TRY_IMM_DRV_CALL_STATE( - origin, port, ERTS_PORT_SFLGS_INVALID_LOOKUP, + c_p, + port, + ERTS_PORT_SFLGS_INVALID_LOOKUP, 0, - 0, /* trap_ref is always set so !trap_ref always is false */ + !0, am_monitor); - ASSERT(origin); + ASSERT(c_p); ASSERT(port); - ASSERT(is_atom(name) || is_port(name)); - ASSERT(refp); + ASSERT(mon); 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); + port_monitor(port, try_call_state.state, mon); finalize_imm_drv_call(&try_call_state); - BUMP_REDS(origin, ERTS_PORT_REDS_MONITOR); + BUMP_REDS(c_p, ERTS_PORT_REDS_MONITOR); return ERTS_PORT_OP_DONE; case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT: - return ERTS_PORT_OP_BADARG; + return ERTS_PORT_OP_DROPPED; 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 */ + sigdp->u.monitor.port_id = port->common.id; + sigdp->u.monitor.mon = mon; /* Ref contents will be initialized here */ - return erts_schedule_proc2port_signal(origin, port, origin->common.id, - refp, sigdp, 0, NULL, + return erts_schedule_proc2port_signal(c_p, + port, + c_p->common.id, + NULL, + 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_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) +port_demonitor(Port *port, erts_aint32_t state, ErtsMonitor *mon) { - 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_proc_unlock(origin_p, p_locks); - } - } else { - port_demonitor_failure(port->common.id, origin, ref); + ErtsMonitorData *mdp = erts_monitor_to_data(mon); + ASSERT(port && mon); + ASSERT(erts_monitor_is_origin(mon)); + if (!erts_monitor_is_in_table(&mdp->target)) + erts_monitor_release(mon); + else { + erts_monitor_list_delete(&ERTS_P_LT_MONITORS(port), &mdp->target); + erts_monitor_release_both(mdp); } } @@ -2823,73 +2718,47 @@ static int port_sig_demonitor(Port *prt, erts_aint32_t state, int op, ErtsProc2PortSigData *sigdp) { - Eterm hp[ERTS_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); - } + if (op == ERTS_PROC2PORT_SIG_EXEC) + port_demonitor(prt, state, sigdp->u.demonitor.mon); + else + erts_monitor_release(sigdp->u.demonitor.mon); 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) +ErtsPortOpResult +erts_port_demonitor(Process *c_p, Port *prt, ErtsMonitor *mon) { - 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, + prt, ERTS_PORT_SFLGS_INVALID_LOOKUP, 0, - !trap_ref, + !0, am_demonitor); - ASSERT(origin); - ASSERT(target); - ASSERT(is_internal_ref(ref)); + ASSERT(c_p && prt && mon); 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); + port_demonitor(prt, try_call_state.state, mon); finalize_imm_drv_call(&try_call_state); - if (mode == ERTS_PORT_DEMONITOR_NORMAL) { - BUMP_REDS(origin, ERTS_PORT_REDS_DEMONITOR); - } + BUMP_REDS(c_p, ERTS_PORT_REDS_DEMONITOR); return ERTS_PORT_OP_DONE; case ERTS_TRY_IMM_DRV_CALL_INVALID_PORT: - return ERTS_PORT_OP_BADARG; + return ERTS_PORT_OP_DROPPED; 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; - { - Uint32 *nums = internal_ref_numbers(ref); - /* Start from 1 skip ref arity */ - sys_memcpy(sigdp->u.demonitor.ref, - nums, - sizeof(sigdp->u.demonitor.ref)); - } + sigdp->u.demonitor.port_id = prt->common.id; + sigdp->u.demonitor.mon = mon; /* Ref contents will be initialized here */ - return erts_schedule_proc2port_signal(c_p, target, origin->common.id, - trap_ref, sigdp, 0, NULL, + return erts_schedule_proc2port_signal(c_p, prt, c_p->common.id, + NULL, sigdp, 0, NULL, port_sig_demonitor); } @@ -2898,11 +2767,13 @@ init_ack_send_reply(Port *port, Eterm resp) { if (!is_internal_port(resp)) { - Process *rp = erts_proc_lookup_raw(port->async_open_port->to); - erts_proc_lock(rp, ERTS_PROC_LOCK_LINK); - erts_remove_link(&ERTS_P_LINKS(port), port->async_open_port->to); - erts_remove_link(&ERTS_P_LINKS(rp), port->common.id); - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); + Eterm proc = port->async_open_port->to; + ErtsLink *lnk = erts_link_tree_lookup(ERTS_P_LINKS(port), + proc); + if (lnk) { + erts_link_tree_delete(&ERTS_P_LINKS(port), lnk); + erts_proc_sig_send_unlink(NULL, lnk); + } } port_sched_op_reply(port->async_open_port->to, port->async_open_port->ref, @@ -2929,7 +2800,7 @@ erl_drv_init_ack(ErlDrvPort ix, ErlDrvData res) { break; case -2: { char *str = erl_errno_id(errno); - resp = erts_atom_put((byte *) str, strlen(str), + resp = erts_atom_put((byte *) str, sys_strlen(str), ERTS_ATOM_ENC_LATIN1, 1); break; } @@ -2964,7 +2835,7 @@ void erts_init_io(int port_tab_size, int port_tab_size_ignore_files, int legacy_port_tab) { - ErlDrvEntry** dp; + ErtsStaticDriver* dp; UWord common_element_size; erts_rwmtx_opt_t drv_list_rwmtx_opts = ERTS_RWMTX_OPT_DEFAULT_INITER; drv_list_rwmtx_opts.type = ERTS_RWMTX_TYPE_EXTREMELY_FREQUENT_READ; @@ -3023,8 +2894,8 @@ void erts_init_io(int port_tab_size, init_driver(&forker_driver, &forker_driver_entry, NULL); #endif erts_init_static_drivers(); - for (dp = driver_tab; *dp != NULL; dp++) - erts_add_driver_entry(*dp, NULL, 1); + for (dp = driver_tab; dp->de != NULL; dp++) + erts_add_driver_entry(dp->de, NULL, 1, dp->taint); erts_tsd_set(driver_list_lock_status_key, NULL); erts_rwmtx_rwunlock(&erts_driver_list_lock); @@ -3680,6 +3551,7 @@ terminate_port(Port *prt) ASSERT(!ERTS_P_LINKS(prt)); ASSERT(!ERTS_P_MONITORS(prt)); + ASSERT(!ERTS_P_LT_MONITORS(prt)); /* state may be altered by kill_port() below */ state = erts_atomic32_read_band_nob(&prt->state, @@ -3767,146 +3639,25 @@ 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) -{ - switch (mon->type) { - case MON_ORIGIN: { - ErtsMonitor *rmon; - Process *rp; - - ASSERT(is_internal_pid(mon->u.pid)); - rp = erts_pid2proc(NULL, 0, mon->u.pid, ERTS_PROC_LOCK_LINK); - if (!rp) { - goto done; - } - rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), mon->ref); - erts_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; - } - done: - erts_destroy_monitor(mon); -} - - - typedef struct { - Port *port; + Eterm port_id; Eterm reason; -} SweepContext; +} ErtsPortExitContext; -static void sweep_one_link(ErtsLink *lnk, void *vpsc) +static void link_port_exit(ErtsLink *lnk, void *vpectxt) { - SweepContext *psc = vpsc; - DistEntry *dep; - Process *rp; - Eterm port_id = psc->port->common.id; - - ASSERT(lnk->type == LINK_PID); - - if (IS_TRACED_FL(psc->port, F_TRACE_PORTS)) - trace_port(psc->port, am_unlink, lnk->pid); - - if (is_external_pid(lnk->pid)) { - dep = external_pid_dist_entry(lnk->pid); - if(dep != erts_this_dist_entry) { - ErtsDistLinkData dld; - ErtsDSigData dsd; - int code; - code = erts_dsig_prepare(&dsd, dep, NULL, 0, ERTS_DSP_NO_LOCK, 0, 0); - switch (code) { - case ERTS_DSIG_PREP_NOT_ALIVE: - case ERTS_DSIG_PREP_NOT_CONNECTED: - break; - case ERTS_DSIG_PREP_PENDING: - case ERTS_DSIG_PREP_CONNECTED: - erts_remove_dist_link(&dld, port_id, lnk->pid, dep); - erts_destroy_dist_link(&dld); - code = erts_dsig_send_exit(&dsd, port_id, lnk->pid, - psc->reason); - ASSERT(code == ERTS_DSIG_SEND_OK); - break; - default: - ASSERT(! "Invalid dsig prepare result"); - break; - } - } - } else { - ErtsProcLocks rp_locks = ERTS_PROC_LOCK_LINK|ERTS_PROC_LOCKS_XSIG_SEND; - ASSERT(is_internal_pid(lnk->pid)); - rp = erts_pid2proc(NULL, 0, lnk->pid, rp_locks); - if (rp) { - ErtsLink *rlnk = erts_remove_link(&ERTS_P_LINKS(rp), port_id); - - if (rlnk) { - int xres = erts_send_exit_signal(NULL, - port_id, - rp, - &rp_locks, - psc->reason, - NIL, - NULL, - 0); - if (xres >= 0) { - if (rp_locks & ERTS_PROC_LOCKS_XSIG_SEND) { - erts_proc_unlock(rp, ERTS_PROC_LOCKS_XSIG_SEND); - rp_locks &= ~ERTS_PROC_LOCKS_XSIG_SEND; - } - /* We didn't exit the process and it is traced */ - if (IS_TRACED_FL(rp, F_TRACE_PROCS)) - trace_proc(NULL, 0, rp, am_getting_unlinked, port_id); - } - erts_destroy_link(rlnk); - } - - erts_proc_unlock(rp, rp_locks); - } - } - erts_destroy_link(lnk); + ErtsPortExitContext *pectxt = vpectxt; + erts_proc_sig_send_link_exit(NULL, pectxt->port_id, + lnk, pectxt->reason, NIL); } -static void -port_fire_one_monitor(ErtsMonitor *mon, void *ctx0) +static void monitor_port_exit(ErtsMonitor *mon, void *vpectxt) { - Process *origin; - ErtsProcLocks origin_locks; - - if (mon->type != MON_TARGET || ! is_pid(mon->u.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->u.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_proc_unlock(origin, origin_locks); - - if (rmon) { - erts_destroy_monitor(rmon); - } - } + ErtsPortExitContext *pectxt = vpectxt; + if (erts_monitor_is_target(mon)) + erts_proc_sig_send_monitor_down(mon, pectxt->reason); + else + erts_proc_sig_send_demonitor(mon); } /* 'from' is sending 'this_port' an exit signal, (this_port must be internal). @@ -3923,9 +3674,12 @@ int erts_deliver_port_exit(Port *prt, Eterm from, Eterm reason, int send_closed, int drop_normal) { - ErtsLink *lnk; + ErtsLink *links; + ErtsMonitor *monitors; + ErtsMonitor *lt_monitors; Eterm modified_reason; erts_aint32_t state, set_state_flags; + ErtsPortExitContext pectxt; ERTS_CHK_NO_PROC_LOCKS; ERTS_LC_ASSERT(erts_lc_is_port_locked(prt)); @@ -3973,23 +3727,36 @@ erts_deliver_port_exit(Port *prt, Eterm from, Eterm reason, int send_closed, set_busy_port(ERTS_Port2ErlDrvPort(prt), 0); + links = ERTS_P_LINKS(prt); + ERTS_P_LINKS(prt) = NULL; + monitors = ERTS_P_MONITORS(prt); + ERTS_P_MONITORS(prt) = NULL; + lt_monitors = ERTS_P_LT_MONITORS(prt); + ERTS_P_LT_MONITORS(prt) = NULL; + if (prt->common.u.alive.reg != NULL) (void) erts_unregister_name(NULL, 0, prt, prt->common.u.alive.reg->name); - { - SweepContext sc = {prt, modified_reason}; - lnk = ERTS_P_LINKS(prt); - ERTS_P_LINKS(prt) = NULL; - erts_sweep_links(lnk, &sweep_one_link, &sc); + pectxt.port_id = prt->common.id; + pectxt.reason = modified_reason; + + if (links) + erts_monitor_tree_foreach_delete(&links, + link_port_exit, + (void *) &pectxt); + + if (monitors || lt_monitors) { + DRV_MONITOR_LOCK_PDL(prt); + if (monitors) + erts_monitor_tree_foreach_delete(&monitors, + monitor_port_exit, + (void *) &pectxt); + if (lt_monitors) + erts_monitor_list_foreach_delete(<_monitors, + monitor_port_exit, + (void *) &pectxt); + DRV_MONITOR_UNLOCK_PDL(prt); } - DRV_MONITOR_LOCK_PDL(prt); - { - 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(prt); if ((state & ERTS_PORT_SFLG_DISTRIBUTION) && prt->dist_entry) { erts_do_net_exits(prt->dist_entry, modified_reason); @@ -5069,14 +4836,18 @@ typedef struct { static void prt_one_monitor(ErtsMonitor *mon, void *vprtd) { + ErtsMonitorData *mdp = erts_monitor_to_data(mon); prt_one_lnk_data *prtd = (prt_one_lnk_data *) vprtd; - erts_print(prtd->to, prtd->arg, "(%T,%T)", mon->u.pid, mon->ref); + if (mon->type == ERTS_MON_TYPE_RESOURCE && erts_monitor_is_target(mon)) + erts_print(prtd->to, prtd->arg, "(%p,%T)", mon->other.ptr, mdp->ref); + else + erts_print(prtd->to, prtd->arg, "(%T,%T)", mon->other.item, mdp->ref); } static void prt_one_lnk(ErtsLink *lnk, void *vprtd) { prt_one_lnk_data *prtd = (prt_one_lnk_data *) vprtd; - erts_print(prtd->to, prtd->arg, "%T", lnk->pid); + erts_print(prtd->to, prtd->arg, "%T", lnk->other.item); } static void dump_port_state(fmtfn_t to, void *arg, erts_aint32_t state) @@ -5188,15 +4959,18 @@ print_port_info(Port *p, fmtfn_t to, void *arg) prtd.to = to; prtd.arg = arg; erts_print(to, arg, "Links: "); - erts_doforall_links(ERTS_P_LINKS(p), &prt_one_lnk, &prtd); + erts_link_tree_foreach(ERTS_P_LINKS(p), prt_one_lnk, (void *) &prtd); erts_print(to, arg, "\n"); } - if (ERTS_P_MONITORS(p)) { + if (ERTS_P_MONITORS(p) || ERTS_P_LT_MONITORS(p)) { prt_one_lnk_data prtd; prtd.to = to; prtd.arg = arg; erts_print(to, arg, "Monitors: "); - erts_doforall_monitors(ERTS_P_MONITORS(p), &prt_one_monitor, &prtd); + erts_monitor_tree_foreach(ERTS_P_MONITORS(p), prt_one_monitor, + (void *) &prtd); + erts_monitor_list_foreach(ERTS_P_LT_MONITORS(p), prt_one_monitor, + (void *) &prtd); erts_print(to, arg, "\n"); } if (p->suspended) { @@ -7033,24 +6807,23 @@ static int do_driver_monitor_process(Port *prt, ErlDrvMonitor *monitor) { Eterm buf[ERTS_REF_THING_SIZE]; - Process *rp; Eterm ref; + ErtsMonitorData *mdp; - if (prt->drv_ptr->process_exit == NULL) { + if (!prt->drv_ptr->process_exit) return -1; - } - rp = erts_pid2proc_opt(NULL, 0, - (Eterm) process, ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X); - if (!rp) { - return 1; - } ref = erts_make_ref_in_buffer(buf); - erts_add_monitor(&ERTS_P_MONITORS(prt), MON_ORIGIN, ref, rp->common.id, NIL); - erts_add_monitor(&ERTS_P_MONITORS(rp), MON_TARGET, ref, prt->common.id, NIL); + mdp = erts_monitor_create(ERTS_MON_TYPE_PORT, ref, + prt->common.id, process, NIL); + + if (!erts_proc_sig_send_monitor(&mdp->target, process)) { + erts_monitor_release_both(mdp); + return 1; + } + + erts_monitor_tree_insert(&ERTS_P_MONITORS(prt), &mdp->origin); - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); erts_ref_to_driver_monitor(ref,monitor); return 0; } @@ -7083,37 +6856,17 @@ int driver_monitor_process(ErlDrvPort drvport, static int do_driver_demonitor_process(Port *prt, const ErlDrvMonitor *monitor) { Eterm heap[ERTS_REF_THING_SIZE]; - Process *rp; Eterm ref; ErtsMonitor *mon; - Eterm to; ref = erts_driver_monitor_to_ref(heap, monitor); - mon = erts_lookup_monitor(ERTS_P_MONITORS(prt), ref); - if (mon == NULL) { - return 1; - } - ASSERT(mon->type == MON_ORIGIN); - to = mon->u.pid; - ASSERT(is_internal_pid(to)); - rp = erts_pid2proc_opt(NULL, - 0, - to, - ERTS_PROC_LOCK_LINK, - ERTS_P2P_FLG_ALLOW_OTHER_X); - mon = erts_remove_monitor(&ERTS_P_MONITORS(prt), ref); - if (mon) { - erts_destroy_monitor(mon); - } - if (rp) { - ErtsMonitor *rmon; - rmon = erts_remove_monitor(&ERTS_P_MONITORS(rp), ref); - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - if (rmon != NULL) { - erts_destroy_monitor(rmon); - } - } + mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(prt), ref); + if (!mon || !erts_monitor_is_origin(mon)) + return 1; + + erts_monitor_tree_delete(&ERTS_P_MONITORS(prt), mon); + erts_proc_sig_send_demonitor(mon); return 0; } @@ -7142,19 +6895,16 @@ static ErlDrvTermData do_driver_get_monitored_process(Port *prt,const ErlDrvMoni { Eterm ref; ErtsMonitor *mon; - Eterm to; Eterm heap[ERTS_REF_THING_SIZE]; ref = erts_driver_monitor_to_ref(heap, monitor); - mon = erts_lookup_monitor(ERTS_P_MONITORS(prt), ref); - if (mon == NULL) { + mon = erts_monitor_tree_lookup(ERTS_P_MONITORS(prt), ref); + if (!mon || !erts_monitor_is_origin(mon)) return driver_term_nil; - } - ASSERT(mon->type == MON_ORIGIN); - to = mon->u.pid; - ASSERT(is_internal_pid(to)); - return (ErlDrvTermData) to; + + ASSERT(is_internal_pid(mon->other.item)); + return (ErlDrvTermData) mon->other.item; } @@ -7186,24 +6936,27 @@ int driver_compare_monitors(const ErlDrvMonitor *monitor1, ERTS_REF_THING_SIZE*sizeof(Eterm)); } -void erts_fire_port_monitor(Port *prt, Eterm ref) +void erts_fire_port_monitor(Port *prt, ErtsMonitor *tmon) { - ErtsMonitor *rmon; + ErtsMonitorData *mdp; void (*callback)(ErlDrvData drv_data, ErlDrvMonitor *monitor); ErlDrvMonitor drv_monitor; int fpe_was_unmasked; ERTS_MSACC_PUSH_STATE_M(); ERTS_LC_ASSERT(erts_lc_is_port_locked(prt)); - ASSERT(prt->drv_ptr != NULL); + ASSERT(prt->drv_ptr != NULL); + ASSERT(erts_monitor_is_target(tmon)); + mdp = erts_monitor_to_data(tmon); DRV_MONITOR_LOCK_PDL(prt); - if (erts_lookup_monitor(ERTS_P_MONITORS(prt), ref) == NULL) { + if (!erts_monitor_is_in_table(&mdp->origin)) { DRV_MONITOR_UNLOCK_PDL(prt); + erts_monitor_release(tmon); return; } callback = prt->drv_ptr->process_exit; ASSERT(callback != NULL); - erts_ref_to_driver_monitor(ref,&drv_monitor); + erts_ref_to_driver_monitor(mdp->ref,&drv_monitor); ERTS_MSACC_SET_STATE_CACHED_M(ERTS_MSACC_STATE_PORT); DRV_MONITOR_UNLOCK_PDL(prt); #ifdef USE_VM_PROBES @@ -7227,11 +6980,9 @@ void erts_fire_port_monitor(Port *prt, Eterm ref) DRV_MONITOR_LOCK_PDL(prt); ERTS_MSACC_POP_STATE_M(); /* remove monitor *after* callback */ - rmon = erts_remove_monitor(&ERTS_P_MONITORS(prt), ref); + erts_monitor_tree_delete(&ERTS_P_MONITORS(prt), &mdp->origin); DRV_MONITOR_UNLOCK_PDL(prt); - if (rmon) { - erts_destroy_monitor(rmon); - } + erts_monitor_release_both(mdp); } @@ -7276,8 +7027,7 @@ driver_failure_term(ErlDrvPort ix, Eterm term, int eof) int driver_exit(ErlDrvPort ix, int err) { Port* prt = erts_drvport2port(ix); - Process* rp; - ErtsLink *lnk, *rlnk = NULL; + ErtsLink *lnk; Eterm connected; ERTS_CHK_NO_PROC_LOCKS; @@ -7286,22 +7036,10 @@ int driver_exit(ErlDrvPort ix, int err) return -1; connected = ERTS_PORT_GET_CONNECTED(prt); - rp = erts_pid2proc(NULL, 0, connected, ERTS_PROC_LOCK_LINK); - if (rp) { - rlnk = erts_remove_link(&ERTS_P_LINKS(rp),prt->common.id); - } - - lnk = erts_remove_link(&ERTS_P_LINKS(prt), connected); - - if (rp) - erts_proc_unlock(rp, ERTS_PROC_LOCK_LINK); - - if (rlnk != NULL) { - erts_destroy_link(rlnk); - } - - if (lnk != NULL) { - erts_destroy_link(lnk); + lnk = erts_link_tree_lookup(ERTS_P_LINKS(prt), connected); + if (lnk) { + erts_link_tree_delete(&ERTS_P_LINKS(prt), lnk); + erts_proc_sig_send_unlink(NULL, lnk); } if (err == 0) @@ -7324,7 +7062,7 @@ int driver_failure_atom(ErlDrvPort ix, char* string) { return driver_failure_term(ix, erts_atom_put((byte *) string, - strlen(string), + sys_strlen(string), ERTS_ATOM_ENC_LATIN1, 1), 0); @@ -7613,7 +7351,8 @@ no_stop_select_callback(ErlDrvEvent event, void* private) } #define IS_DRIVER_VERSION_GE(DE,MAJOR,MINOR) \ - ((DE)->major_version >= (MAJOR) && (DE)->minor_version >= (MINOR)) + ((DE)->major_version > (MAJOR) || \ + ((DE)->major_version == (MAJOR) && (DE)->minor_version >= (MINOR))) static int init_driver(erts_driver_t *drv, ErlDrvEntry *de, DE_Handle *handle) @@ -7696,13 +7435,14 @@ void add_driver_entry(ErlDrvEntry *drv){ * Ignore result of erts_add_driver_entry, the init is not * allowed to fail when drivers are added by drivers. */ - erts_add_driver_entry(drv, NULL, rec_lock != NULL); + erts_add_driver_entry(drv, NULL, rec_lock != NULL, 0); } -int erts_add_driver_entry(ErlDrvEntry *de, DE_Handle *handle, int driver_list_locked) +int erts_add_driver_entry(ErlDrvEntry *de, DE_Handle *handle, + int driver_list_locked, int taint) { erts_driver_t *dp = erts_alloc(ERTS_ALC_T_DRIVER, sizeof(erts_driver_t)); - int res; + int err = 0; if (!driver_list_locked) { erts_rwmtx_rwlock(&erts_driver_list_lock); @@ -7719,9 +7459,21 @@ int erts_add_driver_entry(ErlDrvEntry *de, DE_Handle *handle, int driver_list_lo erts_tsd_set(driver_list_lock_status_key, (void *) 1); } - res = init_driver(dp, de, handle); + if (taint) { + Eterm name_atom = erts_atom_put((byte*)de->driver_name, + sys_strlen(de->driver_name), + ERTS_ATOM_ENC_LATIN1, 0); + if (is_atom(name_atom)) + erts_add_taint(name_atom); + else + err = 1; + } + + if (!err) { + err = init_driver(dp, de, handle); + } - if (res != 0) { + if (err) { /* * Remove it all again... */ @@ -7736,7 +7488,7 @@ int erts_add_driver_entry(ErlDrvEntry *de, DE_Handle *handle, int driver_list_lo erts_tsd_set(driver_list_lock_status_key, NULL); erts_rwmtx_rwunlock(&erts_driver_list_lock); } - return res; + return err; } /* Not allowed for dynamic drivers */ diff --git a/erts/emulator/beam/lttng-wrapper.h b/erts/emulator/beam/lttng-wrapper.h index 0bc75c1552..e7f2971bf7 100644 --- a/erts/emulator/beam/lttng-wrapper.h +++ b/erts/emulator/beam/lttng-wrapper.h @@ -61,13 +61,13 @@ #define lttng_proc_to_mfa_str(p, Name) \ do { \ if (ERTS_PROC_IS_EXITING((p))) { \ - strcpy(Name, "<exiting>"); \ + sys_strcpy(Name, "<exiting>"); \ } else { \ BeamInstr *_fptr = find_function_from_pc((p)->i); \ if (_fptr) { \ lttng_mfa_to_str(_fptr[0],_fptr[1],_fptr[2], Name); \ } else { \ - strcpy(Name, "<unknown>"); \ + sys_strcpy(Name, "<unknown>"); \ } \ } \ } while(0) diff --git a/erts/emulator/beam/msg_instrs.tab b/erts/emulator/beam/msg_instrs.tab index d6d4d2fb49..289436da6f 100644 --- a/erts/emulator/beam/msg_instrs.tab +++ b/erts/emulator/beam/msg_instrs.tab @@ -2,7 +2,7 @@ // // %CopyrightBegin% // -// Copyright Ericsson AB 2017. All Rights Reserved. +// Copyright Ericsson AB 2017-2018. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -45,23 +45,16 @@ i_recv_mark() { /* - * Save the current position in message buffer. + * Save the current end of message queue */ - c_p->msg.saved_last = c_p->msg.last; + ERTS_RECV_MARK_SAVE(c_p); } i_recv_set() { /* - * If c_p->msg.saved_last is non-zero, it points to the first - * message that could possibly be matched out. - * - * If c_p->msg.saved_last is zero, it means that it was invalidated - * because another receive was executed before this i_recv_set() - * instruction was reached. + * If previously saved recv mark, set peek position to it */ - if (c_p->msg.saved_last) { - c_p->msg.save = c_p->msg.saved_last; - } + ERTS_RECV_MARK_SET(c_p); SET_I($NEXT_INSTRUCTION); goto loop_rec_top__; //| -no_next @@ -79,7 +72,6 @@ i_loop_rec(Dest) { /* Entry point from recv_set */ loop_rec_top__: - ; /* * We need to disable GC while matching messages @@ -89,34 +81,59 @@ i_loop_rec(Dest) { ASSERT(!(c_p->flags & F_DELAY_GC)); c_p->flags |= F_DELAY_GC; - /* Entry point from loop_rec_end */ + /* Entry point from loop_rec_end (and locally) */ loop_rec__: + if (FCALLS <= 0 && FCALLS <= neg_o_reds) { + $SET_CP_I_ABS(I); + c_p->flags &= ~F_DELAY_GC; + SWAPOUT; + c_p->arity = 0; + c_p->current = NULL; + goto do_schedule; + } + + ASSERT(!ERTS_PROC_IS_EXITING(c_p)); + PROCESS_MAIN_CHK_LOCKS(c_p); msgp = PEEK_MESSAGE(c_p); - if (!msgp) { - erts_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); - /* Make sure messages wont pass exit signals... */ - if (ERTS_PROC_PENDING_EXIT(c_p)) { - erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); - SWAPOUT; - c_p->flags &= ~F_DELAY_GC; - c_p->arity = 0; - goto do_schedule; /* Will be rescheduled for exit */ - } - ERTS_MSGQ_MV_INQ2PRIVQ(c_p); - msgp = PEEK_MESSAGE(c_p); - if (msgp) { - erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); - } else { + if (ERTS_UNLIKELY(msgp == NULL)) { + int get_out; + SWAPOUT; + FCALLS -= erts_proc_sig_receive_helper(c_p, FCALLS, neg_o_reds, + &msgp, &get_out); + SWAPIN; + if (ERTS_UNLIKELY(msgp == NULL)) { + if (get_out) { + if (get_out < 0) { + ASSERT(FCALLS <= 0 && FCALLS <= neg_o_reds); + goto loop_rec__; /* yield */ + } + else { + ASSERT(ERTS_PROC_IS_EXITING(c_p)); + goto do_schedule; /* exit */ + } + } + + /* + * If there are no more messages in queue + * (and we are not yielding or exiting) + * erts_proc_sig_receive_helper() + * returns with message queue lock locked... + */ c_p->flags &= ~F_DELAY_GC; $SET_I_REL($Dest); Goto(*I); /* Jump to a wait or wait_timeout instruction */ } } - if (is_non_value(ERL_MESSAGE_TERM(msgp))) { + + ASSERT(msgp == PEEK_MESSAGE(c_p)); + ASSERT(msgp && ERTS_SIG_IS_MSG(msgp)); + + if (ERTS_UNLIKELY(ERTS_SIG_IS_EXTERNAL_MSG(msgp))) { + FCALLS -= 10; /* FIXME: bump appropriate amount... */ SWAPOUT; /* erts_decode_dist_message() may write to heap... */ if (!erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) { /* @@ -127,13 +144,16 @@ i_loop_rec(Dest) { ASSERT(HTOP == c_p->htop && E == c_p->stop); /* TODO: Add DTrace probe for this bad message situation? */ UNLINK_MESSAGE(c_p, msgp); - c_p->msg.saved_last = 0; /* Better safe than sorry. */ msgp->next = NULL; erts_cleanup_messages(msgp); goto loop_rec__; } SWAPIN; } + + ASSERT(msgp == PEEK_MESSAGE(c_p)); + ASSERT(ERTS_SIG_IS_INTERNAL_MSG(msgp)); + r(0) = ERL_MESSAGE_TERM(msgp); } @@ -209,13 +229,13 @@ remove_message() { dtrace_proc_str(c_p, receiver_name); token2 = SEQ_TRACE_TOKEN(c_p); if (have_seqtrace(token2)) { - tok_label = signed_val(SEQ_TRACE_T_LABEL(token2)); + tok_label = SEQ_TRACE_T_DTRACE_LABEL(token2); tok_lastcnt = signed_val(SEQ_TRACE_T_LASTCNT(token2)); tok_serial = signed_val(SEQ_TRACE_T_SERIAL(token2)); } DTRACE6(message_receive, receiver_name, size_object(ERL_MESSAGE_TERM(msgp)), - c_p->msg.len - 1, tok_label, tok_lastcnt, tok_serial); + c_p->sig_qs.len - 1, tok_label, tok_lastcnt, tok_serial); } #endif UNLINK_MESSAGE(c_p, msgp); @@ -252,17 +272,8 @@ loop_rec_end(Dest) { $SET_I_REL($Dest); SAVE_MESSAGE(c_p); - if (FCALLS > 0 || FCALLS > neg_o_reds) { - FCALLS--; - goto loop_rec__; - } - - c_p->flags &= ~F_DELAY_GC; - $SET_CP_I_ABS(I); - SWAPOUT; - c_p->arity = 0; - c_p->current = NULL; - goto do_schedule; + FCALLS--; + goto loop_rec__; } timeout_locked() { diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab index 6e03f39d97..8b2d9098a8 100644 --- a/erts/emulator/beam/ops.tab +++ b/erts/emulator/beam/ops.tab @@ -1414,7 +1414,7 @@ has_map_fields Fail Src Size Rest=* => \ ## Transform get_map_elements(s) #{ K1 := V1, K2 := V2 } -get_map_elements Fail Src=xy Size=u==2 Rest=* => \ +get_map_elements Fail Src Size=u==2 Rest=* => \ gen_get_map_element(Fail, Src, Size, Rest) get_map_elements Fail Src Size Rest=* | map_key_sort(Size, Rest) => \ gen_get_map_elements(Fail, Src, Size, Rest) @@ -1424,8 +1424,12 @@ i_get_map_elements f? s I i_get_map_element Fail Src=xy Key=y Dst => \ move Key x | i_get_map_element Fail Src x Dst +i_get_map_element_hash Fail Src=c Key Hash Dst => \ + move Src x | i_get_map_element_hash Fail x Key Hash Dst i_get_map_element_hash f? xy c I xy +i_get_map_element Fail Src=c Key Dst => \ + move Src x | i_get_map_element Fail x Key Dst i_get_map_element f? xy x xy # diff --git a/erts/emulator/beam/packet_parser.c b/erts/emulator/beam/packet_parser.c index f14910bc72..de1d481105 100644 --- a/erts/emulator/beam/packet_parser.c +++ b/erts/emulator/beam/packet_parser.c @@ -200,7 +200,7 @@ static int http_init(void) for (i = 0; i < HTTP_HDR_HASH_SIZE; i++) http_hdr_hash[i] = NULL; for (i = 0; http_hdr_strings[i] != NULL; i++) { - ASSERT(strlen(http_hdr_strings[i]) <= HTTP_MAX_NAME_LEN); + ASSERT(sys_strlen(http_hdr_strings[i]) <= HTTP_MAX_NAME_LEN); http_hdr_table[i].index = i; http_hash_insert(http_hdr_strings[i], &http_hdr_table[i], @@ -516,7 +516,7 @@ static http_atom_t* http_hash_lookup(const char* name, int len, while (ap != NULL) { if ((ap->h == h) && (ap->len == len) && - (strncmp(ap->name, name, len) == 0)) + (sys_strncmp(ap->name, name, len) == 0)) return ap; ap = ap->next; } @@ -656,7 +656,7 @@ int packet_parse_http(const char* buf, int len, int* statep, if (*statep == 0) { /* start-line = Request-Line | Status-Line */ - if (n >= 5 && (strncmp(buf, "HTTP/", 5) == 0)) { + if (n >= 5 && (sys_strncmp(buf, "HTTP/", 5) == 0)) { int major = 0; int minor = 0; int status = 0; @@ -750,7 +750,7 @@ int packet_parse_http(const char* buf, int len, int* statep, } if (n < 8) return -1; - if (strncmp(ptr, "HTTP/", 5) != 0) + if (sys_strncmp(ptr, "HTTP/", 5) != 0) return -1; ptr += 5; n -= 5; diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h index 290e0b209a..be6ab57eeb 100644 --- a/erts/emulator/beam/sys.h +++ b/erts/emulator/beam/sys.h @@ -366,29 +366,11 @@ typedef UWord BeamInstr; # define HAVE_INT64 1 typedef unsigned long Uint64; typedef long Sint64; -# ifdef ULONG_MAX -# define ERTS_UINT64_MAX ULONG_MAX -# endif -# ifdef LONG_MAX -# define ERTS_SINT64_MAX LONG_MAX -# endif -# ifdef LONG_MIN -# define ERTS_SINT64_MIN LONG_MIN -# endif # define ErtsStrToSint64 strtol # elif SIZEOF_LONG_LONG == 8 # define HAVE_INT64 1 typedef unsigned long long Uint64; typedef long long Sint64; -# ifdef ULLONG_MAX -# define ERTS_UINT64_MAX ULLONG_MAX -# endif -# ifdef LLONG_MAX -# define ERTS_SINT64_MAX LLONG_MAX -# endif -# ifdef LLONG_MIN -# define ERTS_SINT64_MIN LLONG_MIN -# endif # define ErtsStrToSint64 strtoll # else # error "No 64-bit integer type found" @@ -402,7 +384,7 @@ typedef long long Sint64; # define ERTS_SINT64_MAX ((Sint64) ((((Uint64) 1) << 63)-1)) #endif #ifndef ERTS_SINT64_MIN -# define ERTS_SINT64_MIN (-1*(((Sint64) 1) << 63)) +# define ERTS_SINT64_MIN ((Sint64) ((((Uint64) 1) << 63))) #endif #if SIZEOF_LONG == 4 @@ -415,6 +397,16 @@ typedef int Sint32; #error Found no appropriate type to use for 'Uint32' and 'Sint32' #endif +#ifndef ERTS_UINT32_MAX +# define ERTS_UINT32_MAX (~((Uint32) 0)) +#endif +#ifndef ERTS_SINT32_MAX +# define ERTS_SINT32_MAX ((Sint32) ((((Uint32) 1) << 31)-1)) +#endif +#ifndef ERTS_SINT32_MIN +# define ERTS_SINT32_MIN ((Sint32) ((((Uint32) 1) << 31))) +#endif + #if SIZEOF_INT == 2 typedef unsigned int Uint16; typedef int Sint16; @@ -425,6 +417,16 @@ typedef short Sint16; #error Found no appropriate type to use for 'Uint16' and 'Sint16' #endif +#ifndef ERTS_UINT16_MAX +# define ERTS_UINT16_MAX (~((Uint16) 0)) +#endif +#ifndef ERTS_SINT16_MAX +# define ERTS_SINT16_MAX ((Sint16) ((((Uint16) 1) << 15)-1)) +#endif +#ifndef ERTS_SINT16_MIN +# define ERTS_SINT16_MIN ((Sint16) ((((Uint16) 1) << 15))) +#endif + #if CHAR_BIT == 8 typedef unsigned char byte; #else @@ -994,18 +996,82 @@ erts_refc_read(erts_refc_t *refcp, erts_aint_t min_val) return val; } -#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ +#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ + -#define sys_memcpy(s1,s2,n) memcpy(s1,s2,n) -#define sys_memmove(s1,s2,n) memmove(s1,s2,n) -#define sys_memcmp(s1,s2,n) memcmp(s1,s2,n) -#define sys_memset(s,c,n) memset(s,c,n) -#define sys_memzero(s, n) memset(s,'\0',n) -#define sys_strcmp(s1,s2) strcmp(s1,s2) -#define sys_strncmp(s1,s2,n) strncmp(s1,s2,n) -#define sys_strcpy(s1,s2) strcpy(s1,s2) -#define sys_strncpy(s1,s2,n) strncpy(s1,s2,n) -#define sys_strlen(s) strlen(s) +/* Thin wrappers around memcpy and friends, which should always be used in + * place of plain memcpy, memset, etc. + * + * Passing NULL to any of these functions is undefined behavior even though it + * may seemingly work when the length (if any) is zero; a compiler can take + * this as a hint that the passed operand may *never* be NULL and then optimize + * based on that information. + */ +ERTS_GLB_INLINE void *sys_memcpy(void *dest, const void *src, size_t n); +ERTS_GLB_INLINE void *sys_memmove(void *dest, const void *src, size_t n); +ERTS_GLB_INLINE int sys_memcmp(const void *s1, const void *s2, size_t n); +ERTS_GLB_INLINE void *sys_memset(void *s, int c, size_t n); +ERTS_GLB_INLINE void *sys_memzero(void *s, size_t n); +ERTS_GLB_INLINE int sys_strcmp(const char *s1, const char *s2); +ERTS_GLB_INLINE int sys_strncmp(const char *s1, const char *s2, size_t n); +ERTS_GLB_INLINE char *sys_strcpy(char *dest, const char *src); +ERTS_GLB_INLINE char *sys_strncpy(char *dest, const char *src, size_t n); +ERTS_GLB_INLINE size_t sys_strlen(const char *s); + +#if ERTS_GLB_INLINE_INCL_FUNC_DEF + +ERTS_GLB_INLINE void *sys_memcpy(void *dest, const void *src, size_t n) +{ + ASSERT(dest != NULL && src != NULL); + return memcpy(dest,src,n); +} +ERTS_GLB_INLINE void *sys_memmove(void *dest, const void *src, size_t n) +{ + ASSERT(dest != NULL && src != NULL); + return memmove(dest,src,n); +} +ERTS_GLB_INLINE int sys_memcmp(const void *s1, const void *s2, size_t n) +{ + ASSERT(s1 != NULL && s2 != NULL); + return memcmp(s1,s2,n); +} +ERTS_GLB_INLINE void *sys_memset(void *s, int c, size_t n) +{ + ASSERT(s != NULL); + return memset(s,c,n); +} +ERTS_GLB_INLINE void *sys_memzero(void *s, size_t n) +{ + ASSERT(s != NULL); + return memset(s,'\0',n); +} +ERTS_GLB_INLINE int sys_strcmp(const char *s1, const char *s2) +{ + ASSERT(s1 != NULL && s2 != NULL); + return strcmp(s1,s2); +} +ERTS_GLB_INLINE int sys_strncmp(const char *s1, const char *s2, size_t n) +{ + ASSERT(s1 != NULL && s2 != NULL); + return strncmp(s1,s2,n); +} +ERTS_GLB_INLINE char *sys_strcpy(char *dest, const char *src) +{ + ASSERT(dest != NULL && src != NULL); + return strcpy(dest,src); + +} +ERTS_GLB_INLINE char *sys_strncpy(char *dest, const char *src, size_t n) +{ + ASSERT(dest != NULL && src != NULL); + return strncpy(dest,src,n); +} +ERTS_GLB_INLINE size_t sys_strlen(const char *s) +{ + ASSERT(s != NULL); + return strlen(s); +} +#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */ /* define function symbols (needed in sys_drv_api) */ #define sys_fp_alloc sys_alloc diff --git a/erts/emulator/drivers/common/inet_drv.c b/erts/emulator/drivers/common/inet_drv.c index 4294fb4f46..1a68f65b52 100644 --- a/erts/emulator/drivers/common/inet_drv.c +++ b/erts/emulator/drivers/common/inet_drv.c @@ -9930,6 +9930,12 @@ static int tcp_recv_closed(tcp_descriptor* desc) set_busy_port(desc->inet.port, 0); inet_reply_error_am(INETP(desc), am_closed); DEBUGF(("tcp_recv_closed(%ld): busy reply 'closed'\r\n", port)); + } else { + /* No blocking send op to reply to right now. + * If next op is a send, make sure it returns {error,closed} + * rather than {error,enotconn}. + */ + desc->tcp_add_flags |= TCP_ADDF_DELAYED_CLOSE_SEND; } if (!desc->inet.active) { /* We must cancel any timer here ! */ diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c index 3323e8640b..74e793577c 100644 --- a/erts/emulator/hipe/hipe_mkliterals.c +++ b/erts/emulator/hipe/hipe_mkliterals.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2016. All Rights Reserved. + * Copyright Ericsson AB 2001-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -519,8 +519,8 @@ static const struct rts_param rts_params[] = { 1, offsetof(struct process, hipe.bif_callee) #endif }, - { 49, "P_MSG_FIRST", 1, offsetof(struct process, msg.first) }, - { 50, "P_MSG_SAVE", 1, offsetof(struct process, msg.save) }, + { 49, "P_MSG_FIRST", 1, offsetof(struct process, sig_qs.first) }, + { 50, "P_MSG_SAVE", 1, offsetof(struct process, sig_qs.save) }, { 51, "P_CALLEE_EXP", 1, offsetof(struct process, hipe.u.callee_exp) }, { 52, "THE_NON_VALUE", 1, (int)THE_NON_VALUE }, @@ -531,8 +531,8 @@ static const struct rts_param rts_params[] = { #endif }, - { 54, "P_MSG_LAST", 1, offsetof(struct process, msg.last) }, - { 55, "P_MSG_SAVED_LAST", 1, offsetof(struct process, msg.saved_last) }, + { 54, "P_MSG_LAST", 1, offsetof(struct process, sig_qs.last) }, + { 55, "P_MSG_SAVED_LAST", 1, offsetof(struct process, sig_qs.saved_last) }, }; #define NR_PARAMS ARRAY_SIZE(rts_params) diff --git a/erts/emulator/hipe/hipe_mode_switch.c b/erts/emulator/hipe/hipe_mode_switch.c index 8b497c9970..bc9a700204 100644 --- a/erts/emulator/hipe/hipe_mode_switch.c +++ b/erts/emulator/hipe/hipe_mode_switch.c @@ -496,8 +496,10 @@ Process *hipe_mode_switch(Process *p, unsigned cmd, Eterm reg[]) erts_proc_lock(p, ERTS_PROC_LOCKS_MSG_RECEIVE); p->i = hipe_beam_pc_resume; p->arity = 0; - erts_atomic32_read_band_relb(&p->state, - ~ERTS_PSFLG_ACTIVE); + if (erts_atomic32_read_nob(&p->state) & ERTS_PSFLG_EXITING) + ASSERT(erts_atomic32_read_nob(&p->state) & ERTS_PSFLG_ACTIVE); + else + erts_atomic32_read_band_relb(&p->state, ~ERTS_PSFLG_ACTIVE); erts_proc_unlock(p, ERTS_PROC_LOCKS_MSG_RECEIVE); do_schedule: { @@ -662,7 +664,8 @@ void hipe_inc_nstack(Process *p) Eterm *new_nstack = erts_alloc(ERTS_ALC_T_HIPE_STK, new_size*sizeof(Eterm)); unsigned used_size = p->hipe.nstend - p->hipe.nsp; - sys_memcpy(new_nstack+new_size-used_size, p->hipe.nsp, used_size*sizeof(Eterm)); + if (used_size) + sys_memcpy(new_nstack+new_size-used_size, p->hipe.nsp, used_size*sizeof(Eterm)); if (p->hipe.nstgraylim) p->hipe.nstgraylim = new_nstack + new_size - (p->hipe.nstend - p->hipe.nstgraylim); if (p->hipe.nstblacklim) diff --git a/erts/emulator/hipe/hipe_native_bif.c b/erts/emulator/hipe/hipe_native_bif.c index 498b43ac6b..cf8c4139be 100644 --- a/erts/emulator/hipe/hipe_native_bif.c +++ b/erts/emulator/hipe/hipe_native_bif.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2017. All Rights Reserved. + * Copyright Ericsson AB 2001-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,6 +35,7 @@ #include "hipe_native_bif.h" #include "hipe_arch.h" #include "hipe_stack.h" +#include "erl_proc_sig_queue.h" /* * These are wrappers for BIFs that may trigger a native @@ -254,7 +255,7 @@ void hipe_handle_exception(Process *c_p) /* Synthesized to avoid having to generate code for it. */ c_p->def_arg_reg[0] = exception_tag[GET_EXC_CLASS(c_p->freason)]; - c_p->msg.saved_last = 0; /* No longer safe to use this position */ + ERTS_RECV_MARK_CLEAR(c_p); /* No longer safe to use this position */ hipe_find_handler(c_p); } @@ -544,38 +545,57 @@ Eterm hipe_check_get_msg(Process *c_p) msgp = PEEK_MESSAGE(c_p); if (!msgp) { - erts_proc_lock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); - /* Make sure messages wont pass exit signals... */ - if (ERTS_PROC_PENDING_EXIT(c_p)) { - erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); - return THE_NON_VALUE; /* Will be rescheduled for exit */ - } - ERTS_MSGQ_MV_INQ2PRIVQ(c_p); - msgp = PEEK_MESSAGE(c_p); - if (msgp) - erts_proc_unlock(c_p, ERTS_PROC_LOCKS_MSG_RECEIVE); - else { - /* XXX: BEAM doesn't need this */ - c_p->hipe_smp.have_receive_locks = 1; - c_p->flags &= ~F_DELAY_GC; - return THE_NON_VALUE; - } + int get_out; + (void) erts_proc_sig_receive_helper(c_p, CONTEXT_REDS, 0, + &msgp, &get_out); + /* FIXME: Need to bump reductions... */ + if (!msgp) { + if (get_out) { + if (get_out < 0) { + /* + * FIXME: We should get out yielding + * here... + */ + goto next_message; + } + /* Go exit... */ + return THE_NON_VALUE; + } + + /* + * If there are no more messages in queue + * (and we are not yielding or exiting) + * erts_proc_sig_receive_helper() + * returns with message queue lock locked... + */ + + /* XXX: BEAM doesn't need this */ + c_p->hipe_smp.have_receive_locks = 1; + c_p->flags &= ~F_DELAY_GC; + return THE_NON_VALUE; + } } - if (is_non_value(ERL_MESSAGE_TERM(msgp)) - && !erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) { - /* - * A corrupt distribution message that we weren't able to decode; - * remove it... - */ - ASSERT(!msgp->data.attached); - UNLINK_MESSAGE(c_p, msgp); - msgp->next = NULL; - erts_cleanup_messages(msgp); - goto next_message; + ASSERT(msgp == PEEK_MESSAGE(c_p)); + ASSERT(msgp && ERTS_SIG_IS_MSG(msgp)); + + if (ERTS_SIG_IS_EXTERNAL_MSG(msgp)) { + /* FIXME: bump appropriate amount... */ + if (!erts_decode_dist_message(c_p, ERTS_PROC_LOCK_MAIN, msgp, 0)) { + /* + * A corrupt distribution message that we weren't able to decode; + * remove it... + */ + /* TODO: Add DTrace probe for this bad message situation? */ + UNLINK_MESSAGE(c_p, msgp); + msgp->next = NULL; + erts_cleanup_messages(msgp); + goto next_message; + } } - ASSERT(is_value(ERL_MESSAGE_TERM(msgp))); + ASSERT(msgp == PEEK_MESSAGE(c_p)); + ASSERT(ERTS_SIG_IS_INTERNAL_MSG(msgp)); return ERL_MESSAGE_TERM(msgp); } diff --git a/erts/emulator/nifs/common/prim_file_nif.c b/erts/emulator/nifs/common/prim_file_nif.c index 6874f41d75..bbd9becb47 100644 --- a/erts/emulator/nifs/common/prim_file_nif.c +++ b/erts/emulator/nifs/common/prim_file_nif.c @@ -891,10 +891,10 @@ static ERL_NIF_TERM set_owner_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM a posix_errno_t posix_errno; efile_path_t path; - Uint32 uid, gid; + Sint32 uid, gid; - if(argc != 3 || !enif_get_uint(env, argv[1], &uid) - || !enif_get_uint(env, argv[2], &gid)) { + if(argc != 3 || !enif_get_int(env, argv[1], &uid) + || !enif_get_int(env, argv[2], &gid)) { return enif_make_badarg(env); } diff --git a/erts/emulator/nifs/common/prim_file_nif.h b/erts/emulator/nifs/common/prim_file_nif.h index cc9bc8f5c3..4194cdc7d9 100644 --- a/erts/emulator/nifs/common/prim_file_nif.h +++ b/erts/emulator/nifs/common/prim_file_nif.h @@ -177,7 +177,7 @@ posix_errno_t efile_set_permissions(const efile_path_t *path, Uint32 permissions /** @brief On Unix, this will set the owner/group to the given values. It will * do nothing on other platforms. */ -posix_errno_t efile_set_owner(const efile_path_t *path, Uint32 owner, Uint32 group); +posix_errno_t efile_set_owner(const efile_path_t *path, Sint32 owner, Sint32 group); /** @brief Resolves the final path of the given link. */ posix_errno_t efile_read_link(ErlNifEnv *env, const efile_path_t *path, ERL_NIF_TERM *result); diff --git a/erts/emulator/nifs/unix/unix_prim_file.c b/erts/emulator/nifs/unix/unix_prim_file.c index 4a6c476882..1637f9cb71 100644 --- a/erts/emulator/nifs/unix/unix_prim_file.c +++ b/erts/emulator/nifs/unix/unix_prim_file.c @@ -687,7 +687,7 @@ posix_errno_t efile_set_permissions(const efile_path_t *path, Uint32 permissions return 0; } -posix_errno_t efile_set_owner(const efile_path_t *path, Uint32 owner, Uint32 group) { +posix_errno_t efile_set_owner(const efile_path_t *path, Sint32 owner, Sint32 group) { if(chown((const char*)path->data, owner, group) < 0) { return errno; } diff --git a/erts/emulator/nifs/win32/win_prim_file.c b/erts/emulator/nifs/win32/win_prim_file.c index 9b79182f2c..8058350b25 100644 --- a/erts/emulator/nifs/win32/win_prim_file.c +++ b/erts/emulator/nifs/win32/win_prim_file.c @@ -801,7 +801,7 @@ posix_errno_t efile_set_permissions(const efile_path_t *path, Uint32 permissions return windows_to_posix_errno(GetLastError()); } -posix_errno_t efile_set_owner(const efile_path_t *path, Uint32 owner, Uint32 group) { +posix_errno_t efile_set_owner(const efile_path_t *path, Sint32 owner, Sint32 group) { (void)path; (void)owner; (void)group; diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c index f93d4c1557..6d531fdb76 100644 --- a/erts/emulator/sys/common/erl_check_io.c +++ b/erts/emulator/sys/common/erl_check_io.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2006-2017. All Rights Reserved. + * Copyright Ericsson AB 2006-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -885,7 +885,7 @@ enif_select(ErlNifEnv* env, ErtsDrvSelectDataState *free_select = NULL; ErtsNifSelectDataState *free_nif = NULL; - ASSERT(!(resource->monitors && resource->monitors->is_dying)); + ASSERT(!resource->monitors); #ifdef ERTS_SYS_CONTINOUS_FD_NUMBERS if (!grow_drv_ev_state(fd)) { diff --git a/erts/emulator/sys/common/erl_mmap.c b/erts/emulator/sys/common/erl_mmap.c index a9c6e72c5f..5dadd8a5a6 100644 --- a/erts/emulator/sys/common/erl_mmap.c +++ b/erts/emulator/sys/common/erl_mmap.c @@ -296,11 +296,10 @@ typedef struct { }ErtsFreeSegMap; struct ErtsMemMapper_ { - int (*reserve_physical)(char *, UWord, int exec); + int (*reserve_physical)(char *, UWord); void (*unreserve_physical)(char *, UWord); int supercarrier; int no_os_mmap; - int executable; /* is client a native code allocator? */ /* * Super unaligned area is located above super aligned * area. That is, `sa.bot` is beginning of the super @@ -358,10 +357,6 @@ char* erts_literals_start; UWord erts_literals_size; #endif -#ifdef ERTS_HAVE_EXEC_MMAPPER -ErtsMemMapper erts_exec_mmapper; -#endif - #define ERTS_MMAP_SIZE_SC_SA_INC(SZ) \ do { \ @@ -1240,7 +1235,6 @@ Eterm build_free_seg_list(Process* p, ErtsFreeSegMap* map) #if HAVE_MMAP # define ERTS_MMAP_PROT (PROT_READ|PROT_WRITE) -# define ERTS_MMAP_PROT_EXEC (PROT_READ|PROT_WRITE|PROT_EXEC) # if defined(MAP_ANONYMOUS) # define ERTS_MMAP_FLAGS (MAP_ANON|MAP_PRIVATE) # define ERTS_MMAP_FD (-1) @@ -1254,26 +1248,24 @@ Eterm build_free_seg_list(Process* p, ErtsFreeSegMap* map) #endif static ERTS_INLINE void * -os_mmap(void *hint_ptr, UWord size, int try_superalign, int executable) +os_mmap(void *hint_ptr, UWord size, int try_superalign) { #if HAVE_MMAP - const int prot = executable ? ERTS_MMAP_PROT_EXEC : ERTS_MMAP_PROT; void *res; #ifdef MAP_ALIGN if (try_superalign) - res = mmap((void *) ERTS_SUPERALIGNED_SIZE, size, prot, + res = mmap((void *) ERTS_SUPERALIGNED_SIZE, size, ERTS_MMAP_PROT, ERTS_MMAP_FLAGS|MAP_ALIGN, ERTS_MMAP_FD, 0); else #endif - res = mmap((void *) hint_ptr, size, prot, + res = mmap((void *) hint_ptr, size, ERTS_MMAP_PROT, ERTS_MMAP_FLAGS, ERTS_MMAP_FD, 0); if (res == MAP_FAILED) return NULL; return res; #elif HAVE_VIRTUALALLOC - const DWORD prot = executable ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE; return (void *) VirtualAlloc(NULL, (SIZE_T) size, - MEM_COMMIT|MEM_RESERVE, prot); + MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE); #else # error "missing mmap() or similar" #endif @@ -1330,7 +1322,6 @@ os_mremap(void *ptr, UWord old_size, UWord new_size, int try_superalign) #if HAVE_MMAP #define ERTS_MMAP_RESERVE_PROT (ERTS_MMAP_PROT) -#define ERTS_MMAP_RESERVE_PROT_EXEC (ERTS_MMAP_PROT_EXEC) #define ERTS_MMAP_RESERVE_FLAGS (ERTS_MMAP_FLAGS|MAP_FIXED) #define ERTS_MMAP_UNRESERVE_PROT (PROT_NONE) #if defined(__FreeBSD__) @@ -1346,10 +1337,9 @@ os_mremap(void *ptr, UWord old_size, UWord new_size, int try_superalign) #endif /* __FreeBSD__ */ static int -os_reserve_physical(char *ptr, UWord size, int exec) +os_reserve_physical(char *ptr, UWord size) { - const int prot = exec ? ERTS_MMAP_RESERVE_PROT_EXEC : ERTS_MMAP_RESERVE_PROT; - void *res = mmap((void *) ptr, (size_t) size, prot, + void *res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_RESERVE_PROT, ERTS_MMAP_RESERVE_FLAGS, ERTS_MMAP_FD, 0); if (res == (void *) MAP_FAILED) return 0; @@ -1366,37 +1356,11 @@ os_unreserve_physical(char *ptr, UWord size) } static void * -os_mmap_virtual(char *ptr, UWord size, int exec) +os_mmap_virtual(char *ptr, UWord size) { int flags = ERTS_MMAP_VIRTUAL_FLAGS; void* res; -#ifdef ERTS_ALC_A_EXEC - if (exec) { - ASSERT(!ptr); - /* OTP-19.0: Nice hack below cut-and-pasted from hipe_amd64.c */ - -# ifdef MAP_32BIT - /* If we got MAP_32BIT (Linux), then use that to ask for low memory */ - flags |= MAP_32BIT; -# else - /* FreeBSD doesn't have MAP_32BIT, and it doesn't respect - a plain map_hint (returns high mappings even though the - hint refers to a free area), so we have to use both map_hint - and MAP_FIXED to get addresses below the 2GB boundary. - This is even worse than the Linux/ppc64 case. - Similarly, Solaris 10 doesn't have MAP_32BIT, - and it doesn't respect a plain map_hint. */ - ptr = (char*)(512*1024*1024); /* 0.5GB */ - -# if defined(__FreeBSD__) || defined(__sun__) - flags |= MAP_FIXED; -# endif -# endif /* !MAP_32BIT */ - } -#else /* !ERTS_ALC_A_EXEC */ - ASSERT(!exec); -#endif res = mmap((void *) ptr, (size_t) size, ERTS_MMAP_VIRTUAL_PROT, flags, ERTS_MMAP_FD, 0); if (res == (void *) MAP_FAILED) @@ -1411,7 +1375,7 @@ os_mmap_virtual(char *ptr, UWord size, int exec) #endif /* ERTS_HAVE_OS_MMAP */ -static int reserve_noop(char *ptr, UWord size, int exec) +static int reserve_noop(char *ptr, UWord size) { #ifdef ERTS_MMAP_DEBUG_FILL_AREAS Uint32 *uip, *end = (Uint32 *) (ptr + size); @@ -1454,7 +1418,7 @@ alloc_desc_insert_free_seg(ErtsMemMapper* mm, #if ERTS_HAVE_OS_MMAP if (!mm->no_os_mmap) { - ptr = os_mmap(mm->desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0, 0); + ptr = os_mmap(mm->desc.new_area_hint, ERTS_PAGEALIGNED_SIZE, 0); if (ptr) { mm->desc.new_area_hint = ptr+ERTS_PAGEALIGNED_SIZE; ERTS_MMAP_SIZE_OS_INC(ERTS_PAGEALIGNED_SIZE); @@ -1473,7 +1437,7 @@ alloc_desc_insert_free_seg(ErtsMemMapper* mm, da_map = &mm->sua.map; desc = lookup_free_seg(da_map, ERTS_PAGEALIGNED_SIZE); if (desc) { - if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE, 0)) + if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE)) ERTS_MMAP_SIZE_SC_SUA_INC(ERTS_PAGEALIGNED_SIZE); else desc = NULL; @@ -1483,7 +1447,7 @@ alloc_desc_insert_free_seg(ErtsMemMapper* mm, da_map = &mm->sa.map; desc = lookup_free_seg(da_map, ERTS_PAGEALIGNED_SIZE); if (desc) { - if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE, 0)) + if (mm->reserve_physical(desc->start, ERTS_PAGEALIGNED_SIZE)) ERTS_MMAP_SIZE_SC_SA_INC(ERTS_PAGEALIGNED_SIZE); else desc = NULL; @@ -1544,7 +1508,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep) if (desc) { seg = desc->start; end = seg+asize; - if (!mm->reserve_physical(seg, asize, mm->executable)) + if (!mm->reserve_physical(seg, asize)) goto supercarrier_reserve_failure; if (desc->end == end) { delete_free_seg(&mm->sua.map, desc); @@ -1559,8 +1523,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep) } if (asize <= mm->sua.bot - mm->sa.top) { - if (!mm->reserve_physical(mm->sua.bot - asize, asize, - mm->executable)) + if (!mm->reserve_physical(mm->sua.bot - asize, asize)) goto supercarrier_reserve_failure; mm->sua.bot -= asize; seg = mm->sua.bot; @@ -1576,8 +1539,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep) char *start = seg = desc->start; seg = (char *) ERTS_SUPERALIGNED_CEILING(seg); end = seg+asize; - if (!mm->reserve_physical(start, (UWord) (end - start), - mm->executable)) + if (!mm->reserve_physical(start, (UWord) (end - start))) goto supercarrier_reserve_failure; ERTS_MMAP_SIZE_SC_SA_INC(asize); if (desc->end == end) { @@ -1609,8 +1571,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep) if (asize + (seg - start) <= mm->sua.bot - start) { end = seg + asize; - if (!mm->reserve_physical(start, (UWord) (end - start), - mm->executable)) + if (!mm->reserve_physical(start, (UWord) (end - start))) goto supercarrier_reserve_failure; mm->sa.top = end; ERTS_MMAP_SIZE_SC_SA_INC(asize); @@ -1632,8 +1593,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep) seg = (char *) ERTS_SUPERALIGNED_CEILING(org_start); end = seg + asize; - if (!mm->reserve_physical(seg, (UWord) (org_end - seg), - mm->executable)) + if (!mm->reserve_physical(seg, (UWord) (org_end - seg))) goto supercarrier_reserve_failure; ERTS_MMAP_SIZE_SC_SUA_INC(asize); if (org_start != seg) { @@ -1666,13 +1626,13 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep) /* Map using OS primitives */ if (!(ERTS_MMAPFLG_SUPERCARRIER_ONLY & flags) && !mm->no_os_mmap) { if (!(ERTS_MMAPFLG_SUPERALIGNED & flags)) { - seg = os_mmap(NULL, asize, 0, mm->executable); + seg = os_mmap(NULL, asize, 0); if (!seg) goto failure; } else { asize = ERTS_SUPERALIGNED_CEILING(*sizep); - seg = os_mmap(NULL, asize, 1, mm->executable); + seg = os_mmap(NULL, asize, 1); if (!seg) goto failure; @@ -1682,8 +1642,7 @@ erts_mmap(ErtsMemMapper* mm, Uint32 flags, UWord *sizep) os_munmap(seg, asize); - ptr = os_mmap(NULL, asize + ERTS_SUPERALIGNED_SIZE, 1, - mm->executable); + ptr = os_mmap(NULL, asize + ERTS_SUPERALIGNED_SIZE, 1); if (!ptr) goto failure; @@ -2018,8 +1977,7 @@ erts_mremap(ErtsMemMapper* mm, if (next && new_end <= next->end) { if (!mm->reserve_physical(((char *) ptr) + old_size, - asize - old_size, - mm->executable)) + asize - old_size)) goto supercarrier_reserve_failure; if (new_end < next->end) resize_free_seg(&mm->sua.map, next, new_end, next->end); @@ -2037,8 +1995,7 @@ erts_mremap(ErtsMemMapper* mm, if (end == mm->sa.top) { if (new_end <= mm->sua.bot) { if (!mm->reserve_physical(((char *) ptr) + old_size, - asize - old_size, - mm->executable)) + asize - old_size)) goto supercarrier_reserve_failure; mm->sa.top = new_end; new_ptr = ptr; @@ -2050,8 +2007,7 @@ erts_mremap(ErtsMemMapper* mm, adjacent_free_seg(&mm->sa.map, start, end, &prev, &next); if (next && new_end <= next->end) { if (!mm->reserve_physical(((char *) ptr) + old_size, - asize - old_size, - mm->executable)) + asize - old_size)) goto supercarrier_reserve_failure; if (new_end < next->end) resize_free_seg(&mm->sa.map, next, new_end, next->end); @@ -2171,7 +2127,7 @@ static void hard_dbg_mseg_init(void); #endif void -erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable) +erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init) { static int is_first_call = 1; int virtual_map = 0; @@ -2203,7 +2159,6 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable) mm->supercarrier = 0; mm->reserve_physical = reserve_noop; mm->unreserve_physical = unreserve_noop; - mm->executable = executable; #if HAVE_MMAP && !defined(MAP_ANON) mm->mmap_fd = open("/dev/zero", O_RDWR); @@ -2225,7 +2180,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable) ptr = (char *) ERTS_PAGEALIGNED_CEILING(init->virtual_range.start); end = (char *) ERTS_PAGEALIGNED_FLOOR(init->virtual_range.end); sz = end - ptr; - start = os_mmap_virtual(ptr, sz, executable); + start = os_mmap_virtual(ptr, sz); if (!start || start > ptr || start >= end) erts_exit(1, "erts_mmap: Failed to create virtual range for super carrier\n"); @@ -2250,7 +2205,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable) sz = ERTS_PAGEALIGNED_CEILING(init->scs); #ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION if (!init->scrpm) { - start = os_mmap_virtual(NULL, sz, executable); + start = os_mmap_virtual(NULL, sz); mm->reserve_physical = os_reserve_physical; mm->unreserve_physical = os_unreserve_physical; virtual_map = 1; @@ -2262,7 +2217,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable) * The whole supercarrier will by physically * reserved all the time. */ - start = os_mmap(NULL, sz, 1, executable); + start = os_mmap(NULL, sz, 1); } if (!start) erts_exit(1, @@ -2336,7 +2291,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable) mm->sua.top -= ERTS_PAGEALIGNED_SIZE; mm->size.supercarrier.used.total += ERTS_PAGEALIGNED_SIZE; #ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION - if (!virtual_map || os_reserve_physical(mm->sua.top, ERTS_PAGEALIGNED_SIZE, 0)) + if (!virtual_map || os_reserve_physical(mm->sua.top, ERTS_PAGEALIGNED_SIZE)) #endif add_free_desc_area(mm, mm->sua.top, end); mm->desc.reserved += (end - mm->sua.top) / sizeof(ErtsFreeSegDesc); @@ -2349,7 +2304,7 @@ erts_mmap_init(ErtsMemMapper* mm, ErtsMMapInit *init, int executable) * will be used for free segment descritors. */ #ifdef ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION - if (virtual_map && !os_reserve_physical(start, mm->sa.bot - start, 0)) + if (virtual_map && !os_reserve_physical(start, mm->sa.bot - start)) erts_exit(1, "erts_mmap: Failed to reserve physical memory for descriptors\n"); #endif mm->desc.unused_start = start; diff --git a/erts/emulator/sys/common/erl_mmap.h b/erts/emulator/sys/common/erl_mmap.h index 2a07d93c8c..c1f9668b18 100644 --- a/erts/emulator/sys/common/erl_mmap.h +++ b/erts/emulator/sys/common/erl_mmap.h @@ -93,11 +93,6 @@ typedef struct { #define ERTS_MMAP_INIT_LITERAL_INITER \ {{NULL, NULL}, {NULL, NULL}, ERTS_LITERAL_VIRTUAL_AREA_SIZE, 1, (1 << 10), 0} -#define ERTS_HIPE_EXEC_VIRTUAL_AREA_SIZE (UWORD_CONSTANT(512)*1024*1024) - -#define ERTS_MMAP_INIT_HIPE_EXEC_INITER \ - {{NULL, NULL}, {NULL, NULL}, ERTS_HIPE_EXEC_VIRTUAL_AREA_SIZE, 1, (1 << 10), 0} - #define ERTS_SUPERALIGNED_SIZE \ (1 << ERTS_MMAP_SUPERALIGNED_BITS) @@ -140,7 +135,7 @@ void *erts_mmap(ErtsMemMapper*, Uint32 flags, UWord *sizep); void erts_munmap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord size); void *erts_mremap(ErtsMemMapper*, Uint32 flags, void *ptr, UWord old_size, UWord *sizep); int erts_mmap_in_supercarrier(ErtsMemMapper*, void *ptr); -void erts_mmap_init(ErtsMemMapper*, ErtsMMapInit*, int executable); +void erts_mmap_init(ErtsMemMapper*, ErtsMMapInit*); struct erts_mmap_info_struct { UWord sizes[6]; @@ -165,15 +160,6 @@ extern ErtsMemMapper erts_dflt_mmapper; extern ErtsMemMapper erts_literal_mmapper; # endif -# if defined(ERTS_ALC_A_EXEC) && defined(__x86_64__) - /* - * On x86_64, exec_alloc employs its own super carrier 'erts_exec_mmaper' - * to ensure low memory for HiPE AMD64 small code model. - */ -# define ERTS_HAVE_EXEC_MMAPPER -extern ErtsMemMapper erts_exec_mmapper; -# endif - # endif /* ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION */ #endif /* ERTS_WANT_MEM_MAPPERS */ diff --git a/erts/emulator/sys/common/erl_mseg.c b/erts/emulator/sys/common/erl_mseg.c index bf6de9b13a..ced3d61525 100644 --- a/erts/emulator/sys/common/erl_mseg.c +++ b/erts/emulator/sys/common/erl_mseg.c @@ -1406,15 +1406,9 @@ erts_mseg_init(ErtsMsegInit_t *init) erts_mtx_init(&init_atoms_mutex, "mseg_init_atoms", NIL, ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC); -#ifdef ERTS_HAVE_EXEC_MMAPPER - /* Initialize erts_exec_mapper *FIRST*, to increase probability - * of getting low memory for HiPE AMD64's small code model. - */ - erts_mmap_init(&erts_exec_mmapper, &init->exec_mmap, 1); -#endif - erts_mmap_init(&erts_dflt_mmapper, &init->dflt_mmap, 0); + erts_mmap_init(&erts_dflt_mmapper, &init->dflt_mmap); #if defined(ARCH_64) && defined(ERTS_HAVE_OS_PHYSICAL_MEMORY_RESERVATION) - erts_mmap_init(&erts_literal_mmapper, &init->literal_mmap, 0); + erts_mmap_init(&erts_literal_mmapper, &init->literal_mmap); #endif if (!IS_2POW(GET_PAGE_SIZE)) diff --git a/erts/emulator/sys/common/erl_mseg.h b/erts/emulator/sys/common/erl_mseg.h index bba0dec499..af275c18be 100644 --- a/erts/emulator/sys/common/erl_mseg.h +++ b/erts/emulator/sys/common/erl_mseg.h @@ -61,7 +61,6 @@ typedef struct { Uint nos; ErtsMMapInit dflt_mmap; ErtsMMapInit literal_mmap; - ErtsMMapInit exec_mmap; } ErtsMsegInit_t; #define ERTS_MSEG_INIT_DEFAULT_INITIALIZER \ @@ -72,7 +71,6 @@ typedef struct { 1000, /* cci: Cache check interval */ \ ERTS_MMAP_INIT_DEFAULT_INITER, \ ERTS_MMAP_INIT_LITERAL_INITER, \ - ERTS_MMAP_INIT_HIPE_EXEC_INITER \ } typedef struct { diff --git a/erts/emulator/sys/unix/erl_child_setup.c b/erts/emulator/sys/unix/erl_child_setup.c index 57973b10d7..10601529a4 100644 --- a/erts/emulator/sys/unix/erl_child_setup.c +++ b/erts/emulator/sys/unix/erl_child_setup.c @@ -56,6 +56,8 @@ #include <stdio.h> #include <stdarg.h> #include <sys/wait.h> +#include <sys/types.h> +#include <sys/socket.h> #define WANT_NONBLOCKING diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c index b7ac89d89a..117855acf0 100644 --- a/erts/emulator/sys/unix/sys_drivers.c +++ b/erts/emulator/sys/unix/sys_drivers.c @@ -50,6 +50,9 @@ #include <sys/ioctl.h> #endif +#include <sys/types.h> +#include <sys/socket.h> + #define WANT_NONBLOCKING /* must define this to pull in defs from sys.h */ #include "sys.h" diff --git a/erts/emulator/sys/unix/sys_uds.c b/erts/emulator/sys/unix/sys_uds.c index dd0a3b03ff..278c6b6ba1 100644 --- a/erts/emulator/sys/unix/sys_uds.c +++ b/erts/emulator/sys/unix/sys_uds.c @@ -18,6 +18,42 @@ * %CopyrightEnd% */ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#if defined(__sun__) && !defined(_XOPEN_SOURCE) +#define _XOPEN_SOURCE 500 +#endif + +#include <limits.h> + +#include <sys/types.h> +#include <sys/socket.h> + +#ifdef HAVE_SYS_SOCKETIO_H +# include <sys/socketio.h> +#endif +#ifdef HAVE_SYS_SOCKIO_H +# include <sys/sockio.h> +#endif + +#ifdef HAVE_NET_ERRNO_H +#include <net/errno.h> +#endif + +#ifdef HAVE_DIRENT_H +# include <dirent.h> +#endif + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <errno.h> + #include "sys_uds.h" int diff --git a/erts/emulator/sys/unix/sys_uds.h b/erts/emulator/sys/unix/sys_uds.h index a598102d5c..26c91d6a00 100644 --- a/erts/emulator/sys/unix/sys_uds.h +++ b/erts/emulator/sys/unix/sys_uds.h @@ -21,18 +21,6 @@ #ifndef _ERL_UNIX_UDS_H #define _ERL_UNIX_UDS_H -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#if defined(__sun__) && !defined(_XOPEN_SOURCE) -#define _XOPEN_SOURCE 500 -#endif - -#include <limits.h> - -#include <sys/types.h> -#include <sys/socket.h> #include <sys/uio.h> #if defined IOV_MAX @@ -43,8 +31,6 @@ #define MAXIOV 16 #endif -#include "sys.h" - int sys_uds_readv(int fd, struct iovec *iov, size_t iov_len, int *fds, int fd_count, int flags); int sys_uds_read(int fd, char *buff, size_t len, diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index e1b42e5d85..22706ae8b1 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -33,7 +33,9 @@ atom_to_binary/1,min_max/1, erlang_halt/1, erl_crash_dump_bytes/1, is_builtin/1, error_stacktrace/1, - error_stacktrace_during_call_trace/1]). + error_stacktrace_during_call_trace/1, + group_leader_prio/1, group_leader_prio_dirty/1, + is_process_alive/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -46,7 +48,9 @@ all() -> display, display_string, list_to_utf8_atom, atom_to_binary, binary_to_atom, binary_to_existing_atom, erl_crash_dump_bytes, min_max, erlang_halt, is_builtin, - error_stacktrace, error_stacktrace_during_call_trace]. + error_stacktrace, error_stacktrace_during_call_trace, + group_leader_prio, group_leader_prio_dirty, + is_process_alive]. %% Uses erlang:display to test that erts_printf does not do deep recursion display(Config) when is_list(Config) -> @@ -825,7 +829,6 @@ error_stacktrace_during_call_trace(Config) when is_list(Config) -> end end, ok. - error_stacktrace_test() -> Types = [apply_const_last, apply_const, apply_last, @@ -963,9 +966,140 @@ do_error_1(call) -> erlang:error(id(oops)). +group_leader_prio(Config) when is_list(Config) -> + group_leader_prio_test(false). + +group_leader_prio_dirty(Config) when is_list(Config) -> + group_leader_prio_test(true). + +group_leader_prio_test(Dirty) -> + %% + %% Unfortunately back in the days node local group_leader/2 was not + %% implemented as sending an asynchronous signal to the process to change + %% group leader for. Instead it has always been synchronously changed, and + %% nothing in the documentation have hinted otherwise... Therefore I do not + %% dare the change this. + %% + %% In order to prevent priority inversion, the priority of the receiver of + %% the group leader signal is elevated while handling incoming signals if + %% the sender has a higher priority than the receiver. This test tests that + %% the priority elevation actually works... + %% + Tester = self(), + Init = erlang:whereis(init), + GL = erlang:group_leader(), + process_flag(priority, max), + {TestProcFun, NTestProcs} + = case Dirty of + false -> + %% These processes will handle all incoming signals + %% by them selves... + {fun () -> + Tester ! {alive, self()}, + receive after infinity -> ok end + end, + 100}; + true -> + %% These processes wont handle incoming signals by + %% them selves since they are stuck on dirty schedulers + %% when we try to change group leader. A dirty process + %% signal handler process (system process) will be notified + %% of the need to handle incoming signals for these processes, + %% and will instead handle the signal for these processes... + {fun () -> + %% The following sends the message '{alive, self()}' + %% to Tester once on a dirty io scheduler, then wait + %% there until the process terminates... + erts_debug:dirty_io(alive_waitexiting, Tester) + end, + erlang:system_info(dirty_io_schedulers)} + end, + TPs = lists:map(fun (_) -> + spawn_opt(TestProcFun, + [link, {priority, normal}]) + end, lists:seq(1, NTestProcs)), + lists:foreach(fun (TP) -> receive {alive, TP} -> ok end end, TPs), + TLs = lists:map(fun (_) -> + spawn_opt(fun () -> tok_loop() end, + [link, {priority, high}]) + end, + lists:seq(1, 2*erlang:system_info(schedulers))), + %% Wait to ensure distribution of high prio processes over schedulers... + receive after 1000 -> ok end, + %% + %% Test that we can get group-leader signals through to normal prio + %% processes from a max prio process even though all schedulers are filled + %% with executing high prio processes. + %% + lists:foreach(fun (_) -> + lists:foreach(fun (TP) -> + erlang:yield(), + %% whitebox -- Enqueue some signals on it + %% preventing us from hogging its main lock + %% and set group-leader directly.... + erlang:demonitor(erlang:monitor(process, TP)), + true = erlang:group_leader(Init, TP), + {group_leader, Init} = process_info(TP, group_leader), + erlang:demonitor(erlang:monitor(process, TP)), + true = erlang:group_leader(GL, TP), + {group_leader, GL} = process_info(TP, group_leader) + end, + TPs) + end, + lists:seq(1,100)), + %% + %% Also test when it is exiting... + %% + lists:foreach(fun (TP) -> + erlang:yield(), + M = erlang:monitor(process, TP), + unlink(TP), + exit(TP, bang), + badarg = try + true = erlang:group_leader(Init, TP) + catch + error : What -> What + end, + receive + {'DOWN', M, process, TP, Reason} -> + bang = Reason + end + end, + TPs), + lists:foreach(fun (TL) -> + M = erlang:monitor(process, TL), + unlink(TL), + exit(TL, bang), + receive + {'DOWN', M, process, TL, Reason} -> + bang = Reason + end + end, + TLs), + ok. +is_process_alive(Config) when is_list(Config) -> + process_flag(priority, max), + Ps = lists:map(fun (_) -> + spawn_opt(fun () -> tok_loop() end, + [{priority, high}, link]) + end, + lists:seq(1, 2*erlang:system_info(schedulers))), + receive after 1000 -> ok end, %% Wait for load to spread + lists:foreach(fun (P) -> + %% Ensure that signal order is preserved + %% and that we are not starved due to + %% priority inversion + true = erlang:is_process_alive(P), + unlink(P), + true = erlang:is_process_alive(P), + exit(P, kill), + false = erlang:is_process_alive(P) + end, + Ps), + ok. -%% Helpers +%% helpers id(I) -> I. @@ -1005,3 +1139,11 @@ hostname([$@ | Hostname]) -> list_to_atom(Hostname); hostname([_C | Cs]) -> hostname(Cs). + +tok_loop() -> + tok_loop(hej). + +tok_loop(hej) -> + tok_loop(hopp); +tok_loop(hopp) -> + tok_loop(hej). 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 2a8b999307..a94a2c0b02 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 @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2009-2017. All Rights Reserved. + * Copyright Ericsson AB 2009-2018. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -112,15 +112,12 @@ static ERL_NIF_TERM send_from_dirty_nif(ErlNifEnv* env, int argc, const ERL_NIF_ { ERL_NIF_TERM result; ErlNifPid pid; - ErlNifEnv* menv; int res; if (!enif_get_local_pid(env, argv[0], &pid)) return enif_make_badarg(env); result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid)); - menv = enif_alloc_env(); - res = enif_send(env, &pid, menv, result); - enif_free_env(menv); + res = enif_send(env, &pid, NULL, result); if (!res) return enif_make_badarg(env); else @@ -131,15 +128,12 @@ static ERL_NIF_TERM send_wait_from_dirty_nif(ErlNifEnv* env, int argc, const ERL { ERL_NIF_TERM result; ErlNifPid pid; - ErlNifEnv* menv; int res; if (!enif_get_local_pid(env, argv[0], &pid)) return enif_make_badarg(env); result = enif_make_tuple2(env, enif_make_atom(env, "ok"), enif_make_pid(env, &pid)); - menv = enif_alloc_env(); - res = enif_send(env, &pid, menv, result); - enif_free_env(menv); + res = enif_send(env, &pid, NULL, result); #ifdef __WIN32__ Sleep(2000); @@ -211,10 +205,8 @@ dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) /* 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' message, and then after the sleep, send a 'done' message. */ - if (argc == 1 && enif_get_local_pid(env, argv[0], &pid)) { - msg_env = enif_alloc_env(); - enif_send(env, &pid, msg_env, enif_make_atom(msg_env, "ready")); - } + if (argc == 1 && enif_get_local_pid(env, argv[0], &pid)) + enif_send(env, &pid, NULL, enif_make_atom(env, "ready")); #ifdef __WIN32__ Sleep(2000); @@ -222,11 +214,8 @@ dirty_sleeper(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) sleep(2); #endif - if (argc == 1) { - assert(msg_env != NULL); - enif_send(env, &pid, msg_env, enif_make_atom(msg_env, "done")); - enif_free_env(msg_env); - } + if (argc == 1) + enif_send(env, &pid, NULL, enif_make_atom(env, "done")); return enif_make_atom(env, "ok"); } @@ -247,8 +236,8 @@ static ERL_NIF_TERM dirty_call_while_terminated_nif(ErlNifEnv* env, int argc, co self_term = enif_make_pid(env, &self); - result = enif_make_tuple2(env, enif_make_atom(env, "dirty_alive"), self_term); menv = enif_alloc_env(); + result = enif_make_tuple2(menv, enif_make_atom(menv, "dirty_alive"), self_term); res = enif_send(env, &to, menv, result); enif_free_env(menv); if (!res) @@ -259,9 +248,7 @@ static ERL_NIF_TERM dirty_call_while_terminated_nif(ErlNifEnv* env, int argc, co ; result = enif_make_tuple2(env, enif_make_atom(env, "dirty_dead"), self_term); - menv = enif_alloc_env(); - res = enif_send(env, &to, menv, result); - enif_free_env(menv); + res = enif_send(env, &to, NULL, result); #ifdef __WIN32__ Sleep(1000); diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl index 294c42780d..40c7cc11e1 100644 --- a/erts/emulator/test/driver_SUITE.erl +++ b/erts/emulator/test/driver_SUITE.erl @@ -2589,8 +2589,18 @@ stop_driver(Port, Name) -> ok = erl_ddll:stop(). load_driver(Dir, Driver) -> + Before = erlang:system_info(taints), case erl_ddll:load_driver(Dir, Driver) of - ok -> ok; + ok -> + After = erlang:system_info(taints), + case lists:member(Driver, Before) of + true -> + After = Before; + false -> + true = lists:member(Driver, After), + Before = lists:delete(Driver, After) + end, + ok; {error, Error} = Res -> io:format("~s\n", [erl_ddll:format_error(Error)]), Res diff --git a/erts/emulator/test/erl_link_SUITE.erl b/erts/emulator/test/erl_link_SUITE.erl index a66ca7a57d..ed444f2599 100644 --- a/erts/emulator/test/erl_link_SUITE.erl +++ b/erts/emulator/test/erl_link_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2017. All Rights Reserved. +%% Copyright Ericsson AB 2001-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -53,26 +53,17 @@ -export([test_proc/0]). --define(LINK_UNDEF, 0). --define(LINK_PID, 1). --define(LINK_NODE, 3). - - -% These are to be kept in sync with erl_monitors.h --define(MON_ORIGIN, 1). --define(MON_TARGET, 2). - - --record(erl_link, {type = ?LINK_UNDEF, +-record(erl_link, {type, % process | port | dist_process pid = [], - targets = []}). + id}). % This is to be kept in sync with erl_bif_info.c (make_monitor_list) --record(erl_monitor, {type, % MON_ORIGIN or MON_TARGET - ref, +-record(erl_monitor, {type, % process | port | time_offset | dist_process | resource | node | nodes | suspend + dir, % origin | target + ref, % Reference | [] pid, % Process or nodename - name = []}). % registered name or [] + extra = []}). % registered name, integer or, [] suite() -> @@ -106,7 +97,7 @@ end_per_suite(_Config) -> links(Config) when is_list(Config) -> common_link_test(node(), node()), true = link(self()), - [] = find_erl_link(self(), ?LINK_PID, self()), + [] = find_erl_link(self(), process, self()), true = unlink(self()), ok. @@ -191,6 +182,7 @@ monitor_nodes(Config) when is_list(Config) -> monitor_node(A, false), monitor_node(B, true), monitor_node(C, true), + receive after 1000 -> ok end, monitor_node(C, false), monitor_node(C, true), monitor_node(B, true), @@ -304,7 +296,8 @@ run_common_process_monitors(TP1, TP2) -> wait_until(fun () -> is_proc_dead(TP2) end), ok = tp_call(TP1, fun () -> receive - {'DOWN',R2,process,TP2O,bye} -> + {'DOWN',R2,process,TP2O,Reason1} -> + bye = Reason1, ok end end), @@ -313,7 +306,8 @@ run_common_process_monitors(TP1, TP2) -> R3 = tp_call(TP1, fun () -> erlang:monitor(process, TP2) end), ok = tp_call(TP1, fun () -> receive - {'DOWN',R3,process,TP2O,noproc} -> + {'DOWN',R3,process,TP2O,Reason2} -> + noproc = Reason2, ok end end), @@ -698,22 +692,10 @@ test_proc() -> end, test_proc(). -expand_link_list([#erl_link{type = ?LINK_NODE, targets = N} = Rec | T]) -> - lists:duplicate(N,Rec#erl_link{targets = []}) ++ expand_link_list(T); -expand_link_list([#erl_link{targets = [#erl_link{pid = Pid}]} = Rec | T]) -> - [Rec#erl_link{targets = [Pid]} | expand_link_list(T)]; -expand_link_list([#erl_link{targets = [#erl_link{pid = Pid}|TT]} = Rec | T]) -> - [ Rec#erl_link{targets = [Pid]} | expand_link_list( - [Rec#erl_link{targets = TT} | T])]; -expand_link_list([#erl_link{targets = []} = Rec | T]) -> - [Rec | expand_link_list(T)]; -expand_link_list([]) -> - []. - get_local_link_list(Obj) -> case catch erts_debug:get_internal_state({link_list, Obj}) of LL when is_list(LL) -> - expand_link_list(LL); + LL; _ -> [] end. @@ -722,7 +704,7 @@ get_remote_link_list(Node, Obj) -> case catch rpc:call(Node, erts_debug, get_internal_state, [{link_list, Obj}]) of LL when is_list(LL) -> - expand_link_list(LL); + LL; _ -> [] end. @@ -776,82 +758,106 @@ get_monitor_list(undefined) -> find_erl_monitor(Pid, Ref) when is_reference(Ref) -> + MonitorList = get_monitor_list(Pid), + io:format("~p MonitorList: ~p~n", [Pid, MonitorList]), lists:foldl(fun (#erl_monitor{ref = R} = EL, Acc) when R == Ref -> [EL|Acc]; (_, Acc) -> Acc end, [], - get_monitor_list(Pid)). - -% find_erl_link(Obj, Ref) when is_reference(Ref) -> -% lists:foldl(fun (#erl_link{ref = R} = EL, Acc) when R == Ref -> -% [EL|Acc]; -% (_, Acc) -> -% Acc -% end, -% [], -% get_link_list(Obj)). - -find_erl_link(Obj, Type, [Item, Data]) when is_pid(Item); - is_port(Item); - is_atom(Item) -> - lists:foldl(fun (#erl_link{type = T, pid = I, targets = D} = EL, + MonitorList); +find_erl_monitor(Pid, Item) -> + MonitorList = get_monitor_list(Pid), + io:format("~p MonitorList: ~p~n", [Pid, MonitorList]), + lists:foldl(fun (#erl_monitor{pid = I} = EL, Acc) when I == Item -> + [EL|Acc]; + (_, Acc) -> + Acc + end, + [], + MonitorList). + + +find_erl_link(Obj, Type, Item) when is_pid(Item); is_port(Item) -> + LinkList = get_link_list(Obj), + io:format("~p LinkList: ~p~n", [Obj, LinkList]), + lists:foldl(fun (#erl_link{type = T, pid = I} = EL, Acc) when T == Type, I == Item -> - case Data of - D -> - [EL|Acc]; - [] -> - [EL|Acc]; - _ -> - Acc - end; + [EL|Acc]; + (_, Acc) -> + Acc + end, + [], + LinkList); +find_erl_link(Obj, Type, Id) when is_integer(Id) -> + %% Find by Id + LinkList = get_link_list(Obj), + io:format("~p LinkList: ~p~n", [Obj, LinkList]), + lists:foldl(fun (#erl_link{type = T, id = I} = EL, + Acc) when T == Type, I == Id -> + [EL|Acc]; (_, Acc) -> Acc end, [], - get_link_list(Obj)); -find_erl_link(Obj, Type, Item) when is_pid(Item); is_port(Item); is_atom(Item) -> - find_erl_link(Obj, Type, [Item, []]). + LinkList). +get_link_type(A, B) when is_port(A); + is_port(B) -> + port; +get_link_type(A, B) when is_pid(A), + is_pid(B) -> + case node(A) == node(B) of + true -> + process; + false -> + dist_process + end. +check_link(A, B) when node(A) == node(B) -> + LinkType = get_link_type(A, B), + [#erl_link{type = LinkType, + pid = B, + id = Id}] = find_erl_link(A, LinkType, B), + [#erl_link{type = LinkType, + pid = A, + id = Id}] = find_erl_link(B, LinkType, A), + [] = find_erl_link({node(A), node(B)}, + LinkType, + A), + [] = find_erl_link({node(B), node(A)}, + LinkType, + B), + ok; check_link(A, B) -> - [#erl_link{type = ?LINK_PID, + [#erl_link{type = dist_process, pid = B, - targets = []}] = find_erl_link(A, ?LINK_PID, B), - [#erl_link{type = ?LINK_PID, + id = IdA}] = find_erl_link(A, dist_process, B), + [#erl_link{type = dist_process, pid = A, - targets = []}] = find_erl_link(B, ?LINK_PID, A), - case node(A) == node(B) of - false -> - [#erl_link{type = ?LINK_PID, - pid = A, - targets = [B]}] = find_erl_link({node(A), - node(B)}, - ?LINK_PID, - [A, [B]]), - [#erl_link{type = ?LINK_PID, - pid = B, - targets = [A]}] = find_erl_link({node(B), - node(A)}, - ?LINK_PID, - [B, [A]]); - true -> - [] = find_erl_link({node(A), node(B)}, - ?LINK_PID, - [A, [B]]), - [] = find_erl_link({node(B), node(A)}, - ?LINK_PID, - [B, [A]]) - end, + id = IdA}] = find_erl_link({node(A), + node(B)}, + dist_process, + IdA), + [#erl_link{type = dist_process, + pid = A, + id = IdB}] = find_erl_link(B, dist_process, A), + [#erl_link{type = dist_process, + pid = B, + id = IdB}] = find_erl_link({node(B), + node(A)}, + dist_process, + IdB), ok. check_unlink(A, B) -> - [] = find_erl_link(A, ?LINK_PID, B), - [] = find_erl_link(B, ?LINK_PID, A), - [] = find_erl_link({node(A), node(B)}, ?LINK_PID, [A, [B]]), - [] = find_erl_link({node(B), node(A)}, ?LINK_PID, [B, [A]]), + LinkType = get_link_type(A, B), + [] = find_erl_link(A, LinkType, B), + [] = find_erl_link(B, LinkType, A), + [] = find_erl_link({node(A), node(B)}, dist_process, A), + [] = find_erl_link({node(B), node(A)}, dist_process, B), ok. check_process_monitor(From, {Name, Node}, Ref) when is_pid(From), @@ -864,22 +870,26 @@ check_process_monitor(From, {Name, Node}, Ref) when is_pid(From), is_atom(Node), is_reference(Ref) -> MonitoredPid = rpc:call(Node, erlang, whereis, [Name]), - [#erl_monitor{type = ?MON_ORIGIN, + [#erl_monitor{type = dist_process, + dir = origin, ref = Ref, pid = Node, - name = Name}] = find_erl_monitor(From, Ref), - [#erl_monitor{type = ?MON_TARGET, + extra = Name}] = find_erl_monitor(From, Ref), + [#erl_monitor{type = dist_process, + dir = target, ref = Ref, pid = From, - name = Name}] = find_erl_monitor({node(From), Node}, Ref), - [#erl_monitor{type = ?MON_ORIGIN, + extra = Name}] = find_erl_monitor({node(From), Node}, Ref), + [#erl_monitor{type = dist_process, + dir = origin, ref = Ref, pid = MonitoredPid, - name = Name}] = find_erl_monitor({Node, node(From)}, Ref), - [#erl_monitor{type = ?MON_TARGET, + extra = Name}] = find_erl_monitor({Node, node(From)}, Ref), + [#erl_monitor{type = dist_process, + dir = target, ref = Ref, pid = From, - name = Name}] = find_erl_monitor(MonitoredPid, Ref), + extra = Name}] = find_erl_monitor(MonitoredPid, Ref), ok; check_process_monitor(From, Name, Ref) when is_pid(From), is_atom(Name), @@ -887,27 +897,36 @@ check_process_monitor(From, Name, Ref) when is_pid(From), is_reference(Ref) -> MonitoredPid = rpc:call(node(From), erlang, whereis, [Name]), - [#erl_monitor{type = ?MON_ORIGIN, + [#erl_monitor{type = process, + dir = origin, ref = Ref, pid = MonitoredPid, - name = Name}] = find_erl_monitor(From, Ref), + extra = Name}] = find_erl_monitor(From, Ref), - [#erl_monitor{type = ?MON_TARGET, + [#erl_monitor{type = process, + dir = target, ref = Ref, pid = From, - name = Name}] = find_erl_monitor(MonitoredPid,Ref), + extra = Name}] = find_erl_monitor(MonitoredPid,Ref), ok; check_process_monitor(From, To, Ref) when is_pid(From), is_pid(To), is_reference(Ref) -> - OriMon = [#erl_monitor{type = ?MON_ORIGIN, + MonType = case node(From) == node(To) of + true -> process; + false -> dist_process + end, + + OriMon = [#erl_monitor{type = MonType, + dir = origin, ref = Ref, pid = To}], OriMon = find_erl_monitor(From, Ref), - TargMon = [#erl_monitor{type = ?MON_TARGET, + TargMon = [#erl_monitor{type = MonType, + dir = target, ref = Ref, pid = From}], TargMon = find_erl_monitor(To, Ref), @@ -915,7 +934,11 @@ check_process_monitor(From, To, Ref) when is_pid(From), case node(From) == node(To) of false -> - TargMon = find_erl_monitor({node(From), node(To)}, Ref), + DistTargMon = [#erl_monitor{type = dist_process, + dir = target, + ref = Ref, + pid = From}], + DistTargMon = find_erl_monitor({node(From), node(To)}, Ref), OriMon = find_erl_monitor({node(To), node(From)}, Ref); true -> [] = find_erl_monitor({node(From), node(From)}, Ref) @@ -986,19 +1009,36 @@ check_process_demonitor(From, To, Ref) when is_pid(From), ok. no_of_monitor_node(From, Node) when is_pid(From), is_atom(Node) -> - length(find_erl_link(From, ?LINK_NODE, Node)). + case find_erl_monitor(From, Node) of + [] -> 0; + [#erl_monitor{type = node, + dir = origin, + pid = Node, + extra = N}] -> N + end. +check_monitor_node(From, Node, 0) when is_pid(From), + is_atom(Node) -> + [] = find_erl_monitor(From, Node), + [] = find_erl_monitor({node(From), Node}, From); check_monitor_node(From, Node, No) when is_pid(From), is_atom(Node), is_integer(No), - No >= 0 -> - LL = lists:duplicate(No, #erl_link{type = ?LINK_NODE, pid = Node}), - DLL = lists:duplicate(No, #erl_link{type = ?LINK_NODE, pid = From}), - LL = find_erl_link(From, ?LINK_NODE, Node), - DLL = find_erl_link({node(From), Node}, ?LINK_NODE, From), - ok. - - + No > 0 -> + [#erl_monitor{type = node, + dir = origin, + pid = Node, + extra = No}] = find_erl_monitor(From, Node), + [#erl_monitor{type = node, + dir = target, + pid = From}] = find_erl_monitor({node(From), Node}, From). + +connection_id(Node) -> + try + erts_debug:get_internal_state({connection_id, Node}) + catch + _:_ -> -1 + end. hostname() -> from($@, atom_to_list(node())). diff --git a/erts/emulator/test/module_info_SUITE.erl b/erts/emulator/test/module_info_SUITE.erl index ba9b564fdc..46a3bba732 100644 --- a/erts/emulator/test/module_info_SUITE.erl +++ b/erts/emulator/test/module_info_SUITE.erl @@ -23,7 +23,7 @@ -include_lib("common_test/include/ct.hrl"). -export([all/0, suite/0, - exports/1,functions/1,deleted/1,native/1,info/1]). + exports/1,functions/1,deleted/1,native/1,info/1,nifs/1]). %%-compile(native). @@ -38,7 +38,7 @@ all() -> modules(). modules() -> - [exports, functions, deleted, native, info]. + [exports, functions, deleted, native, info, nifs]. %% Should return all functions exported from this module. (local) all_exported() -> @@ -62,12 +62,24 @@ exports(Config) when is_list(Config) -> All = lists:sort(?MODULE:module_info(exports)), ok. -%% Test that the list of exported functions from this module is correct. +%% Test that the list of local and exported functions from this module is +%% correct. functions(Config) when is_list(Config) -> All = all_functions(), All = lists:sort(?MODULE:module_info(functions)), ok. +nifs(Config) when is_list(Config) -> + [] = ?MODULE:module_info(nifs), + + %% erl_tracer is guaranteed to be present and contain these NIFs + TraceNIFs = erl_tracer:module_info(nifs), + true = lists:member({enabled, 3}, TraceNIFs), + true = lists:member({trace, 5}, TraceNIFs), + 2 = length(TraceNIFs), + + ok. + %% Test that deleted modules cause badarg deleted(Config) when is_list(Config) -> Data = proplists:get_value(data_dir, Config), diff --git a/erts/emulator/test/monitor_SUITE.erl b/erts/emulator/test/monitor_SUITE.erl index 9d772480d9..c7250a9d26 100644 --- a/erts/emulator/test/monitor_SUITE.erl +++ b/erts/emulator/test/monitor_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2017. All Rights Reserved. +%% Copyright Ericsson AB 1999-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -86,7 +86,7 @@ case_2(Config) when is_list(Config) -> R = erlang:monitor(process, B), B ! R, receive - {'EXIT', _} -> ok; + true -> ok; Other -> ct:fail({rec, Other}) end, @@ -98,7 +98,7 @@ case_2a(Config) when is_list(Config) -> {B,R} = spawn_monitor(?MODULE, y2, [self()]), B ! R, receive - {'EXIT', _} -> ok; + true -> ok; Other -> ct:fail({rec, Other}) end, @@ -182,7 +182,7 @@ demon_e_1(Config) when is_list(Config) -> end ), receive {P2, ref, R2} -> - demon_error(R2, badarg), + true = erlang:demonitor(R2), P2 ! {self(), stop}; Other2 -> ct:fail({rec, Other2}) @@ -729,8 +729,8 @@ named_down(Config) when is_list(Config) -> end), ?assertEqual(true, register(Name, NamedProc)), unlink(NamedProc), - exit(NamedProc, bang), Mon = erlang:monitor(process, Name), + exit(NamedProc, bang), receive {'DOWN',Mon, _, _, bang} -> ok after 3000 -> ?assert(false) end, ?assertEqual(true, register(Name, self())), diff --git a/erts/emulator/test/nif_SUITE.erl b/erts/emulator/test/nif_SUITE.erl index b6e15ababb..a9eb4b2768 100644 --- a/erts/emulator/test/nif_SUITE.erl +++ b/erts/emulator/test/nif_SUITE.erl @@ -1170,6 +1170,12 @@ maps(Config) when is_list(Config) -> {1, M2} = make_map_remove_nif(M2, "key3"), {0, undefined} = make_map_remove_nif(self(), key), + M1 = maps_from_list_nif(maps:to_list(M1)), + M2 = maps_from_list_nif(maps:to_list(M2)), + M3 = maps_from_list_nif(maps:to_list(M3)), + + has_duplicate_keys = maps_from_list_nif([{1,1},{1,1}]), + verify_tmpmem(TmpMem), ok. diff --git a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c index fa9ae1015c..e8d9302505 100644 --- a/erts/emulator/test/nif_SUITE_data/nif_SUITE.c +++ b/erts/emulator/test/nif_SUITE_data/nif_SUITE.c @@ -2140,24 +2140,45 @@ static ERL_NIF_TERM make_map_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_ /* maps */ static ERL_NIF_TERM maps_from_list_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - ERL_NIF_TERM cell = argv[0]; - ERL_NIF_TERM map = enif_make_new_map(env); - ERL_NIF_TERM tuple; - const ERL_NIF_TERM *pair; - int arity = -1; + ERL_NIF_TERM *keys, *values; + ERL_NIF_TERM result, cell; + unsigned count; - if (argc != 1 && !enif_is_list(env, cell)) return enif_make_badarg(env); + if (argc != 1 || !enif_get_list_length(env, argv[0], &count)) { + return enif_make_badarg(env); + } - /* assume sorted keys */ + keys = enif_alloc(sizeof(ERL_NIF_TERM) * count * 2); + values = keys + count; - while (!enif_is_empty_list(env,cell)) { - if (!enif_get_list_cell(env, cell, &tuple, &cell)) return enif_make_badarg(env); - if (enif_get_tuple(env,tuple,&arity,&pair)) { - enif_make_map_put(env, map, pair[0], pair[1], &map); - } + cell = argv[0]; + count = 0; + + while (!enif_is_empty_list(env, cell)) { + const ERL_NIF_TERM *pair; + ERL_NIF_TERM tuple; + int arity; + + if (!enif_get_list_cell(env, cell, &tuple, &cell) + || !enif_get_tuple(env, tuple, &arity, &pair) + || arity != 2) { + enif_free(keys); + return enif_make_badarg(env); + } + + keys[count] = pair[0]; + values[count] = pair[1]; + + count++; } - return map; + if (!enif_make_map_from_arrays(env, keys, values, count, &result)) { + result = enif_make_atom(env, "has_duplicate_keys"); + } + + enif_free(keys); + + return result; } static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { diff --git a/erts/emulator/test/port_SUITE.erl b/erts/emulator/test/port_SUITE.erl index e46665a881..5b39d05df8 100644 --- a/erts/emulator/test/port_SUITE.erl +++ b/erts/emulator/test/port_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -109,7 +109,6 @@ 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, @@ -180,8 +179,7 @@ all() -> mon_port_bad_named, mon_port_pid_demonitor, mon_port_name_demonitor, - mon_port_driver_die, - mon_port_driver_die_demonitor + mon_port_driver_die ]. groups() -> @@ -2783,7 +2781,7 @@ mon_port_driver_die(Config) -> end, ok. - +-ifdef(DISABLED_TESTCASE). %% 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 @@ -2819,6 +2817,7 @@ mon_port_driver_die_demonitor(Config) -> after 5000 -> ?assert(false) end, ok. +-endif. %% @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 diff --git a/erts/emulator/test/port_trace_SUITE.erl b/erts/emulator/test/port_trace_SUITE.erl index a1986397a8..eba8f194e0 100644 --- a/erts/emulator/test/port_trace_SUITE.erl +++ b/erts/emulator/test/port_trace_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2017. All Rights Reserved. +%% Copyright Ericsson AB 1999-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -202,8 +202,7 @@ ports(_Config) -> erlang:port_close(Prt), [{trace,Prt,closed,normal}, - {trace,Prt,unregister,port_trace_SUITE}, - {trace,Prt,unlink,S}] = flush(), + {trace,Prt,unregister,port_trace_SUITE}] = flush(), ok. @@ -475,8 +474,7 @@ failure_test(Failure, Reason) -> process_flag(trap_exit, false) end, [{trace, Prt, 'receive', {S, {command, Failure}}}, - {trace, Prt, closed, Reason}, - {trace, Prt, unlink, S}] = flush(), + {trace, Prt, closed, Reason}] = flush(), ok. @@ -599,13 +597,11 @@ close(Prt, Flags) -> if Recv, Ports -> [{trace, Prt, 'receive', {S, close}}, - {trace, Prt, closed, normal}, - {trace, Prt, unlink, S}] = flush(); + {trace, Prt, closed, normal}] = flush(); Recv -> [{trace, Prt, 'receive', {S, close}}] = flush(); Ports -> - [{trace, Prt, closed, normal}, - {trace, Prt, unlink, S}] = flush(); + [{trace, Prt, closed, normal}] = flush(); true -> [] = flush() end. diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl index a8bcfac84d..7eff786e8b 100644 --- a/erts/emulator/test/process_SUITE.erl +++ b/erts/emulator/test/process_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -43,7 +43,6 @@ process_info_lock_reschedule3/1, process_info_garbage_collection/1, bump_reductions/1, low_prio/1, binary_owner/1, yield/1, yield2/1, - process_status_exiting/1, otp_4725/1, bad_register/1, garbage_collect/1, otp_6237/1, process_info_messages/1, process_flag_badarg/1, process_flag_heap_size/1, spawn_opt_heap_size/1, spawn_opt_max_heap_size/1, @@ -80,7 +79,6 @@ all() -> process_info_lock_reschedule2, process_info_lock_reschedule3, process_info_garbage_collection, - process_status_exiting, bump_reductions, low_prio, yield, yield2, otp_4725, bad_register, garbage_collect, process_info_messages, process_flag_badarg, process_flag_heap_size, @@ -841,28 +839,6 @@ process_info_lock_reschedule3(Config) when is_list(Config) -> ct:fail(BadStatus) end. -process_status_exiting(Config) when is_list(Config) -> - %% Make sure that erts_debug:get_internal_state({process_status,P}) - %% returns exiting if it is in status P_EXITING. - erts_debug:set_internal_state(available_internal_state,true), - Prio = process_flag(priority, max), - P = spawn_opt(fun () -> receive after infinity -> ok end end, - [{priority, normal}]), - erlang:yield(), - %% The tok_loop processes are here to make it hard for the exiting - %% process to be scheduled in for exit... - TokLoops = lists:map(fun (_) -> - spawn_opt(fun tok_loop/0, - [link,{priority, high}]) - end, lists:seq(1, erlang:system_info(schedulers_online))), - exit(P, boom), - wait_until(fun() -> - exiting =:= erts_debug:get_internal_state({process_status,P}) - end), - lists:foreach(fun (Tok) -> unlink(Tok), exit(Tok,bang) end, TokLoops), - process_flag(priority, Prio), - ok. - otp_4725(Config) when is_list(Config) -> Tester = self(), Ref1 = make_ref(), @@ -1000,7 +976,7 @@ gv(Key,List) -> %% Tests erlang:bump_reductions/1. bump_reductions(Config) when is_list(Config) -> erlang:garbage_collect(), - receive after 1 -> ok end, % Clear reductions. + erlang:yield(), % Clear reductions. {reductions,R1} = process_info(self(), reductions), true = erlang:bump_reductions(100), {reductions,R2} = process_info(self(), reductions), diff --git a/erts/emulator/test/signal_SUITE.erl b/erts/emulator/test/signal_SUITE.erl index 61a8617165..fab2f45f28 100644 --- a/erts/emulator/test/signal_SUITE.erl +++ b/erts/emulator/test/signal_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2017. All Rights Reserved. +%% Copyright Ericsson AB 2006-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -34,23 +34,9 @@ -export([init_per_testcase/2, end_per_testcase/2]). % Test cases --export([xm_sig_order/1, - pending_exit_unlink_process/1, - pending_exit_unlink_dist_process/1, - pending_exit_unlink_port/1, - pending_exit_trap_exit/1, - pending_exit_receive/1, - pending_exit_exit/1, - pending_exit_gc/1, - pending_exit_is_process_alive/1, - pending_exit_process_display/1, - pending_exit_process_info_1/1, - pending_exit_process_info_2/1, - pending_exit_group_leader/1, - exit_before_pending_exit/1]). +-export([xm_sig_order/1]). init_per_testcase(Func, Config) when is_atom(Func), is_list(Config) -> - available_internal_state(true), [{testcase, Func}|Config]. end_per_testcase(_Func, _Config) -> @@ -60,24 +46,14 @@ init_per_suite(Config) -> Config. end_per_suite(_Config) -> - available_internal_state(true), - catch erts_debug:set_internal_state(not_running_optimization, true), - available_internal_state(false). + ok. suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap, {minutes, 2}}]. all() -> - [xm_sig_order, pending_exit_unlink_process, - pending_exit_unlink_dist_process, - pending_exit_unlink_port, pending_exit_trap_exit, - pending_exit_receive, pending_exit_trap_exit, - pending_exit_gc, pending_exit_is_process_alive, - pending_exit_process_display, - pending_exit_process_info_1, - pending_exit_process_info_2, pending_exit_group_leader, - exit_before_pending_exit]. + [xm_sig_order]. %% Test that exit signals and messages are received in correct order @@ -113,362 +89,9 @@ xm_sig_order_proc() -> end, xm_sig_order_proc(). -pending_exit_unlink_process(Config) when is_list(Config) -> - pending_exit_test(self(), unlink). - -pending_exit_unlink_dist_process(Config) when is_list(Config) -> - {ok, Node} = start_node(Config), - From = spawn(Node, fun () -> receive after infinity -> ok end end), - Res = pending_exit_test(From, unlink), - stop_node(Node), - Res. - -pending_exit_unlink_port(Config) when is_list(Config) -> - pending_exit_test(hd(erlang:ports()), unlink). - -pending_exit_trap_exit(Config) when is_list(Config) -> - pending_exit_test(self(), trap_exit). - -pending_exit_receive(Config) when is_list(Config) -> - pending_exit_test(self(), 'receive'). - -pending_exit_exit(Config) when is_list(Config) -> - pending_exit_test(self(), exit). - -pending_exit_gc(Config) when is_list(Config) -> - pending_exit_test(self(), gc). - -pending_exit_test(From, Type) -> - OTE = process_flag(trap_exit, true), - Ref = make_ref(), - Master = self(), - ExitBySignal = case Type of - gc -> - lists:duplicate(10000, - exit_by_signal); - _ -> - exit_by_signal - end, - Pid = spawn_link( - fun () -> - receive go -> ok end, - false = have_pending_exit(), - exit = fake_exit(From, - self(), - ExitBySignal), - true = have_pending_exit(), - Master ! {self(), Ref, Type}, - case Type of - gc -> - force_gc(), - erlang:yield(); - unlink -> - unlink(From); - trap_exit -> - process_flag(trap_exit, true); - 'receive' -> - receive _ -> ok - after 0 -> ok - end; - exit -> - ok - end, - exit(exit_by_myself) - end), - Mon = erlang:monitor(process, Pid), - Pid ! go, - Reason = receive - {'DOWN', Mon, process, Pid, R} -> - receive - {Pid, Ref, Type} -> - ok - after 0 -> - ct:fail(premature_exit) - end, - case Type of - exit -> - exit_by_myself = R; - _ -> - ExitBySignal = R - end - end, - receive - {'EXIT', Pid, R2} -> - Reason = R2 - end, - process_flag(trap_exit, OTE), - ok, - {comment, "Test only valid with current SMP emulator."}. - - - -exit_before_pending_exit(Config) when is_list(Config) -> - %% This is a testcase testcase very specific to the smp - %% implementation as it is of the time of writing. - %% - %% The testcase tries to check that a process can - %% exit by itself even though it has a pending exit. - OTE = process_flag(trap_exit, true), - Master = self(), - Tester = spawn_link( - fun () -> - Opts = case {erlang:system_info(run_queues), - erlang:system_info(schedulers_online)} of - {RQ, SO} when RQ =:= 1; SO =:= 1 -> []; - _ -> - process_flag(scheduler, 1), - [{scheduler, 2}] - end, - P = self(), - Exiter = spawn_opt(fun () -> - receive - {exit_me, P, R} -> - exit(P, R) - end - end, Opts), - erlang:yield(), - Exiter ! {exit_me, self(), exited_by_exiter}, - %% We want to get a pending exit - %% before we exit ourselves. We - %% don't want to be scheduled out - %% since we will then see the - %% pending exit. - %% - %% Do something that takes - %% relatively long time but - %% consumes few reductions... - repeat(fun() -> erlang:system_info(procs) end,10), - %% ... then exit. - Master ! {self(), - pending_exit, - have_pending_exit()}, - exit(exited_by_myself) - end), - PendingExit = receive {Tester, pending_exit, PE} -> PE end, - receive - {'EXIT', Tester, exited_by_myself} -> - process_flag(trap_exit, OTE), - ok; - Msg -> - ct:fail({unexpected_message, Msg}) - end, - NoScheds = integer_to_list(erlang:system_info(schedulers_online)), - {comment, - "Was " - ++ case PendingExit of - true -> ""; - false ->"*not*" - end ++ " able to trigger a pending exit. " - ++ "Running on " ++ NoScheds ++ " scheduler(s). " - ++ "This test is only interesting with at least two schedulers."}. - --define(PE_INFO_REPEAT, 100). - -pending_exit_is_process_alive(Config) when is_list(Config) -> - S = exit_op_test_init(), - TestFun = fun (P) -> false = is_process_alive(P) end, - repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT), - verify_pending_exit_success(S), - comment(). - -pending_exit_process_info_1(Config) when is_list(Config) -> - S = exit_op_test_init(), - TestFun = fun (P) -> - undefined = process_info(P) - end, - repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT), - verify_pending_exit_success(S), - comment(). - -pending_exit_process_info_2(Config) when is_list(Config) -> - S0 = exit_op_test_init(), - repeated_exit_op_test(fun (P) -> - undefined = process_info(P, messages) - end, ?PE_INFO_REPEAT), - S1 = verify_pending_exit_success(S0), - repeated_exit_op_test(fun (P) -> - undefined = process_info(P, status) - end, ?PE_INFO_REPEAT), - S2 = verify_pending_exit_success(S1), - repeated_exit_op_test(fun (P) -> - undefined = process_info(P, links) - end, ?PE_INFO_REPEAT), - S3 = verify_pending_exit_success(S2), - repeated_exit_op_test(fun (P) -> - undefined = process_info(P, [messages]) - end, ?PE_INFO_REPEAT), - S4 = verify_pending_exit_success(S3), - repeated_exit_op_test(fun (P) -> - undefined = process_info(P, [status]) - end, ?PE_INFO_REPEAT), - S5 = verify_pending_exit_success(S4), - repeated_exit_op_test(fun (P) -> - undefined = process_info(P, [links]) - end, ?PE_INFO_REPEAT), - S6 = verify_pending_exit_success(S5), - repeated_exit_op_test(fun (P) -> - undefined = process_info(P, [status, - links]) - end, ?PE_INFO_REPEAT), - S7 = verify_pending_exit_success(S6), - repeated_exit_op_test(fun (P) -> - undefined = process_info(P, [messages, - status]) - end, ?PE_INFO_REPEAT), - S8 = verify_pending_exit_success(S7), - repeated_exit_op_test(fun (P) -> - undefined = process_info(P, [messages, - links]) - end, ?PE_INFO_REPEAT), - S9 = verify_pending_exit_success(S8), - repeated_exit_op_test( - fun (P) -> - undefined = process_info(P, [message_queue_len, - status]) - end, ?PE_INFO_REPEAT), - S10 = verify_pending_exit_success(S9), - repeated_exit_op_test(fun (P) -> - undefined = process_info(P, [messages, - links, - status]) - end, ?PE_INFO_REPEAT), - verify_pending_exit_success(S10), - comment(). - -pending_exit_process_display(Config) when is_list(Config) -> - S = exit_op_test_init(), - TestFun = fun (P) -> - badarg = try - erlang:process_display(P, backtrace) - catch - error:badarg -> badarg - end - end, - repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT), - verify_pending_exit_success(S), - comment(). - -pending_exit_group_leader(Config) when is_list(Config) -> - S = exit_op_test_init(), - TestFun = fun (P) -> - badarg = try - group_leader(self(), P) - catch - error:badarg -> badarg - end - end, - repeated_exit_op_test(TestFun, ?PE_INFO_REPEAT), - verify_pending_exit_success(S), - comment(). - %% %% -- Internal utils -------------------------------------------------------- %% -exit_op_test_init() -> - put(no_pending_exit_success, 0), - put(no_pending_exit_tries, 0), - {case {erlang:system_info(run_queues), - erlang:system_info(schedulers_online)} of - {RQ, SO} when RQ =:= 1; SO =:= 1 -> false; - _ -> true - end, 0, 0}. - -verify_pending_exit_success({false, _, _} = S) -> - S; -verify_pending_exit_success({true, S, T}) -> - NewS = get(no_pending_exit_success), - NewT = get(no_pending_exit_tries), - case NewT =:= T of - true -> ok; - _ -> case NewS > S of - true -> ok; - _ -> exit(no_pending_exits) - end - end, - {true, NewS, NewT}. - -comment() -> - {comment, - "Pending exit trigger ratio " - ++ integer_to_list(get(no_pending_exit_success)) - ++ "/" - ++ integer_to_list(get(no_pending_exit_tries)) - ++ "." - ++ case get(not_running_opt_test) of - true -> " No 'not running optimization' to disable."; - _ -> "" - end}. - -repeated_exit_op_test(TestFun, N) -> - WorkFun0 = fun () -> - lists:sort(lists:reverse(lists:seq(1, 1000))) - end, - repeat(fun () -> exit_op_test(TestFun, WorkFun0) end, N), - try erts_debug:set_internal_state(not_running_optimization, false) of - Bool when Bool == true; Bool == false -> - WorkFun1 = fun () -> - erts_debug:set_internal_state(sleep, 0), - lists:sort(lists:reverse(lists:seq(1, 1000))) - end, - repeat(fun () -> - exit_op_test(TestFun, WorkFun1) - end, N) - catch - error:notsup -> put(not_running_opt_test, true) - after - catch erts_debug:set_internal_state(not_running_optimization, true) - end. - -exit_op_test(TestFun, WorkFun) -> - Opts = case {erlang:system_info(run_queues), - erlang:system_info(schedulers_online)} of - {RQ, SO} when RQ =:= 1; SO =:= 1 -> []; - _ -> - process_flag(scheduler, 1), - [{scheduler, 2}] - end, - Master = self(), - Going = make_ref(), - P = spawn_opt(fun () -> - loop(10, WorkFun), - Master ! Going, - loop(infinity, WorkFun) - end, Opts), - receive Going -> ok end, - loop(10, WorkFun), - erlang:yield(), - exit(P, bang), - PE0 = have_pending_exit(P), - TestFun(P), - PE = case PE0 of - true -> true; - _ -> false - end, - case {PE, get(no_pending_exit_success), get(no_pending_exit_tries)} of - {true, undefined, undefined} -> - put(no_pending_exit_success, 1), - put(no_pending_exit_tries, 1); - {false, undefined, undefined} -> - put(no_pending_exit_success, 0), - put(no_pending_exit_tries, 1); - {true, S, T} -> - put(no_pending_exit_success, S+1), - put(no_pending_exit_tries, T+1); - {false, _S, T} -> - put(no_pending_exit_tries, T+1) - end, - ok. - -loop(infinity, WorkFun) -> - do_loop(infinity, WorkFun); -loop(0, _WorkFun) -> - ok; -loop(N, WorkFun) when is_integer(N) -> - do_loop(N-1, WorkFun). - -do_loop(N, WorkFun) -> - WorkFun(), - loop(N, WorkFun). repeat(_Fun, N) when is_integer(N), N =< 0 -> ok; @@ -486,30 +109,3 @@ start_node(Config) -> stop_node(Node) -> test_server:stop_node(Node). - -have_pending_exit() -> - have_pending_exit(self()). - -have_pending_exit(Pid) -> - erts_debug:get_internal_state({have_pending_exit, Pid}). - -force_gc() -> - erts_debug:set_internal_state(force_gc, self()). - -fake_exit(From, To, Reason) -> - erts_debug:set_internal_state(send_fake_exit_signal, {From, To, Reason}). - -available_internal_state(Bool) when Bool == true; Bool == false -> - case {Bool, - (catch erts_debug:get_internal_state(available_internal_state))} of - {true, true} -> - true; - {false, true} -> - erts_debug:set_internal_state(available_internal_state, false), - true; - {true, _} -> - erts_debug:set_internal_state(available_internal_state, true), - false; - {false, _} -> - false - end. diff --git a/erts/emulator/test/smoke_test_SUITE.erl b/erts/emulator/test/smoke_test_SUITE.erl index adc6f56c06..b3d34103f1 100644 --- a/erts/emulator/test/smoke_test_SUITE.erl +++ b/erts/emulator/test/smoke_test_SUITE.erl @@ -70,6 +70,20 @@ boot_combo(Config) when is_list(Config) -> chk_boot(Config, "+Ktrue", NOOP), chk_boot(Config, "+A42", A42), chk_boot(Config, "+Ktrue +A42", A42), + + WBTArgs = ["very_short", "short", "medium", "long", "very_long"], + WTArgs = ["very_low", "low", "medium", "high", "very_high"], + [chk_boot(Config, + " +sbwt " ++ WBT ++ + " +sbwtdcpu " ++ WBT ++ + " +sbwtdio " ++ WBT ++ + " +swt " ++ WT ++ + " +swtdcpu " ++ WT ++ + " +swtdio " ++ WT, NOOP) || WBT <- WBTArgs, WT <- WTArgs], + + WSArgs = ["legacy", "default"], + [chk_boot(Config, " +sws " ++ WS, NOOP) || WS <- WSArgs], + %% A lot more combos could be implemented... ok after diff --git a/erts/emulator/test/trace_SUITE.erl b/erts/emulator/test/trace_SUITE.erl index a81aa64057..def25dba7d 100644 --- a/erts/emulator/test/trace_SUITE.erl +++ b/erts/emulator/test/trace_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2017. All Rights Reserved. +%% Copyright Ericsson AB 1997-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -235,7 +235,7 @@ link_receive_call_correlation(Config) when is_list(Config) -> 1 = erlang:trace(Receiver, true, ['receive', procs, call, timestamp, scheduler_id]), 1 = erlang:trace_pattern({?MODULE, receive_msg, '_'}, [], [local]), - Num = 100000, + Num = 100, (fun F(0) -> []; F(N) -> @@ -255,7 +255,7 @@ link_receive_call_correlation(Config) when is_list(Config) -> Msgs = (fun F() -> receive M -> [M | F()] after 1 -> [] end end)(), - case check_consistent(Receiver, Num, Num, Num, Msgs) of + case check_consistent(Receiver, Num, Num, Num, Msgs, false, undefined) of ok -> ok; {error, Reason} -> @@ -265,20 +265,63 @@ link_receive_call_correlation(Config) when is_list(Config) -> -define(schedid, , _). -check_consistent(_Pid, Recv, Call, _LU, [Msg | _]) when Recv > Call -> +check_consistent(_Pid, Recv, Call, _LU, [Msg | _], _Received, _LinkedN) when Recv > Call -> {error, Msg}; -check_consistent(Pid, Recv, Call, LU, [Msg | Msgs]) -> +check_consistent(Pid, Recv, Call, LU, [Msg | Msgs], false, undefined) -> case Msg of {trace, Pid, 'receive', Recv ?schedid} -> - check_consistent(Pid,Recv - 1, Call, LU, Msgs); + check_consistent(Pid,Recv - 1, Call, LU, Msgs, true, undefined); {trace_ts, Pid, 'receive', Recv ?schedid, _} -> - check_consistent(Pid,Recv - 1, Call, LU, Msgs); + check_consistent(Pid,Recv - 1, Call, LU, Msgs, true, undefined); {trace, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid} -> - check_consistent(Pid,Recv, Call - 1, LU, Msgs); + check_consistent(Pid,Recv, Call - 1, LU, Msgs, false, undefined); {trace_ts, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid, _} -> - check_consistent(Pid,Recv, Call - 1, LU, Msgs); + check_consistent(Pid,Recv, Call - 1, LU, Msgs, false, undefined); + + {trace, Pid, _, _Self ?schedid} -> + check_consistent(Pid, Recv, Call, LU, Msgs, false, undefined); + {trace_ts, Pid, _, _Self ?schedid, _} -> + check_consistent(Pid, Recv, Call, LU, Msgs, false, undefined); + + Msg -> + {error, Msg} + end; +check_consistent(Pid, Recv, Call, LU, [Msg | Msgs], true, undefined) -> + + case Msg of + {trace, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid} -> + check_consistent(Pid,Recv, Call - 1, LU, Msgs, true, undefined); + {trace_ts, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid, _} -> + check_consistent(Pid,Recv, Call - 1, LU, Msgs, true, undefined); + + {trace, Pid, getting_linked, _Self ?schedid} -> + check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, Recv rem 2); + {trace_ts, Pid, getting_linked, _Self ?schedid, _} -> + check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, Recv rem 2); + + {trace, Pid, getting_unlinked, _Self ?schedid} -> + check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, (Recv+1) rem 2); + {trace_ts, Pid, getting_unlinked, _Self ?schedid, _} -> + check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, (Recv+1) rem 2); + + Msg -> + {error, Msg} + end; +check_consistent(Pid, Recv, Call, LU, [Msg | Msgs], true, LinkedN) -> + UnlinkedN = (LinkedN + 1) rem 2, + + case Msg of + {trace, Pid, 'receive', Recv ?schedid} when Recv == LU -> + check_consistent(Pid,Recv - 1, Call, LU, Msgs, true, LinkedN); + {trace_ts, Pid, 'receive', Recv ?schedid, _} when Recv == LU -> + check_consistent(Pid,Recv - 1, Call, LU, Msgs, true, LinkedN); + + {trace, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid} -> + check_consistent(Pid,Recv, Call - 1, LU, Msgs, true, LinkedN); + {trace_ts, Pid, call, {?MODULE, receive_msg, [Call]} ?schedid, _} -> + check_consistent(Pid,Recv, Call - 1, LU, Msgs, true, LinkedN); %% We check that for each receive we have gotten a %% getting_linked or getting_unlinked message. Also @@ -286,38 +329,38 @@ check_consistent(Pid, Recv, Call, LU, [Msg | Msgs]) -> %% message we expect to receive is an even number %% and odd number for getting_unlinked. {trace, Pid, getting_linked, _Self ?schedid} - when Recv rem 2 == 0, Recv == LU -> - check_consistent(Pid, Recv, Call, LU - 1, Msgs); + when Recv rem 2 == LinkedN -> + check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, LinkedN); {trace_ts, Pid, getting_linked, _Self ?schedid, _} - when Recv rem 2 == 0, Recv == LU -> - check_consistent(Pid, Recv, Call, LU - 1, Msgs); + when Recv rem 2 == LinkedN -> + check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, LinkedN); {trace, Pid, getting_unlinked, _Self ?schedid} - when Recv rem 2 == 1, Recv == LU -> - check_consistent(Pid, Recv, Call, LU - 1, Msgs); + when Recv rem 2 == UnlinkedN -> + check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, LinkedN); {trace_ts, Pid, getting_unlinked, _Self ?schedid, _} - when Recv rem 2 == 1, Recv == LU -> - check_consistent(Pid, Recv, Call, LU - 1, Msgs); + when Recv rem 2 == UnlinkedN -> + check_consistent(Pid, Recv, Call, LU - 1, Msgs, true, LinkedN); {trace,Pid,'receive',Ignore ?schedid} when Ignore == stop; Ignore == timeout -> - check_consistent(Pid, Recv, Call, LU, Msgs); + check_consistent(Pid, Recv, Call, LU, Msgs, true, LinkedN); {trace_ts,Pid,'receive',Ignore ?schedid,_} when Ignore == stop; Ignore == timeout -> - check_consistent(Pid, Recv, Call, LU, Msgs); + check_consistent(Pid, Recv, Call, LU, Msgs, true, LinkedN); {trace, Pid, exit, normal ?schedid} -> - check_consistent(Pid, Recv, Call, LU, Msgs); + check_consistent(Pid, Recv, Call, LU, Msgs, true, LinkedN); {trace_ts, Pid, exit, normal ?schedid, _} -> - check_consistent(Pid, Recv, Call, LU, Msgs); + check_consistent(Pid, Recv, Call, LU, Msgs, true, LinkedN); {'EXIT', Pid, normal} -> - check_consistent(Pid, Recv, Call, LU, Msgs); + check_consistent(Pid, Recv, Call, LU, Msgs, true, LinkedN); Msg -> {error, Msg} end; -check_consistent(_, 0, 0, 0, []) -> +check_consistent(_, 0, 0, 1, [], true, _) -> ok; -check_consistent(_, Recv, Call, LU, []) -> +check_consistent(_, Recv, Call, LU, [], _, _) -> {error,{Recv, Call, LU}}. receive_msg(M) -> diff --git a/erts/emulator/utils/make_driver_tab b/erts/emulator/utils/make_driver_tab index ffb5f58ebf..b7bca1dc3a 100755 --- a/erts/emulator/utils/make_driver_tab +++ b/erts/emulator/utils/make_driver_tab @@ -30,6 +30,7 @@ use File::Basename; my $file = ""; my $nif = ""; my @emu_drivers = (); +my @emu_nifs = (); my @static_drivers = (); my @static_nifs = (); my $mode = 1; @@ -61,7 +62,7 @@ while (@ARGV) { } elsif ($mode == 2) { $d = basename $d; $d =~ s/_nif(\..*|)$//; # strip nif.* or just nif - push(@static_nifs, $d); + push(@emu_nifs, $d); next; } $d = basename $d; @@ -94,37 +95,33 @@ foreach (@static_drivers) { } # The array itself -print "\nErlDrvEntry *driver_tab[] =\n{\n"; +print "\nErtsStaticDriver driver_tab[] =\n{\n"; foreach (@emu_drivers) { - print " &${_}driver_entry,\n"; + print " {&${_}driver_entry, 0},\n"; } foreach (@static_drivers) { - print " NULL, /* ${_} */\n"; + print " {NULL, 1}, /* ${_} */\n"; } -print " NULL\n};\n"; +print " {NULL}\n};\n"; print "void erts_init_static_drivers() {\n"; my $index = 0; foreach (@static_drivers) { - print " driver_tab[".(scalar @emu_drivers+$index)."] = ${_}_driver_init();\n"; + print " driver_tab[".(scalar @emu_drivers+$index)."].de = ${_}_driver_init();\n"; $index++; } print "}\n"; -print <<EOF; - -typedef struct ErtsStaticNifEntry_ { - const char *nif_name; - ErtsStaticNifInitFPtr nif_init; -} ErtsStaticNifEntry; - -EOF - # prototypes +foreach (@emu_nifs) { + my $d = ${_}; + $d =~ s/\.debug//; # strip .debug + print "void *".$d."_nif_init(void);\n"; +} foreach (@static_nifs) { my $d = ${_}; $d =~ s/\.debug//; # strip .debug @@ -134,20 +131,25 @@ foreach (@static_nifs) { # The array itself print "static ErtsStaticNifEntry static_nif_tab[] =\n{\n"; +foreach (@emu_nifs) { + my $d = ${_}; + $d =~ s/\.debug//; # strip .debug + print " {\"${_}\", &".$d."_nif_init, 0},\n"; +} foreach (@static_nifs) { my $d = ${_}; $d =~ s/\.debug//; # strip .debug - print "{\"${_}\",&".$d."_nif_init},\n"; + print " {\"${_}\", &".$d."_nif_init, 1},\n"; } print " {NULL,NULL}\n};\n"; print <<EOF; -ErtsStaticNifInitFPtr erts_static_nif_get_nif_init(const char *name, int len) { +ErtsStaticNifEntry* erts_static_nif_get_nif_init(const char *name, int len) { ErtsStaticNifEntry* p; for (p = static_nif_tab; p->nif_name != NULL; p++) if (strncmp(p->nif_name, name, len) == 0 && p->nif_name[len] == 0) - return p->nif_init; + return p; return NULL; } diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c index f7774c6e2e..cf6d1bacca 100644 --- a/erts/etc/common/erlexec.c +++ b/erts/etc/common/erlexec.c @@ -129,6 +129,8 @@ static char *plusM_other_switches[] = { /* +s arguments with values */ static char *pluss_val_switches[] = { "bt", + "bwtdcpu", + "bwtdio", "bwt", "cl", "ct", @@ -136,6 +138,8 @@ static char *pluss_val_switches[] = { "fwi", "tbt", "wct", + "wtdcpu", + "wtdio", "wt", "ws", "ss", diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in index 02ace9126c..bac90cb472 100644 --- a/erts/etc/unix/etp-commands.in +++ b/erts/etc/unix/etp-commands.in @@ -2,7 +2,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2017. All Rights Reserved. +# Copyright Ericsson AB 2005-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -1290,7 +1290,7 @@ document etp-msgq % Sequential trace tokens are included in comments and % the current match position in the queue is marked '<='. % -% A process's message queue is process_tab[i]->msg. +% A process's message queue is process_tab[i]->sig_qs. %--------------------------------------------------------------------------- end @@ -1693,7 +1693,7 @@ define etp-proc-state-int printf "dirty-cpu-proc | " end if ($arg0 & 0x2000000) - printf "GARBAGE<0x2000000> | " + printf "sig-q | " end if ($arg0 & 0x1000000) printf "off-heap-msgq | " @@ -1714,10 +1714,10 @@ define etp-proc-state-int printf "active-sys | " end if ($arg0 & 0x80000) - printf "trapping-exit | " + printf "sig-in-q | " end if ($arg0 & 0x40000) - printf "GARBAGE<0x40000> | " + printf "sys-tasks | " end if ($arg0 & 0x20000) printf "garbage-collecting | " @@ -1735,7 +1735,7 @@ define etp-proc-state-int printf "active | " end if ($arg0 & 0x1000) - printf "pending-exit | " + printf "unused | " end if ($arg0 & 0x800) printf "exiting | " @@ -1819,160 +1819,21 @@ document etp-proc-state % Print state of process %--------------------------------------------------------------------------- end -define etp-proc-state-int + +define etp-proc-flags-int # Args: int # - if ($arg0 & 0x80000000) - printf "GARBAGE<0x80000000> | " - end - if ($arg0 & 0x40000000) - printf "dirty-running-sys | " - end - if ($arg0 & 0x20000000) - printf "dirty-running | " - end - if ($arg0 & 0x10000000) - printf "dirty-active-sys | " + if ($arg0 & ~0xfffffff) + printf "GARBAGE<%x> ", ($arg0 & ~0x1ffffff) end if ($arg0 & 0x8000000) - printf "dirty-io-proc | " + printf "trap-exit " end if ($arg0 & 0x4000000) - printf "dirty-cpu-proc | " + printf "local-sigs-only " end if ($arg0 & 0x2000000) - printf "on-heap-msgq | " - end - if ($arg0 & 0x1000000) - printf "off-heap-msgq | " - end - if ($arg0 & 0x800000) - printf "delayed-sys | " - end - if ($arg0 & 0x400000) - printf "proxy | " - set $proxy_process = 1 - else - set $proxy_process = 0 - end - if ($arg0 & 0x200000) - printf "running-sys | " - end - if ($arg0 & 0x100000) - printf "active-sys | " - end - if ($arg0 & 0x80000) - printf "trapping-exit | " - end - if ($arg0 & 0x40000) - printf "bound | " - end - if ($arg0 & 0x20000) - printf "garbage-collecting | " - end - if ($arg0 & 0x10000) - printf "suspended | " - end - if ($arg0 & 0x8000) - printf "running | " - end - if ($arg0 & 0x4000) - printf "in-run-queue | " - end - if ($arg0 & 0x2000) - printf "active | " - end - if ($arg0 & 0x1000) - printf "pending-exit | " - end - if ($arg0 & 0x800) - printf "exiting | " - end - if ($arg0 & 0x400) - printf "free | " - end - if ($arg0 & 0x200) - printf "in-prq-low | " - end - if ($arg0 & 0x100) - printf "in-prq-normal | " - end - if ($arg0 & 0x80) - printf "in-prq-high | " - end - if ($arg0 & 0x40) - printf "in-prq-max | " - end - if ($arg0 & 0x30) == 0x0 - printf "prq-prio-max | " - else - if ($arg0 & 0x30) == 0x10 - printf "prq-prio-high | " - else - if ($arg0 & 0x30) == 0x20 - printf "prq-prio-normal | " - else - printf "prq-prio-low | " - end - end - end - if ($arg0 & 0xc) == 0x0 - printf "usr-prio-max | " - else - if ($arg0 & 0xc) == 0x4 - printf "usr-prio-high | " - else - if ($arg0 & 0xc) == 0x8 - printf "usr-prio-normal | " - else - printf "usr-prio-low | " - end - end - end - if ($arg0 & 0x3) == 0x0 - printf "act-prio-max\n" - else - if ($arg0 & 0x3) == 0x1 - printf "act-prio-high\n" - else - if ($arg0 & 0x3) == 0x2 - printf "act-prio-normal\n" - else - printf "act-prio-low\n" - end - end - end -end - -document etp-proc-state-int -%--------------------------------------------------------------------------- -% etp-proc-state-int int -% -% Print state of process state value -%--------------------------------------------------------------------------- -end - - -define etp-proc-state -# Args: Process* -# - set $state_int = *(((Uint32 *) &(((Process *) $arg0)->state))) - etp-proc-state-int $state_int -end - -document etp-proc-state -%--------------------------------------------------------------------------- -% etp-proc-state Process* -% -% Print state of process -%--------------------------------------------------------------------------- -end - -define etp-proc-flags-int -# Args: int -# - if ($arg0 & ~0x1ffffff) - printf "GARBAGE<%x> ", ($arg0 & ~0x1ffffff) + printf "hibernated " end if ($arg0 & 0x1000000) printf "dirty-minor-gc " @@ -2131,9 +1992,9 @@ define etp-process-info end printf " Mbuf size: %ld\n", $etp_proc->mbuf_sz if (etp_smp_compiled) - 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 + printf " Msgq len: %ld (inner=%ld, outer=%ld)\n", ($etp_proc->sig_qs.len + $etp_proc->sig_inq.len), $etp_proc->sig_qs.len, $etp_proc->sig_inq.len else - printf " Msgq len: %d\n", $etp_proc->msg.len + printf " Msgq len: %d\n", $etp_proc->sig_qs.len end printf " Parent: " etp-1 $etp_proc->parent @@ -2236,9 +2097,9 @@ define etp-process-memory-info end printf "] [Mbuf: %5ld", $etp_pmem_proc->mbuf_sz if (etp_smp_compiled) - 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 + printf " | %3ld (%3ld | %3ld)", ($etp_pmem_proc->sig_qs.len + $etp_pmem_proc->sig_inq.len), $etp_pmem_proc->sig_qs.len, $etp_pmem_proc->sig_inq.len else - printf " | %3ld", $etp_pmem_proc->msg.len + printf " | %3ld", $etp_pmem_proc->sig_qs.len end printf "] " if ($etp_pmem_proc->i) @@ -3696,7 +3557,7 @@ define etp-chart # Non-reentrant etp-chart-start ($arg0) set ($arg0) = ($arg0) - etp-msgq (($arg0)->msg) + etp-msgq (($arg0)->sig_qs) etp-stackdump ($arg0) etp-dictdump (($arg0)->dictionary) etp-dictdump (($arg0)->debug_dictionary) diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam Binary files differindex cf4b5c56ea..e93f053e01 100644 --- a/erts/preloaded/ebin/erlang.beam +++ b/erts/preloaded/ebin/erlang.beam diff --git a/erts/preloaded/ebin/erts_dirty_process_code_checker.beam b/erts/preloaded/ebin/erts_dirty_process_code_checker.beam Binary files differdeleted file mode 100644 index 2a4cb53d3e..0000000000 --- a/erts/preloaded/ebin/erts_dirty_process_code_checker.beam +++ /dev/null diff --git a/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam Binary files differnew file mode 100644 index 0000000000..2df6da7415 --- /dev/null +++ b/erts/preloaded/ebin/erts_dirty_process_signal_handler.beam diff --git a/erts/preloaded/ebin/erts_internal.beam b/erts/preloaded/ebin/erts_internal.beam Binary files differindex 7d2edd9845..f5967780ad 100644 --- a/erts/preloaded/ebin/erts_internal.beam +++ b/erts/preloaded/ebin/erts_internal.beam diff --git a/erts/preloaded/ebin/prim_file.beam b/erts/preloaded/ebin/prim_file.beam Binary files differindex 902b0945c6..9cc22222db 100644 --- a/erts/preloaded/ebin/prim_file.beam +++ b/erts/preloaded/ebin/prim_file.beam diff --git a/erts/preloaded/src/Makefile b/erts/preloaded/src/Makefile index a2872082f2..4333f6643a 100644 --- a/erts/preloaded/src/Makefile +++ b/erts/preloaded/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2008-2016. All Rights Reserved. +# Copyright Ericsson AB 2008-2018. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -47,7 +47,7 @@ PRE_LOADED_ERL_MODULES = \ erts_internal \ erl_tracer \ erts_literal_area_collector \ - erts_dirty_process_code_checker + erts_dirty_process_signal_handler PRE_LOADED_BEAM_MODULES = \ prim_eval diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl index 370ecdc3f6..bffa59338e 100644 --- a/erts/preloaded/src/erlang.erl +++ b/erts/preloaded/src/erlang.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ -export([localtime_to_universaltime/1]). -export([suspend_process/1]). -export([min/2, max/2]). --export([dmonitor_node/3, dmonitor_p/2]). +-export([dmonitor_node/3]). -export([delay_trap/2]). -export([set_cookie/2, get_cookie/0]). -export([nodes/0]). @@ -39,7 +39,6 @@ -export([integer_to_list/2]). -export([integer_to_binary/2]). -export([set_cpu_topology/1, format_cpu_topology/1]). --export([await_proc_exit/3]). -export([memory/0, memory/1]). -export([alloc_info/1, alloc_sizes/1]). @@ -122,7 +121,7 @@ -export([delete_element/2]). -export([delete_module/1, demonitor/1, demonitor/2, display/1]). -export([display_nl/0, display_string/1, erase/0, erase/1]). --export([error/1, error/2, exit/1, exit/2, external_size/1]). +-export([error/1, error/2, exit/1, exit/2, exit_signal/2, external_size/1]). -export([external_size/2, finish_after_on_load/2, finish_loading/1, float/1]). -export([float_to_binary/1, float_to_binary/2, float_to_list/1, float_to_list/2, floor/1]). @@ -786,6 +785,13 @@ exit(_Reason) -> exit(_Pid, _Reason) -> erlang:nif_error(undefined). +%% exit_signal/2 +-spec erlang:exit_signal(Pid, Reason) -> true when + Pid :: pid() | port(), + Reason :: term(). +exit_signal(_Pid, _Reason) -> + erlang:nif_error(undefined). + %% external_size/1 -spec erlang:external_size(Term) -> non_neg_integer() when Term :: term(). @@ -1012,8 +1018,20 @@ group_leader() -> -spec group_leader(GroupLeader, Pid) -> true when GroupLeader :: pid(), Pid :: pid(). -group_leader(_GroupLeader, _Pid) -> - erlang:nif_error(undefined). +group_leader(GroupLeader, Pid) -> + case case erts_internal:group_leader(GroupLeader, Pid) of + false -> + Ref = erlang:make_ref(), + erts_internal:group_leader(GroupLeader, + Pid, + Ref), + receive {Ref, MsgRes} -> MsgRes end; + Res -> + Res + end of + true -> true; + Error -> erlang:error(Error, [GroupLeader, Pid]) + end. %% halt/0 %% Shadowed by erl_bif_types: erlang:halt/0 @@ -1904,7 +1922,7 @@ element(_N, _Tuple) -> %% Not documented -type module_info_key() :: attributes | compile | exports | functions | md5 - | module | native | native_addresses. + | module | native | native_addresses | nifs. -spec erlang:get_module_info(Module, Item) -> ModuleInfo when Module :: atom(), Item :: module_info_key(), @@ -3279,13 +3297,6 @@ dmonitor_node(Node, Flag, Opts) -> dmonitor_node(Node,Flag,[]) end. --spec erlang:dmonitor_p('process', pid() | {atom(),atom()}) -> reference(). -dmonitor_p(process, ProcSpec) -> - %% Only called when auto-connect attempt failed early in VM - Ref = erlang:make_ref(), - erlang:self() ! {'DOWN', Ref, process, ProcSpec, noconnection}, - Ref. - %% %% Trap function used when modified timing has been enabled. %% @@ -3518,33 +3529,6 @@ rvrs(Xs) -> rvrs(Xs, []). rvrs([],Ys) -> Ys; rvrs([X|Xs],Ys) -> rvrs(Xs, [X|Ys]). -%% erlang:await_proc_exit/3 is for internal use only! -%% -%% BIFs that need to await a specific process exit before -%% returning traps to erlang:await_proc_exit/3. -%% -%% NOTE: This function is tightly coupled to -%% the implementation of the -%% erts_bif_prep_await_proc_exit_*() -%% functions in bif.c. Do not make -%% any changes to it without reading -%% the comment about them in bif.c! --spec erlang:await_proc_exit(dst(), 'apply' | 'data' | 'reason', term()) -> term(). -await_proc_exit(Proc, Op, Data) -> - Mon = erlang:monitor(process, Proc), - receive - {'DOWN', Mon, process, _Proc, Reason} -> - case Op of - apply -> - {M, F, A} = Data, - erlang:apply(M, F, A); - data -> - Data; - reason -> - Reason - end - end. - -spec min(Term1, Term2) -> Minimum when Term1 :: term(), Term2 :: term(), diff --git a/erts/preloaded/src/erts_dirty_process_code_checker.erl b/erts/preloaded/src/erts_dirty_process_code_checker.erl deleted file mode 100644 index 7d3fa264be..0000000000 --- a/erts/preloaded/src/erts_dirty_process_code_checker.erl +++ /dev/null @@ -1,81 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - --module(erts_dirty_process_code_checker). - --export([start/0]). - -%% -%% The erts_dirty_process_code_checker is started at -%% VM boot by the VM. It is a spawned as a system -%% process, i.e, the whole VM will terminate if -%% this process terminates. -%% -start() -> - process_flag(trap_exit, true), - msg_loop(). - -msg_loop() -> - _ = receive - Request -> - handle_request(Request) - end, - msg_loop(). - -check_process(Requester, Target, ReqId, Module) -> - Result = erts_internal:check_dirty_process_code(Target, Module), - Requester ! {check_process_code, ReqId, Result}. - -handle_request({Requester, - Target, - Prio, - {check_process_code, - ReqId, - Module} = Op}) -> - %% - %% Target may have stopped executing dirty since the - %% initial request was made. Check its current state - %% and try to send the request if possible; otherwise, - %% check the dirty executing process and send the result... - %% - try - case erts_internal:is_process_executing_dirty(Target) of - true -> - check_process(Requester, Target, ReqId, Module); - false -> - case erts_internal:request_system_task(Requester, - Target, - Prio, - Op) of - ok -> - ok; - dirty_execution -> - check_process(Requester, Target, ReqId, Module) - end - end - catch - _ : _ -> - ok %% Ignore all failures; someone passed us garbage... - end; -handle_request(_Garbage) -> - ignore. - - - diff --git a/erts/preloaded/src/erts_dirty_process_signal_handler.erl b/erts/preloaded/src/erts_dirty_process_signal_handler.erl new file mode 100644 index 0000000000..ab71790b9d --- /dev/null +++ b/erts/preloaded/src/erts_dirty_process_signal_handler.erl @@ -0,0 +1,97 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2018. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(erts_dirty_process_signal_handler). + +-export([start/0]). + +%% +%% The erts_dirty_process_signal_handler is started at +%% VM boot by the VM. It is a spawned as a system +%% process, i.e, the whole VM will terminate if +%% this process terminates. +%% +start() -> + process_flag(trap_exit, true), + msg_loop(). + +msg_loop() -> + _ = receive + Request -> + try + handle_request(Request) + catch + _ : _ -> + %% Ignore all failures; + %% someone passed us garbage... + ok + end + end, + msg_loop(). + +handle_request(Pid) when is_pid(Pid) -> + handle_incoming_signals(Pid, 0); +handle_request({Requester, Target, Prio, + {SysTaskOp, ReqId, Arg} = Op} = Request) -> + case handle_sys_task(Requester, Target, SysTaskOp, ReqId, Arg) of + true -> + ok; + false -> + %% Target has stopped executing dirty since the + %% initial request was made. Dispatch the + %% request to target and let it handle it itself... + case erts_internal:request_system_task(Requester, + Target, + Prio, + Op) of + ok -> + ok; + dirty_execution -> + %% Ahh... It began executing dirty again... + handle_request(Request) + end + end; +handle_request(_Garbage) -> + ignore. + +%% +%% ---------------------------------------------------------------------------- +%% + +handle_incoming_signals(Pid, 5) -> + self() ! Pid; %% Work with other requests for a while... +handle_incoming_signals(Pid, N) -> + case erts_internal:dirty_process_handle_signals(Pid) of + more -> handle_incoming_signals(Pid, N+1); + _Res -> ok + end. + +handle_sys_task(Requester, Target, check_process_code, ReqId, Module) -> + case erts_internal:is_process_executing_dirty(Target) of + false -> + false; + true -> + _ = check_process(Requester, Target, ReqId, Module), + true + end. + +check_process(Requester, Target, ReqId, Module) -> + Result = erts_internal:check_dirty_process_code(Target, Module), + Requester ! {check_process_code, ReqId, Result}. diff --git a/erts/preloaded/src/erts_internal.erl b/erts/preloaded/src/erts_internal.erl index a083e9ac2f..da5c9c68ed 100644 --- a/erts/preloaded/src/erts_internal.erl +++ b/erts/preloaded/src/erts_internal.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2012-2017. All Rights Reserved. +%% Copyright Ericsson AB 2012-2018. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -45,6 +45,8 @@ -export([check_process_code/3]). -export([check_dirty_process_code/2]). -export([is_process_executing_dirty/1]). +-export([dirty_process_handle_signals/1]). + -export([release_literal_area_switch/0]). -export([purge_module/2]). @@ -63,6 +65,7 @@ -export([dist_ctrl_put_data/2]). +-export([get_dflags/0]). -export([new_connection/1]). -export([abort_connection/2]). @@ -70,9 +73,13 @@ gather_sched_wall_time_result/1, await_sched_wall_time_modifications/2]). +-export([group_leader/2, group_leader/3]). + %% Auto import name clash -export([check_process_code/1]). +-export([is_process_alive/1, is_process_alive/2]). + %% %% Await result of send to port %% @@ -305,6 +312,13 @@ check_dirty_process_code(_Pid,_Module) -> is_process_executing_dirty(_Pid) -> erlang:nif_error(undefined). +-spec dirty_process_handle_signals(Pid) -> Res when + Pid :: pid(), + Res :: 'false' | 'true' | 'noproc' | 'normal' | 'more' | 'ok'. + +dirty_process_handle_signals(_Pid) -> + erlang:nif_error(undefined). + -spec release_literal_area_switch() -> 'true' | 'false'. release_literal_area_switch() -> @@ -497,10 +511,10 @@ dist_ctrl_put_data(DHandle, IoList) -> %% erlang:dist_ctrl_put_data/2 ... RootST = try erlang:error(Reason) catch - error:Reason -> - case erlang:get_stacktrace() of + error:Reason:ST -> + case ST of [] -> []; - ST -> tl(ST) + [_|T] -> T end end, StackTrace = [{erlang, dist_ctrl_put_data, @@ -510,6 +524,11 @@ dist_ctrl_put_data(DHandle, IoList) -> end. +-spec erts_internal:get_dflags() -> {erts_dflags, integer(), integer(), + integer(), integer(), integer()}. +get_dflags() -> + erlang:nif_error(undefined). + -spec erts_internal:new_connection(Node) -> ConnId when Node :: atom(), ConnId :: {integer(), erlang:dist_handle()}. @@ -568,3 +587,37 @@ sched_wall_time(Ref, N, Acc) -> {Ref, SWTL} when erlang:is_list(SWTL) -> sched_wall_time(Ref, N-1, Acc ++ SWTL); {Ref, SWT} -> sched_wall_time(Ref, N-1, [SWT|Acc]) end. + +-spec erts_internal:group_leader(GL, Pid) -> true | false | badarg when + GL :: pid(), + Pid :: pid(). + +group_leader(_GL, _Pid) -> + erlang:nif_error(undefined). + +-spec erts_internal:group_leader(GL, Pid, Ref) -> ok when + GL :: pid(), + Pid :: pid(), + Ref :: reference(). + +group_leader(_GL, _Pid, _Ref) -> + erlang:nif_error(undefined). + +-spec erts_internal:is_process_alive(Pid, Ref) -> 'ok' when + Pid :: pid(), + Ref :: reference(). + +is_process_alive(_Pid, _Ref) -> + erlang:nif_error(undefined). + +-spec erts_internal:is_process_alive(Pid) -> boolean() when + Pid :: pid(). + +is_process_alive(Pid) -> + Ref = make_ref(), + erts_internal:is_process_alive(Pid, Ref), + receive + {Ref, Res} -> + Res + end. + diff --git a/erts/preloaded/src/prim_file.erl b/erts/preloaded/src/prim_file.erl index 35042a7c72..432a8c15cd 100644 --- a/erts/preloaded/src/prim_file.erl +++ b/erts/preloaded/src/prim_file.erl @@ -618,8 +618,10 @@ write_file_info_1(Filename, Info, TimeType) -> error:_ -> {error, badarg} end. -set_owner(_EncodedName, undefined, undefined) -> - ok; +set_owner(EncodedName, Uid, undefined) -> + set_owner(EncodedName, Uid, -1); +set_owner(EncodedName, undefined, Gid) -> + set_owner(EncodedName, -1, Gid); set_owner(EncodedName, Uid, Gid) -> set_owner_nif(EncodedName, Uid, Gid). set_owner_nif(_Path, _Uid, _Gid) -> diff --git a/erts/preloaded/src/prim_zip.erl b/erts/preloaded/src/prim_zip.erl index b1ddbbe173..5cc15b7acd 100644 --- a/erts/preloaded/src/prim_zip.erl +++ b/erts/preloaded/src/prim_zip.erl @@ -74,8 +74,8 @@ open(FilterFun, FilterAcc, F) when is_function(FilterFun, 2) -> throw(Reason); throw:InternalReason -> {error, InternalReason}; - Class:Reason -> - erlang:error(erlang:raise(Class, Reason, erlang:get_stacktrace())) + Class:Reason:Stk -> + erlang:error(erlang:raise(Class, Reason, Stk)) end; open(_, _, _) -> {error, einval}. @@ -89,9 +89,9 @@ do_open(FilterFun, FilterAcc, F) -> {PrimZip2, FilterAcc2} = get_central_dir(PrimZip, FilterFun, FilterAcc), {ok, PrimZip2, FilterAcc2} catch - Class:Reason -> + Class:Reason:Stk -> _ = close(PrimZip), - erlang:error(erlang:raise(Class, Reason, erlang:get_stacktrace())) + erlang:error(erlang:raise(Class, Reason, Stk)) end. %% iterate over all files in a zip archive @@ -106,8 +106,8 @@ foldl(FilterFun, FilterAcc, #primzip{files = Files} = PrimZip) throw(Reason); throw:InternalReason -> {error, InternalReason}; - Class:Reason -> - erlang:error(erlang:raise(Class, Reason, erlang:get_stacktrace())) + Class:Reason:Stk -> + erlang:error(erlang:raise(Class, Reason, Stk)) end; foldl(_, _, _) -> {error, einval}. diff --git a/erts/test/erlexec_SUITE.erl b/erts/test/erlexec_SUITE.erl index db993abe52..73ed0ac56a 100644 --- a/erts/test/erlexec_SUITE.erl +++ b/erts/test/erlexec_SUITE.erl @@ -400,7 +400,7 @@ emu_args(CmdLineArgs) -> {ok,[[Erl]]} = init:get_argument(progname), EmuCL = os:cmd(Erl ++ " -emu_args_exit " ++ CmdLineArgs), io:format("EmuCL = ~ts", [EmuCL]), - split_emu_clt(string:lexemes(EmuCL, [$ ,$\t,$\n,$\r])). + split_emu_clt(string:lexemes(EmuCL, [$ ,$\t,$\n,[$\r,$\n]])). split_emu_clt(EmuCLT) -> split_emu_clt(EmuCLT, [], [], [], emu). diff --git a/erts/vsn.mk b/erts/vsn.mk index 50b77fdbc0..c3a62a5535 100644 --- a/erts/vsn.mk +++ b/erts/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% # -VSN = 9.2.1 +VSN = 9.3 # Port number 4365 in 4.2 # Port number 4366 in 4.3 |