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