aboutsummaryrefslogtreecommitdiffstats
path: root/erts
diff options
context:
space:
mode:
Diffstat (limited to 'erts')
-rwxr-xr-xerts/autoconf/configure.vxworks1
-rw-r--r--erts/autoconf/vxworks/sed.general2
-rw-r--r--erts/doc/src/Makefile39
-rw-r--r--erts/doc/src/alt_disco.xml2
-rw-r--r--erts/doc/src/book.xml3
-rw-r--r--erts/doc/src/communication.xml2
-rw-r--r--erts/doc/src/crash_dump.xml2
-rw-r--r--erts/doc/src/erl.xml23
-rw-r--r--erts/doc/src/erl_driver.xml24
-rw-r--r--erts/doc/src/erl_ext_dist.xml251
-rw-r--r--erts/doc/src/erl_nif.xml4
-rw-r--r--erts/doc/src/erlang.xml6
-rw-r--r--erts/doc/src/internal.xml44
-rw-r--r--erts/doc/src/notes.xml128
-rw-r--r--erts/emulator/Makefile.in2
-rw-r--r--erts/emulator/beam/arith_instrs.tab83
-rw-r--r--erts/emulator/beam/beam_debug.c16
-rw-r--r--erts/emulator/beam/beam_emu.c54
-rw-r--r--erts/emulator/beam/bif.c4
-rw-r--r--erts/emulator/beam/bif.tab1
-rw-r--r--erts/emulator/beam/big.c18
-rw-r--r--erts/emulator/beam/big.h2
-rw-r--r--erts/emulator/beam/break.c8
-rw-r--r--erts/emulator/beam/bs_instrs.tab2
-rw-r--r--erts/emulator/beam/dist.c38
-rw-r--r--erts/emulator/beam/dist.h6
-rw-r--r--erts/emulator/beam/erl_alloc.c15
-rw-r--r--erts/emulator/beam/erl_bif_info.c17
-rw-r--r--erts/emulator/beam/erl_cpu_topology.c41
-rw-r--r--erts/emulator/beam/erl_cpu_topology.h22
-rw-r--r--erts/emulator/beam/erl_db.c162
-rw-r--r--erts/emulator/beam/erl_db.h13
-rw-r--r--erts/emulator/beam/erl_db_catree.c74
-rw-r--r--erts/emulator/beam/erl_db_catree.h6
-rw-r--r--erts/emulator/beam/erl_db_hash.c63
-rw-r--r--erts/emulator/beam/erl_db_tree.c97
-rw-r--r--erts/emulator/beam/erl_db_tree_util.h18
-rw-r--r--erts/emulator/beam/erl_db_util.h20
-rw-r--r--erts/emulator/beam/erl_flxctr.c370
-rw-r--r--erts/emulator/beam/erl_flxctr.h406
-rw-r--r--erts/emulator/beam/erl_init.c30
-rw-r--r--erts/emulator/beam/erl_message.c3
-rw-r--r--erts/emulator/beam/erl_node_tables.c7
-rw-r--r--erts/emulator/beam/erl_node_tables.h3
-rw-r--r--erts/emulator/beam/erl_posix_str.c3
-rw-r--r--erts/emulator/beam/erl_proc_sig_queue.c2
-rw-r--r--erts/emulator/beam/erl_process.c16
-rw-r--r--erts/emulator/beam/erl_process.h2
-rw-r--r--erts/emulator/beam/erl_utils.h1
-rw-r--r--erts/emulator/beam/external.c90
-rw-r--r--erts/emulator/beam/external.h8
-rw-r--r--erts/emulator/beam/global.h9
-rw-r--r--erts/emulator/beam/instrs.tab2
-rw-r--r--erts/emulator/beam/macros.tab15
-rw-r--r--erts/emulator/beam/msg_instrs.tab1
-rw-r--r--erts/emulator/beam/ops.tab11
-rw-r--r--erts/emulator/beam/sys.h8
-rw-r--r--erts/emulator/beam/utils.c8
-rw-r--r--erts/emulator/internal_doc/CarrierMigration.md27
-rw-r--r--erts/emulator/internal_doc/CodeLoading.md6
-rw-r--r--erts/emulator/internal_doc/GarbageCollection.md53
-rw-r--r--erts/emulator/internal_doc/PTables.md4
-rw-r--r--erts/emulator/internal_doc/SuperCarrier.md10
-rw-r--r--erts/emulator/internal_doc/Tracing.md6
-rw-r--r--erts/emulator/internal_doc/beam_makeops.md7
-rw-r--r--erts/emulator/sys/common/erl_check_io.c6
-rw-r--r--erts/emulator/sys/unix/sys_drivers.c4
-rw-r--r--erts/emulator/test/Makefile9
-rw-r--r--erts/emulator/test/bs_construct_SUITE.erl86
-rw-r--r--erts/emulator/test/bs_match_int_SUITE.erl71
-rw-r--r--erts/emulator/test/distribution_SUITE.erl102
-rw-r--r--erts/emulator/test/driver_SUITE.erl4
-rw-r--r--erts/emulator/test/dump_SUITE.erl51
-rw-r--r--erts/emulator/test/efile_SUITE.erl45
-rw-r--r--erts/emulator/test/emulator_bench.spec1
-rw-r--r--erts/emulator/test/erts_debug_SUITE.erl15
-rw-r--r--erts/emulator/test/erts_test_utils.erl2
-rw-r--r--erts/emulator/test/fun_SUITE.erl2
-rw-r--r--erts/emulator/test/lcnt_SUITE.erl3
-rw-r--r--erts/emulator/test/map_SUITE.erl92
-rw-r--r--erts/emulator/test/process_SUITE.erl42
-rw-r--r--erts/emulator/test/small_SUITE.erl115
-rw-r--r--erts/emulator/test/statistics_SUITE.erl43
-rw-r--r--erts/epmd/epmd.mk2
-rw-r--r--erts/epmd/src/epmd.h1
-rw-r--r--erts/epmd/src/epmd_int.h10
-rw-r--r--erts/epmd/src/epmd_srv.c89
-rw-r--r--erts/etc/unix/cerl.src2
-rw-r--r--erts/etc/unix/etp-commands.in8
-rw-r--r--erts/etc/unix/to_erl.c2
-rw-r--r--erts/include/internal/ethr_internal.h2
-rw-r--r--erts/include/internal/ethread_inline.h3
-rw-r--r--erts/test/erl_print_SUITE.erl41
-rw-r--r--erts/test/z_SUITE.erl4
-rw-r--r--erts/vsn.mk2
95 files changed, 2578 insertions, 702 deletions
diff --git a/erts/autoconf/configure.vxworks b/erts/autoconf/configure.vxworks
index 1893f3f7e0..c3bdfd0095 100755
--- a/erts/autoconf/configure.vxworks
+++ b/erts/autoconf/configure.vxworks
@@ -135,6 +135,7 @@ for file in $CONFIG_FILES; do
-e "s,@HOST_TYPE@,$HOST_TYPE,g" \
-e "s,@WIND_BASE@,$WIND_BASE,g" \
-e "s,@TARGET@,$target,g" \
+ -e "s,@ENV_CFLAGS@,$CFLAGS,g" \
$in_file > $new_name
done
diff --git a/erts/autoconf/vxworks/sed.general b/erts/autoconf/vxworks/sed.general
index d32fbdc5c0..ffd5a8133c 100644
--- a/erts/autoconf/vxworks/sed.general
+++ b/erts/autoconf/vxworks/sed.general
@@ -111,7 +111,7 @@ s|@erlexec@|erl.exec|
s|@EMU_LIBOBJS@||
# General CFLAGS
-s|@GENERAL_CFLAGS@|-DHAVE_LOCALTIME_R -DHAVE_GMTIME_R -DENABLE_ELIB_MALLOC -DELIB_HEAP_USER -DELIB_SORTED_BLOCKS -DWORDS_BIGENDIAN -DELIB_DONT_INITIALIZE -DSIZEOF_CHAR=1 -DSIZEOF_SHORT=2 -DSIZEOF_INT=4 -DSIZEOF_LONG=4 -DSIZEOF_LONG_LONG=8 -DSIZEOF_VOID_P=4 -DERTS_USE_PORT_TASKS=1|g
+s|@GENERAL_CFLAGS@|@ENV_CFLAGS@ -DHAVE_LOCALTIME_R -DHAVE_GMTIME_R -DENABLE_ELIB_MALLOC -DELIB_HEAP_USER -DELIB_SORTED_BLOCKS -DWORDS_BIGENDIAN -DELIB_DONT_INITIALIZE -DSIZEOF_CHAR=1 -DSIZEOF_SHORT=2 -DSIZEOF_INT=4 -DSIZEOF_LONG=4 -DSIZEOF_LONG_LONG=8 -DSIZEOF_VOID_P=4 -DERTS_USE_PORT_TASKS=1|g
s|@WFLAGS@||
# Thread flags for eidefs.mk (erl_interface)
diff --git a/erts/doc/src/Makefile b/erts/doc/src/Makefile
index 06a8691c0e..bc01919da1 100644
--- a/erts/doc/src/Makefile
+++ b/erts/doc/src/Makefile
@@ -67,7 +67,22 @@ XML_REF3_FILES = \
erts_alloc.xml
XML_PART_FILES = \
- part.xml
+ part.xml internal.xml
+
+XML_INTERNAL_FILES = \
+ CarrierMigration.xml \
+ ThreadProgress.xml \
+ CodeLoading.xml \
+ Tracing.xml \
+ DelayedDealloc.xml \
+ beam_makeops.xml \
+ GarbageCollection.xml \
+ PTables.xml \
+ PortSignals.xml \
+ ProcessManagementOptimizations.xml \
+ SuperCarrier.xml \
+ CountingInstructions.xml
+
XML_CHAPTER_FILES = \
introduction.xml \
@@ -97,6 +112,8 @@ XML_FILES = \
$(BOOK_FILES) $(XML_CHAPTER_FILES) \
$(XML_PART_FILES) $(XML_REF3_FILES) $(XML_REF1_FILES) $(XML_APPLICATION_FILES)
+XML_GEN_FILES = $(XML_INTERNAL_FILES:%=$(XMLDIR)/%)
+
# ----------------------------------------------------
HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
@@ -116,6 +133,12 @@ SPECS_FILES = $(XML_REF3_EFILES:%.xml=$(SPECDIR)/specs_%.xml)
TOP_SPECS_FILE = specs.xml
+XML_FIGURE_DIR = $(XMLDIR)/figures
+
+INTERNAL_DOC_PNG_FILES = $(wildcard ../../emulator/internal_doc/figures/*.png)
+PNG_FILES = $(notdir $(INTERNAL_DOC_PNG_FILES))
+XMLDIR_PNG_FILES = $(PNG_FILES:%=$(XML_FIGURE_DIR)/%)
+
# ----------------------------------------------------
# FLAGS
# ----------------------------------------------------
@@ -128,10 +151,15 @@ SPECS_FLAGS = -I$(KERNEL_SRC) -I$(KERNEL_INCLUDE)
# ----------------------------------------------------
# Targets
# ----------------------------------------------------
+_create_dirs := $(shell mkdir -p $(XML_FIGURE_DIR))
+
$(HTMLDIR)/%.gif: %.gif
$(INSTALL_DATA) $< $@
-docs: man pdf html $(INFO_FILE)
+$(XML_FIGURE_DIR)/%.png: ../../emulator/internal_doc/figures/%.png
+ $(INSTALL_DATA) $< $@
+
+docs: figures man pdf html $(INFO_FILE)
$(TOP_PDF_FILE): $(XML_FILES)
@@ -146,6 +174,7 @@ gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
$(INFO_FILE): $(INFO_FILE_SRC) $(ERL_TOP)/make/$(TARGET)/otp.mk
sed -e 's;%RELEASE%;$(SYSTEM_VSN);' $(INFO_FILE_SRC) > $(INFO_FILE)
+figures: $(XMLDIR_PNG_FILES)
debug opt:
@@ -164,6 +193,9 @@ $(SPECDIR)/specs_%.xml:
$(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \
-o$(dir $@) -module $(patsubst $(SPECDIR)/specs_%.xml,%,$@)
+$(XMLDIR)/%.xml: ../../emulator/internal_doc/%.md $(ERL_TOP)/make/emd2exml
+ $(ERL_TOP)/make/emd2exml $< $@
+
# ----------------------------------------------------
# Release Target
# ----------------------------------------------------
@@ -173,8 +205,11 @@ release_docs_spec: docs
$(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf"
$(INSTALL_DIR) "$(RELSYSDIR)/doc/html"
+ $(INSTALL_DIR) "$(RELSYSDIR)/doc/html/figures"
$(INSTALL_DATA) $(HTMLDIR)/* \
"$(RELSYSDIR)/doc/html"
+ $(INSTALL_DATA) $(XMLDIR)/figures/* \
+ "$(RELSYSDIR)/doc/html/figures"
$(INSTALL_DATA) $(ERL_TOP)/erts/example/time_compat.erl \
"$(RELSYSDIR)/doc/html"
$(INSTALL_DATA) $(ERL_TOP)/lib/kernel/examples/gen_tcp_dist/src/gen_tcp_dist.erl \
diff --git a/erts/doc/src/alt_disco.xml b/erts/doc/src/alt_disco.xml
index d04221b9b3..148d6f549e 100644
--- a/erts/doc/src/alt_disco.xml
+++ b/erts/doc/src/alt_disco.xml
@@ -63,7 +63,7 @@
<seealso marker="kernel:erl_epmd">EPMD module</seealso>. However, instead of
communicating with EPMD you can connect to any service to find out
connection details of other nodes. A discovery module is enabled
- by setting <seealso marker="erts:erl#epmd_module">-epmd_module</seealso>
+ by setting <seealso marker="erts:erl">-epmd_module</seealso>
when starting erlang. The discovery module must implement the following
callbacks:</p>
diff --git a/erts/doc/src/book.xml b/erts/doc/src/book.xml
index a0780c91d9..d79da1e4f7 100644
--- a/erts/doc/src/book.xml
+++ b/erts/doc/src/book.xml
@@ -41,6 +41,9 @@
<applications>
<xi:include href="ref_man.xml"/>
</applications>
+ <internals>
+ <xi:include href="internal.xml"/>
+ </internals>
<releasenotes>
<xi:include href="notes.xml"/>
</releasenotes>
diff --git a/erts/doc/src/communication.xml b/erts/doc/src/communication.xml
index 7e18a73aa8..251b52dc65 100644
--- a/erts/doc/src/communication.xml
+++ b/erts/doc/src/communication.xml
@@ -64,7 +64,7 @@
a synchronous communication operation consists of two asynchronous
signals; one request signal and one reply signal. An example of
such a synchronous communication is a call to
- <seealso marker="erlang:process_info/2">
+ <seealso marker="erlang#process_info/2">
<c>erlang:process_info/2</c></seealso>
when the first argument is not <c>self()</c>. The caller sends
an asynchronous signal requesting information, and then
diff --git a/erts/doc/src/crash_dump.xml b/erts/doc/src/crash_dump.xml
index a9aeb1888c..33d0903622 100644
--- a/erts/doc/src/crash_dump.xml
+++ b/erts/doc/src/crash_dump.xml
@@ -290,7 +290,7 @@ Slogan: &lt;reason&gt;</pre>
<title>Memory Information</title>
<p>Under the tag <em>=memory</em> is shown information similar
to what can be obtainted on a living node with
- <seealso marker="erts:erlang#erlang:memory/0">
+ <seealso marker="erts:erlang#memory/0">
<c>erlang:memory()</c></seealso>.</p>
</section>
diff --git a/erts/doc/src/erl.xml b/erts/doc/src/erl.xml
index 88ddb03e97..471d7caa5a 100644
--- a/erts/doc/src/erl.xml
+++ b/erts/doc/src/erl.xml
@@ -636,6 +636,29 @@
produces a crash dump. On Unix systems, sending an emulator process
a <c>SIGUSR1</c> signal also forces a crash dump.</p>
</item>
+ <tag><marker id="+dcg"/><c><![CDATA[+rg DecentralizedCounterGroupsLimit]]></c></tag>
+ <item>
+ <p>Limits the number of decentralized counter groups used by
+ decentralized counters optimized for update operations in the
+ Erlang runtime system. By default, the limit is 256.</p>
+ <p>When the number of schedulers is less than or equal to the
+ limit, each scheduler has its own group. When the
+ number of schedulers is larger than the groups limit,
+ schedulers share groups. Shared groups degrade
+ the performance for updating counters while many reader groups
+ degrade the performance for reading counters. So, the limit is a tradeoff
+ between performance for update operations and performance for
+ read operations. Each group consumes 64 bytes in each
+ counter.</p>
+ <p>Notice that a runtime system using decentralized
+ counter groups benefits from <seealso marker="#+sbt">binding
+ schedulers to logical processors</seealso>, as the groups are
+ distributed better between schedulers with this option.</p>
+ <p>This option only affects decentralized counters used for
+ the counters that are keeping track of the memory consumption
+ and the number of terms in ETS tables of type ordered_set with
+ the write_concurrency option activated.</p>
+ </item>
<tag><marker id="+e"/><c><![CDATA[+e Number]]></c></tag>
<item>
<p>Sets the maximum number of ETS tables. This limit is
diff --git a/erts/doc/src/erl_driver.xml b/erts/doc/src/erl_driver.xml
index 58678f2393..3e2d3bb447 100644
--- a/erts/doc/src/erl_driver.xml
+++ b/erts/doc/src/erl_driver.xml
@@ -230,7 +230,7 @@
<item>
<p>With these functions, the driver sends data back to the emulator.
The data is received as messages by the port owner process, see
- <seealso marker="erlang:open_port/2">
+ <seealso marker="erlang#open_port/2">
<c>erlang:open_port/2</c></seealso>. The vector function and the
function taking a driver binary are faster, as they avoid
copying the data buffer. There is also a fast way of sending
@@ -1154,27 +1154,27 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code>
<taglist>
<tag><seealso marker="driver_entry#start">
<c>start</c></seealso></tag>
- <item>Called from <seealso marker="erlang:open_port/2">
+ <item>Called from <seealso marker="erlang#open_port/2">
<c>erlang:open_port/2</c></seealso>.</item>
<tag><seealso marker="driver_entry#output">
<c>output</c></seealso></tag>
- <item>Called from <seealso marker="erlang:send/2">
+ <item>Called from <seealso marker="erlang#send/2">
<c>erlang:send/2</c></seealso> and
- <seealso marker="erlang:port_command/2">
+ <seealso marker="erlang#port_command/2">
<c>erlang:port_command/2</c></seealso>.</item>
<tag><seealso marker="driver_entry#outputv">
<c>outputv</c></seealso></tag>
- <item>Called from <seealso marker="erlang:send/2">
+ <item>Called from <seealso marker="erlang#send/2">
<c>erlang:send/2</c></seealso> and
- <seealso marker="erlang:port_command/2">
+ <seealso marker="erlang#port_command/2">
<c>erlang:port_command/2</c></seealso>.</item>
<tag><seealso marker="driver_entry#control">
<c>control</c></seealso></tag>
- <item>Called from <seealso marker="erlang:port_control/3">
+ <item>Called from <seealso marker="erlang#port_control/3">
<c>erlang:port_control/3</c></seealso>.</item>
<tag><seealso marker="driver_entry#call">
<c>call</c></seealso></tag>
- <item>Called from <seealso marker="erlang:port_call/3">
+ <item>Called from <seealso marker="erlang#port_call/3">
<c>erlang:port_call/3</c></seealso>.</item>
</taglist>
<p>Notice that this function is <em>not</em> thread-safe, not
@@ -2305,7 +2305,7 @@ r = driver_async(myPort, &myKey, myData, myFunc); ]]></code>
<c>*value_size</c> has been set to the buffer size needed.</p>
<warning>
<p>This function reads the emulated environment used by
- <seealso marker="os:getenv/1"><c>os:getenv/1</c></seealso> and not
+ <seealso marker="kernel:os#getenv/1"><c>os:getenv/1</c></seealso> and not
the environment used by libc's <c>getenv(3)</c> or similar. Drivers
that <em>require</em> that these are in sync will need to do so
themselves, but keep in mind that they are segregated for a reason;
@@ -2656,7 +2656,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]
</note>
<warning>
<p>This function modifies the emulated environment used by
- <seealso marker="os:putenv/2"><c>os:putenv/2</c></seealso> and not
+ <seealso marker="kernel:os#putenv/2"><c>os:putenv/2</c></seealso> and not
the environment used by libc's <c>putenv(3)</c> or similar. Drivers
that <em>require</em> that these are in sync will need to do so
themselves, but keep in mind that they are segregated for a reason;
@@ -2849,7 +2849,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]
<desc>
<marker id="erl_drv_set_os_pid"></marker>
<p>Sets the <c>os_pid</c> seen when doing
- <seealso marker="erlang:port_info/2">
+ <seealso marker="erlang#port_info/2">
<c>erlang:port_info/2</c></seealso> on this port.</p>
<p><c>port</c> is the port handle of the port (driver instance) to set
the pid on. <c>pid</c>is the pid to set.</p>
@@ -3204,7 +3204,7 @@ erl_drv_output_term(driver_mk_port(drvport), spec, sizeof(spec) / sizeof(spec[0]
<c>control</c></seealso> driver entry
function will return data to the port owner process.
(The <c>control</c> function is called from
- <seealso marker="erlang:port_control/3">
+ <seealso marker="erlang#port_control/3">
<c>erlang:port_control/3</c></seealso>.)</p>
<p>Currently there are only two meaningful values for
<c>flags</c>: <c>0</c> means that data is returned in a list,
diff --git a/erts/doc/src/erl_ext_dist.xml b/erts/doc/src/erl_ext_dist.xml
index 4721747097..a6bc44b8c8 100644
--- a/erts/doc/src/erl_ext_dist.xml
+++ b/erts/doc/src/erl_ext_dist.xml
@@ -381,44 +381,6 @@
</section>
<section>
- <marker id="REFERENCE_EXT"/>
- <title>REFERENCE_EXT</title>
- <table align="left">
- <row>
- <cell align="center">1</cell>
- <cell align="center">N</cell>
- <cell align="center">4</cell>
- <cell align="center">1</cell>
- </row>
- <row>
- <cell align="center"><c>101</c></cell>
- <cell align="center"><c>Node</c></cell>
- <cell align="center"><c>ID</c></cell>
- <cell align="center"><c>Creation</c></cell>
- </row>
- <tcaption>REFERENCE_EXT</tcaption></table>
- <p>
- Encodes a reference object (an object generated with
- <seealso marker="erlang:make_ref/0">erlang:make_ref/0</seealso>).
- The <c>Node</c> term is an encoded atom, that is,
- <seealso marker="#ATOM_UTF8_EXT"><c>ATOM_UTF8_EXT</c></seealso>,
- <seealso marker="#SMALL_ATOM_UTF8_EXT"><c>SMALL_ATOM_UTF8_EXT</c></seealso>, or
- <seealso marker="#ATOM_CACHE_REF"><c>ATOM_CACHE_REF</c></seealso>.
- The <c>ID</c> field contains a big-endian unsigned integer,
- but <em>is to be regarded as uninterpreted data</em>,
- as this field is node-specific.
- <c>Creation</c> is a byte containing a node serial number, which
- makes it possible to separate old (crashed) nodes from a new one.
- </p>
- <p>
- In <c>ID</c>, only 18 bits are significant; the rest are to be 0.
- In <c>Creation</c>, only two bits are significant; the rest are to be 0.
- See <seealso marker="#NEW_REFERENCE_EXT">
- <c>NEW_REFERENCE_EXT</c></seealso>.
- </p>
- </section>
-
- <section>
<marker id="PORT_EXT"/>
<title>PORT_EXT</title>
<table align="left">
@@ -436,13 +398,46 @@
</row>
<tcaption>PORT_EXT</tcaption></table>
<p>
- Encodes a port object (obtained from
- <seealso marker="erlang:open_port/2">
- <c>erlang:open_port/2</c></seealso>).
- The <c>ID</c> is a node-specific identifier for a local port.
+ Same as <seealso marker="#NEW_PORT_EXT"><c>NEW_PORT_EXT</c></seealso>
+ except the <c>Creation</c> field is only one byte and only two
+ bits are significant, the rest are to be 0.
+ </p>
+ </section>
+
+ <section>
+ <marker id="NEW_PORT_EXT"/>
+ <title>NEW_PORT_EXT</title>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">N</cell>
+ <cell align="center">4</cell>
+ <cell align="center">4</cell>
+ </row>
+ <row>
+ <cell align="center"><c>89</c></cell>
+ <cell align="center"><c>Node</c></cell>
+ <cell align="center"><c>ID</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ </row>
+ <tcaption>NEW_PORT_EXT</tcaption></table>
+ <p>
+ Encodes a port identifier (obtained from
+ <seealso marker="erlang#open_port/2"><c>erlang:open_port/2</c></seealso>).
+ <c>Node</c> is an encoded atom, that is,
+ <seealso marker="#ATOM_UTF8_EXT"><c>ATOM_UTF8_EXT</c></seealso>,
+ <seealso marker="#SMALL_ATOM_UTF8_EXT"><c>SMALL_ATOM_UTF8_EXT</c></seealso>
+ or <seealso marker="#ATOM_CACHE_REF"><c>ATOM_CACHE_REF</c></seealso>.
+ <c>ID</c> is a 32-bit big endian unsigned integer. Only 28 bits are
+ significant; the rest are to be 0. The <c>Creation</c> works just like in
+ <seealso marker="#NEW_PID_EXT"><c>NEW_PID_EXT</c></seealso>.
Port operations are not allowed across node boundaries.
- The <c>Creation</c> works just like in
- <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>.
+ </p>
+ <p>Introduced in OTP 19, but only to be decoded and echoed back. Not
+ encoded for local ports. Planned to supersede <seealso marker="#PORT_EXT">
+ <c>PORT_EXT</c></seealso> in OTP 23 when
+ <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_BIG_CREATON</c></seealso>
+ becomes mandatory.
</p>
</section>
@@ -466,12 +461,65 @@
</row>
<tcaption>PID_EXT</tcaption></table>
<p>
- Encodes a process identifier object (obtained from
- <seealso marker="erlang:spawn/3"><c>erlang:spawn/3</c></seealso> or
- friends). The <c>ID</c> and <c>Creation</c> fields works just like in
- <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>, while
- the <c>Serial</c> field is used to improve safety.
- In <c>ID</c>, only 15 bits are significant; the rest are to be 0.
+ Same as <seealso marker="#NEW_PID_EXT"><c>NEW_PID_EXT</c></seealso>
+ except the <c>Creation</c> field is only one byte and only two
+ bits are significant, the rest are to be 0.
+ </p>
+ </section>
+
+ <section>
+ <marker id="NEW_PID_EXT"/>
+ <title>NEW_PID_EXT</title>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">N</cell>
+ <cell align="center">4</cell>
+ <cell align="center">4</cell>
+ <cell align="center">4</cell>
+ </row>
+ <row>
+ <cell align="center"><c>88</c></cell>
+ <cell align="center"><c>Node</c></cell>
+ <cell align="center"><c>ID</c></cell>
+ <cell align="center"><c>Serial</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ </row>
+ <tcaption>NEW_PID_EXT</tcaption></table>
+ <p>
+ Encodes an Erlang process identifier object.
+ </p>
+ <taglist>
+ <tag><c>Node</c></tag>
+ <item><p>The name of the originating node, encoded using
+ <seealso marker="#ATOM_UTF8_EXT"><c>ATOM_UTF8_EXT</c></seealso>,
+ <seealso marker="#SMALL_ATOM_UTF8_EXT"><c>SMALL_ATOM_UTF8_EXT</c></seealso>
+ or <seealso
+ marker="#ATOM_CACHE_REF"><c>ATOM_CACHE_REF</c></seealso>.</p>
+ </item>
+ <tag><c>ID</c></tag>
+ <item><p>A 32-bit big endian unsigned integer. Only 15 bits are
+ significant; the rest are to be 0.</p>
+ </item>
+ <tag><c>Serial</c></tag>
+ <item><p>A 32-bit big endian unsigned integer. Only 13 bits are
+ significant; the rest are to be 0.</p>
+ </item>
+ <tag><c>Creation</c></tag>
+ <item><p>A 32-bit big endian unsigned integer. All identifiers
+ originating from the same node incarnation must have identical <c>Creation</c>
+ values. This makes it possible to separate identifiers from old
+ (crashed) nodes from a new one. The value zero should be avoided for
+ normal operations as it is used as a wild card for debug purpose
+ (like a pid returned by <seealso marker="erts:erlang#list_to_pid/1">
+ erlang:list_to_pid/1</seealso>).</p>
+ </item>
+ </taglist>
+ <p>Introduced in OTP 19, but only to be decoded and echoed back. Not
+ encoded for local processes. Planned to supersede <seealso marker="#PID_EXT">
+ <c>PID_EXT</c></seealso> in OTP 23 when
+ <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_BIG_CREATON</c></seealso>
+ becomes mandatory.
</p>
</section>
@@ -695,6 +743,30 @@
</section>
<section>
+ <marker id="REFERENCE_EXT"/>
+ <title>REFERENCE_EXT (deprecated)</title>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">N</cell>
+ <cell align="center">4</cell>
+ <cell align="center">1</cell>
+ </row>
+ <row>
+ <cell align="center"><c>101</c></cell>
+ <cell align="center"><c>Node</c></cell>
+ <cell align="center"><c>ID</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ </row>
+ <tcaption>REFERENCE_EXT</tcaption></table>
+ <p>
+ The same as <seealso marker="#NEW_REFERENCE_EXT">
+ <c>NEW_REFERENCE_EXT</c></seealso> except <c>ID</c> is only one word
+ (<c>Len</c> = 1).
+ </p>
+ </section>
+
+ <section>
<marker id="NEW_REFERENCE_EXT"/>
<title>NEW_REFERENCE_EXT</title>
<table align="left">
@@ -714,29 +786,68 @@
</row>
<tcaption>NEW_REFERENCE_EXT</tcaption></table>
<p>
- <c>Node</c> and <c>Creation</c> are as in
- <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>.
- </p>
- <p>
- <c>ID</c> contains a sequence of big-endian unsigned integers
- (4 bytes each, so <c>N'</c> is a multiple of 4),
- but is to be regarded as uninterpreted data.
- </p>
- <p>
- <c>N'</c> = 4 * <c>Len</c>.
- </p>
- <p>
- In the first word (4 bytes) of <c>ID</c>, only 18 bits are
- significant, the rest are to be 0.
- In <c>Creation</c>, only two bits are significant,
- the rest are to be 0.
+ The same as <seealso marker="#NEWER_REFERENCE_EXT">
+ <c>NEWER_REFERENCE_EXT</c></seealso> <em>except</em>:
</p>
+ <taglist>
+ <tag><c>ID</c></tag>
+ <item><p>In the first word (4 bytes) of <c>ID</c>, only 18 bits are
+ significant, the rest must be 0.</p>
+ </item>
+ <tag><c>Creation</c></tag>
+ <item><p>Only one byte long and only two bits are significant, the rest must be 0.</p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <marker id="NEWER_REFERENCE_EXT"/>
+ <title>NEWER_REFERENCE_EXT</title>
+ <table align="left">
+ <row>
+ <cell align="center">1</cell>
+ <cell align="center">2</cell>
+ <cell align="center">N</cell>
+ <cell align="center">4</cell>
+ <cell align="center">N'</cell>
+ </row>
+ <row>
+ <cell align="center"><c>90</c></cell>
+ <cell align="center"><c>Len</c></cell>
+ <cell align="center"><c>Node</c></cell>
+ <cell align="center"><c>Creation</c></cell>
+ <cell align="center"><c>ID ...</c></cell>
+ </row>
+ <tcaption>NEWER_REFERENCE_EXT</tcaption></table>
<p>
- <c>NEW_REFERENCE_EXT</c> was introduced with distribution version 4.
- In version 4, <c>N'</c> is to be at most 12.
+ Encodes a reference term generated with
+ <seealso marker="erts:erlang#make_ref/0">erlang:make_ref/0</seealso>.
</p>
- <p>
- See <seealso marker="#REFERENCE_EXT"><c>REFERENCE_EXT</c></seealso>.
+ <taglist>
+ <tag><c>Node</c></tag>
+ <item><p>The name of the originating node, encoded using
+ <seealso marker="#ATOM_UTF8_EXT"><c>ATOM_UTF8_EXT</c></seealso>,
+ <seealso marker="#SMALL_ATOM_UTF8_EXT"><c>SMALL_ATOM_UTF8_EXT</c></seealso>
+ or <seealso marker="#ATOM_CACHE_REF"><c>ATOM_CACHE_REF</c></seealso>.</p>
+ </item>
+ <tag><c>Len</c></tag>
+ <item><p>A 16-bit big endian unsigned integer not larger than 3.</p>
+ </item>
+ <tag><c>ID</c></tag>
+ <item><p>A sequence of <c>Len</c> big-endian unsigned integers
+ (4 bytes each, so <c>N'</c>&nbsp;=&nbsp;4&nbsp;*&nbsp;<c>Len</c>),
+ but is to be regarded as uninterpreted data.</p>
+ </item>
+ <tag><c>Creation</c></tag>
+ <item><p>Works just like in
+ <seealso marker="#NEW_PID_EXT"><c>NEW_PID_EXT</c></seealso>.</p>
+ </item>
+ </taglist>
+ <p>Introduced in OTP 19, but only to be decoded and echoed back. Not
+ encoded for local references. Planned to supersede <seealso marker="#NEW_REFERENCE_EXT">
+ <c>NEW_REFERENCE_EXT</c></seealso> in OTP 23 when
+ <seealso marker="erl_dist_protocol#dflags"><c>DFLAG_BIG_CREATON</c></seealso>
+ becomes mandatory.
</p>
</section>
diff --git a/erts/doc/src/erl_nif.xml b/erts/doc/src/erl_nif.xml
index cf1994887a..c0be715678 100644
--- a/erts/doc/src/erl_nif.xml
+++ b/erts/doc/src/erl_nif.xml
@@ -546,8 +546,8 @@ int writeiovec(ErlNifEnv *env, ERL_NIF_TERM term, ERL_NIF_TERM *tail,
<p>Many operations communicating with a process executing a
dirty NIF can, however, complete while it executes the
dirty NIF. For example, retrieving information about it through
- <seealso marker="erlang:process_info/1">
- <c>erlang:process_info</c></seealso>, setting its group leader,
+ <seealso marker="erlang#process_info/1">
+ <c>process_info</c></seealso>, setting its group leader,
register/unregister its name, and so on.</p>
<p>Termination of a process executing a dirty NIF can only be
completed up to a certain point while it executes the dirty NIF.
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 0d94f83493..2b444ccf01 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -126,7 +126,7 @@
instance.</p>
<p>One can get an approximation of the <c>native</c>
time unit by calling
- <seealso marker="erlang:convert_time_unit/3">
+ <seealso marker="#convert_time_unit/3">
<c>erlang:convert_time_unit(1, second, native)</c></seealso>.
The result equals the number
of whole <c>native</c> time units per second. If
@@ -4663,6 +4663,7 @@ RealSystem = system + MissedSystem</code>
<name name="port_info" arity="2" clause_i="6" since="OTP R16B"/>
<fsummary>Information about the memory size of a port.</fsummary>
<desc>
+ <marker id="port_info_memory"/>
<p><c><anno>Bytes</anno></c> is the total number of
bytes allocated for this port by the runtime system. The
port itself can have allocated memory that is not
@@ -5387,6 +5388,7 @@ RealSystem = system + MissedSystem</code>
</item>
<tag><c>{memory, <anno>Size</anno>}</c></tag>
<item>
+ <marker id="process_info_memory"/>
<p><c><anno>Size</anno></c> is the size in bytes of the process.
This includes call stack, heap, and internal structures.</p>
</item>
@@ -6676,7 +6678,7 @@ lists:map(
<tag><c>async</c></tag>
<item>Async threads are used by various linked-in drivers (mainly the
file drivers) do offload non-CPU intensive work. See
- <seealso marker="erts:erl#+async_thread_pool_size">erl +A</seealso> for more details.</item>
+ <seealso marker="erts:erl#async_thread_pool_size">erl +A</seealso> for more details.</item>
<tag><c>aux</c></tag>
<item>Takes care of any work that is not
specifically assigned to a scheduler.</item>
diff --git a/erts/doc/src/internal.xml b/erts/doc/src/internal.xml
new file mode 100644
index 0000000000..88609d492a
--- /dev/null
+++ b/erts/doc/src/internal.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<internal xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header>
+ <copyright>
+ <year>2018</year><year>2018</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ </legalnotice>
+
+ <title>ERTS Internal Documentation</title>
+ <prepared>Lukas Larsson</prepared>
+ <docno></docno>
+ <date>2018-07-07</date>
+ <rev>4.5.2</rev>
+ <file>internal.xml</file>
+ </header>
+ <xi:include href="CarrierMigration.xml"/>
+ <xi:include href="ThreadProgress.xml"/>
+ <xi:include href="CodeLoading.xml"/>
+ <xi:include href="Tracing.xml"/>
+ <xi:include href="DelayedDealloc.xml"/>
+ <xi:include href="beam_makeops.xml"/>
+ <xi:include href="CountingInstructions.xml"/>
+ <xi:include href="GarbageCollection.xml"/>
+ <xi:include href="PTables.xml"/>
+ <xi:include href="PortSignals.xml"/>
+ <xi:include href="ProcessManagementOptimizations.xml"/>
+ <xi:include href="SuperCarrier.xml"/>
+</internal>
diff --git a/erts/doc/src/notes.xml b/erts/doc/src/notes.xml
index 248b871ca0..da470b51ec 100644
--- a/erts/doc/src/notes.xml
+++ b/erts/doc/src/notes.xml
@@ -31,6 +31,68 @@
</header>
<p>This document describes the changes made to the ERTS application.</p>
+<section><title>Erts 10.3.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Add missing documentation of new external tags
+ <c>NEW_PID</c>, <c>NEW_PORT</c> and
+ <c>NEWER_REFERENCE</c> introduced in OTP 19.</p> <p>These
+ new tags are planned to be "activated" in OTP 23 when
+ distribution capability flag <c>DFLAG_BIG_CREATION</c>
+ becomes mandatory. Older nodes (>= 19) are able to decode
+ these new tags and send them back to the new node. Nodes
+ older than OTP 23 will however never encode their own
+ local pids, ports and references using the new tags.</p>
+ <p>
+ Own Id: OTP-15766</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.3.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fix <c>erlang:open_port/2</c> with the <c>fd</c> option
+ to correctly cleanup the pollset when the port is closed.
+ Before this fix there would be error printouts sent to
+ logger when the same fd was reused in a new port.</p>
+ <p>
+ Own Id: OTP-15753 Aux Id: ERL-900 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>Erts 10.3.2</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixed a bug in <c>seq_trace:reset_trace/0</c> that
+ could crash the emulator.</p>
+ <p>
+ Own Id: OTP-15704</p>
+ </item>
+ <item>
+ <p>
+ Fixed bug in <c>process_info(reductions)</c> causing it
+ to sometimes return invalid results.</p>
+ <p>
+ Own Id: OTP-15709 Aux Id: ERIERL-337 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 10.3.1</title>
<section><title>Fixed Bugs and Malfunctions</title>
@@ -1960,6 +2022,62 @@
</section>
+<section><title>Erts 9.3.3.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>Fixes of install/release phase in build system.</p>
+ <list> <item>The source tree was modified when
+ installing/releasing and/or applying a patch.</item>
+ <item>Some files were installed with wrong access
+ rights.</item> <item>If applying a patch (using
+ <c>otp_patch_apply</c>) as another user (except root)
+ than the user that built the source, the documentation
+ was not properly updated.</item> </list>
+ <p>
+ Own Id: OTP-15551</p>
+ </item>
+ <item>
+ <p>
+ Minor fixes for <c>make clean</c>.</p>
+ <p>
+ Own Id: OTP-15657</p>
+ </item>
+ <item>
+ <p>
+ Fixed a bug in all <c>ets:select*</c> and
+ <c>ets:match*</c> functions that could in some rare cases
+ lead to very poor performance.</p>
+ <p>
+ Own Id: OTP-15660 Aux Id: ERL-869 </p>
+ </item>
+ <item>
+ <p>
+ Fix a possible deadlock when terminating the ERTS caused
+ by a dirty scheduler not releasing it's run-queue lock
+ when terminating.</p>
+ <p>
+ Own Id: OTP-15690 Aux Id: PR-2172 </p>
+ </item>
+ <item>
+ <p>Add missing documentation of new external tags
+ <c>NEW_PID</c>, <c>NEW_PORT</c> and
+ <c>NEWER_REFERENCE</c> introduced in OTP 19.</p> <p>These
+ new tags are planned to be "activated" in OTP 23 when
+ distribution capability flag <c>DFLAG_BIG_CREATION</c>
+ becomes mandatory. Older nodes (>= 19) are able to decode
+ these new tags and send them back to the new node. Nodes
+ older than OTP 23 will however never encode their own
+ local pids, ports and references using the new tags.</p>
+ <p>
+ Own Id: OTP-15766</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
<section><title>Erts 9.3.3.9</title>
<section><title>Improvements and New Features</title>
@@ -8341,8 +8459,7 @@
Erlang/OTP has been ported to the realtime operating
system OSE. The port supports both smp and non-smp
emulator. For details around the port and how to started
- see the User's Guide in the <seealso
- marker="ose:ose_intro">ose</seealso> application. </p>
+ see the User's Guide in the <em>ose</em> application.</p>
<p>
Note that not all parts of Erlang/OTP has been ported. </p>
<p>
@@ -9448,9 +9565,9 @@
<c>fix_alloc</c> allocator, a different strategy for
management of fix blocks will be used.</item> <item>The
information returned from <seealso
- marker="erlang:system_info_allocator_tuple"><c>erlang:system_info({allocator,
+ marker="erlang#system_info_allocator_tuple"><c>erlang:system_info({allocator,
A})</c></seealso>, and <seealso
- marker="erlang:system_info_allocator_sizes"><c>erlang:system_info({allocator_sizes,
+ marker="erlang#system_info_allocator_sizes"><c>erlang:system_info({allocator_sizes,
A})</c></seealso> will be slightly different when this
feature has been enabled. An <c>mbcs_pool</c> tuple will
be present giving information about abandoned carriers,
@@ -10259,8 +10376,7 @@
information about signal ordering guarantees, see the
chapter on <seealso
marker="erts:communication">communication</seealso> in
- the ERTS user's guide. The <seealso
- marker="erts:erl#+n">+n</seealso> command line flag of
+ the ERTS user's guide. The <c>+n</c> command line flag of
<seealso marker="erts:erl">erl(1)</seealso> can be
helpful when trying to find signaling order bugs in
Erlang code that have been exposed by these
diff --git a/erts/emulator/Makefile.in b/erts/emulator/Makefile.in
index 21351df656..448f41b523 100644
--- a/erts/emulator/Makefile.in
+++ b/erts/emulator/Makefile.in
@@ -894,7 +894,7 @@ RUN_OBJS += \
$(OBJDIR)/erl_ptab.o $(OBJDIR)/erl_map.o \
$(OBJDIR)/erl_msacc.o $(OBJDIR)/erl_lock_flags.o \
$(OBJDIR)/erl_io_queue.o $(OBJDIR)/erl_db_catree.o \
- $(ESOCK_RUN_OBJS)
+ $(ESOCK_RUN_OBJS) $(OBJDIR)/erl_flxctr.o
LTTNG_OBJS = $(OBJDIR)/erlang_lttng.o
diff --git a/erts/emulator/beam/arith_instrs.tab b/erts/emulator/beam/arith_instrs.tab
index 5f23b2c168..f14b376419 100644
--- a/erts/emulator/beam/arith_instrs.tab
+++ b/erts/emulator/beam/arith_instrs.tab
@@ -51,11 +51,50 @@ plus.fetch(Op1, Op2) {
plus.execute(Fail, Dst) {
if (ERTS_LIKELY(is_both_small(PlusOp1, PlusOp2))) {
+#ifdef HAVE_OVERFLOW_CHECK_BUILTINS
+ Sint lhs_tagged, rhs_untagged, res;
+
+ /* The value part of immediate integers start right after the tag and
+ * occupy the rest of the word, so if you squint a bit they look like
+ * fixed-point integers; as long as you mask the tag away you will get
+ * correct results from addition/subtraction since they share the same
+ * notion of zero. It's fairly easy to see that the following holds
+ * when (a + b) is in range:
+ *
+ * (a >> s) + (b >> s) == ((a & ~m) + (b & ~m)) >> s
+ *
+ * Where 's' is the tag size and 'm' is the tag mask.
+ *
+ * The left-hand side is our fallback in the #else clause and is the
+ * fastest way to do this safely in plain C. The actual addition will
+ * never overflow since `Sint` has a much greater range than our
+ * smalls, so we can use the IS_SSMALL macro to see if the result is
+ * within range.
+ *
+ * What we're doing below is an extension of the right-hand side. By
+ * treating `a` and `b` as fixed-point integers, all additions whose
+ * result is out of range will also overflow `Sint` and we can use the
+ * compiler's overflow intrinsics to check for this condition.
+ *
+ * In addition, since the tag lives in the lowest bits we can further
+ * optimize this by only stripping the tag from either side. The higher
+ * bits can't influence the tag bits since we bail on overflow, so the
+ * tag bits from the tagged side will simply appear in the result. */
+ lhs_tagged = PlusOp1;
+ rhs_untagged = PlusOp2 & ~_TAG_IMMED1_MASK;
+
+ if (ERTS_LIKELY(!__builtin_add_overflow(lhs_tagged, rhs_untagged, &res))) {
+ ASSERT(is_small(res));
+ $Dst = res;
+ $NEXT0();
+ }
+#else
Sint i = signed_val(PlusOp1) + signed_val(PlusOp2);
if (ERTS_LIKELY(IS_SSMALL(i))) {
$Dst = make_small(i);
$NEXT0();
}
+#endif
}
$OUTLINED_ARITH_2($Fail, mixed_plus, BIF_splus_2, PlusOp1, PlusOp2, $Dst);
}
@@ -73,11 +112,26 @@ minus.fetch(Op1, Op2) {
minus.execute(Fail, Dst) {
if (ERTS_LIKELY(is_both_small(MinusOp1, MinusOp2))) {
+#ifdef HAVE_OVERFLOW_CHECK_BUILTINS
+ Sint lhs_tagged, rhs_untagged, res;
+
+ /* See plus.execute */
+ lhs_tagged = MinusOp1;
+ rhs_untagged = MinusOp2 & ~_TAG_IMMED1_MASK;
+
+ if (ERTS_LIKELY(!__builtin_sub_overflow(lhs_tagged, rhs_untagged, &res))) {
+ ASSERT(is_small(res));
+ $Dst = res;
+ $NEXT0();
+ }
+#else
Sint i = signed_val(MinusOp1) - signed_val(MinusOp2);
+
if (ERTS_LIKELY(IS_SSMALL(i))) {
$Dst = make_small(i);
$NEXT0();
}
+#endif
}
$OUTLINED_ARITH_2($Fail, mixed_minus, BIF_sminus_2, MinusOp1, MinusOp2, $Dst);
}
@@ -97,12 +151,27 @@ increment.execute(IncrementVal, Dst) {
Eterm result;
if (ERTS_LIKELY(is_small(increment_reg_val))) {
+#ifdef HAVE_OVERFLOW_CHECK_BUILTINS
+ Sint lhs_tagged, rhs_untagged, res;
+
+ /* See plus.execute */
+ lhs_tagged = increment_reg_val;
+ rhs_untagged = (Sint)increment_val << _TAG_IMMED1_SIZE;
+
+ if (ERTS_LIKELY(!__builtin_add_overflow(lhs_tagged, rhs_untagged, &res))) {
+ ASSERT(is_small(res));
+ $Dst = res;
+ $NEXT0();
+ }
+#else
Sint i = signed_val(increment_reg_val) + increment_val;
if (ERTS_LIKELY(IS_SSMALL(i))) {
$Dst = make_small(i);
$NEXT0();
}
+#endif
}
+
result = erts_mixed_plus(c_p, increment_reg_val, make_small(increment_val));
ERTS_HOLE_CHECK(c_p);
if (ERTS_LIKELY(is_value(result))) {
@@ -118,11 +187,15 @@ i_times(Fail, Op1, Op2, Dst) {
Eterm op2 = $Op2;
#ifdef HAVE_OVERFLOW_CHECK_BUILTINS
if (ERTS_LIKELY(is_both_small(op1, op2))) {
- Sint a = signed_val(op1);
- Sint b = signed_val(op2);
- Sint res;
- if (ERTS_LIKELY(!__builtin_mul_overflow(a, b, &res) && IS_SSMALL(res))) {
- $Dst = make_small(res);
+ /* See plus.execute */
+ Sint lhs_untagged, rhs_actual, res;
+
+ lhs_untagged = op1 & ~_TAG_IMMED1_MASK;
+ rhs_actual = signed_val(op2);
+
+ if (ERTS_LIKELY(!__builtin_mul_overflow(lhs_untagged, rhs_actual, &res))) {
+ ASSERT(!(res & _TAG_IMMED1_MASK));
+ $Dst = res | _TAG_IMMED1_SMALL;
$NEXT0();
}
}
diff --git a/erts/emulator/beam/beam_debug.c b/erts/emulator/beam/beam_debug.c
index 762c5da9be..4d52435139 100644
--- a/erts/emulator/beam/beam_debug.c
+++ b/erts/emulator/beam/beam_debug.c
@@ -353,6 +353,22 @@ erts_debug_disassemble_1(BIF_ALIST_1)
return TUPLE3(hp, addr, bin, mfa);
}
+BIF_RETTYPE
+erts_debug_interpreter_size_0(BIF_ALIST_0)
+{
+ int i;
+ BeamInstr low, high;
+
+ low = high = (BeamInstr) process_main;
+ for (i = 0; i < NUM_SPECIFIC_OPS; i++) {
+ BeamInstr a = BeamOpCodeAddr(i);
+ if (a > high) {
+ high = a;
+ }
+ }
+ return erts_make_integer(high - low, BIF_P);
+}
+
void
dbg_bt(Process* p, Eterm* sp)
{
diff --git a/erts/emulator/beam/beam_emu.c b/erts/emulator/beam/beam_emu.c
index f1d8609066..8e93e53003 100644
--- a/erts/emulator/beam/beam_emu.c
+++ b/erts/emulator/beam/beam_emu.c
@@ -375,44 +375,33 @@ do { \
/*
* process_main() is already huge, so we want to avoid inlining
- * into it. Especially functions that are seldom used.
+ * seldom used functions into it.
*/
-#ifdef __GNUC__
-# define NOINLINE __attribute__((__noinline__))
-#else
-# define NOINLINE
-#endif
-
-
-/*
- * The following functions are called directly by process_main().
- * Don't inline them.
- */
-static void init_emulator_finish(void) NOINLINE;
-static ErtsCodeMFA *ubif2mfa(void* uf) NOINLINE;
+static void init_emulator_finish(void) ERTS_NOINLINE;
+static ErtsCodeMFA *ubif2mfa(void* uf) ERTS_NOINLINE;
static BeamInstr* handle_error(Process* c_p, BeamInstr* pc,
- Eterm* reg, ErtsCodeMFA* bif_mfa) NOINLINE;
+ Eterm* reg, ErtsCodeMFA* bif_mfa) ERTS_NOINLINE;
static BeamInstr* call_error_handler(Process* p, ErtsCodeMFA* mfa,
- Eterm* reg, Eterm func) NOINLINE;
+ Eterm* reg, Eterm func) ERTS_NOINLINE;
static BeamInstr* fixed_apply(Process* p, Eterm* reg, Uint arity,
- BeamInstr *I, Uint offs) NOINLINE;
+ BeamInstr *I, Uint offs) ERTS_NOINLINE;
static BeamInstr* apply(Process* p, Eterm* reg,
- BeamInstr *I, Uint offs) NOINLINE;
+ BeamInstr *I, Uint offs) ERTS_NOINLINE;
static BeamInstr* call_fun(Process* p, int arity,
- Eterm* reg, Eterm args) NOINLINE;
+ Eterm* reg, Eterm args) ERTS_NOINLINE;
static BeamInstr* apply_fun(Process* p, Eterm fun,
- Eterm args, Eterm* reg) NOINLINE;
+ Eterm args, Eterm* reg) ERTS_NOINLINE;
static Eterm new_fun(Process* p, Eterm* reg,
- ErlFunEntry* fe, int num_free) NOINLINE;
+ ErlFunEntry* fe, int num_free) ERTS_NOINLINE;
static int is_function2(Eterm Term, Uint arity);
static Eterm erts_gc_new_map(Process* p, Eterm* reg, Uint live,
- Uint n, BeamInstr* ptr) NOINLINE;
+ Uint n, BeamInstr* ptr) ERTS_NOINLINE;
static Eterm erts_gc_new_small_map_lit(Process* p, Eterm* reg, Eterm keys_literal,
- Uint live, BeamInstr* ptr) NOINLINE;
+ Uint live, BeamInstr* ptr) ERTS_NOINLINE;
static Eterm erts_gc_update_map_assoc(Process* p, Eterm* reg, Uint live,
- Uint n, BeamInstr* new_p) NOINLINE;
+ Uint n, BeamInstr* new_p) ERTS_NOINLINE;
static Eterm erts_gc_update_map_exact(Process* p, Eterm* reg, Uint live,
- Uint n, Eterm* new_p) NOINLINE;
+ Uint n, Eterm* new_p) ERTS_NOINLINE;
static Eterm get_map_element(Eterm map, Eterm key);
static Eterm get_map_element_hash(Eterm map, Eterm key, Uint32 hx);
@@ -3267,20 +3256,23 @@ erts_is_builtin(Eterm Mod, Eterm Name, int arity)
/*
- * Return the current number of reductions for the given process.
+ * Return the current number of reductions consumed by the given process.
* To get the total number of reductions, p->reds must be added.
*/
Uint
-erts_current_reductions(Process *current, Process *p)
+erts_current_reductions(Process *c_p, Process *p)
{
- if (current != p) {
+ Sint reds_left;
+ if (c_p != p || !(erts_atomic32_read_nob(&c_p->state)
+ & ERTS_PSFLG_RUNNING)) {
return 0;
- } else if (current->fcalls < 0 && ERTS_PROC_GET_SAVED_CALLS_BUF(current)) {
- return current->fcalls + CONTEXT_REDS;
+ } else if (c_p->fcalls < 0 && ERTS_PROC_GET_SAVED_CALLS_BUF(c_p)) {
+ reds_left = c_p->fcalls + CONTEXT_REDS;
} else {
- return REDS_IN(current) - current->fcalls;
+ reds_left = c_p->fcalls;
}
+ return REDS_IN(c_p) - reds_left;
}
int
diff --git a/erts/emulator/beam/bif.c b/erts/emulator/beam/bif.c
index d0e2d9afc2..b81056c774 100644
--- a/erts/emulator/beam/bif.c
+++ b/erts/emulator/beam/bif.c
@@ -1915,7 +1915,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm return_term, Eterm *refp,
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
erts_dsprintf(dsbufp,
"Discarding message %T from %T to %T in an old "
- "incarnation (%u) of this node (%u)\n",
+ "incarnation (%d) of this node (%d)\n",
msg,
p->common.id,
to,
@@ -1959,7 +1959,7 @@ do_send(Process *p, Eterm to, Eterm msg, Eterm return_term, Eterm *refp,
erts_dsprintf_buf_t *dsbufp = erts_create_logger_dsbuf();
erts_dsprintf(dsbufp,
"Discarding message %T from %T to %T in an old "
- "incarnation (%u) of this node (%u)\n",
+ "incarnation (%d) of this node (%d)\n",
msg,
p->common.id,
to,
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 34a0be4f2d..db9c258cb7 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -437,6 +437,7 @@ bif erts_debug:set_internal_state/2
bif erts_debug:display/1
bif erts_debug:dist_ext_to_term/2
bif erts_debug:instructions/0
+bif erts_debug:interpreter_size/0
bif erts_debug:dirty_cpu/2
bif erts_debug:dirty_io/2
bif erts_debug:dirty/3
diff --git a/erts/emulator/beam/big.c b/erts/emulator/beam/big.c
index 7666f23a4f..522f50287a 100644
--- a/erts/emulator/beam/big.c
+++ b/erts/emulator/beam/big.c
@@ -2176,24 +2176,6 @@ term_to_Uint64(Eterm term, Uint64 *up)
#endif
}
-int
-term_to_Uint32(Eterm term, Uint32 *up)
-{
-#if ERTS_SIZEOF_ETERM == 4
- return term_to_Uint(term,up);
-#else
- if (is_small(term)) {
- Sint i = signed_val(term);
- if (i >= 0) {
- *up = (Uint32) i;
- return 1;
- }
- }
- *up = BADARG;
- return 0;
-#endif
-}
-
int term_to_Sint(Eterm term, Sint *sp)
{
diff --git a/erts/emulator/beam/big.h b/erts/emulator/beam/big.h
index 3fed076419..ad19cce395 100644
--- a/erts/emulator/beam/big.h
+++ b/erts/emulator/beam/big.h
@@ -168,8 +168,6 @@ Eterm erts_uint64_array_to_big(Uint **, int, int, Uint64 *);
int term_to_Uint64(Eterm, Uint64*);
int term_to_Sint64(Eterm, Sint64*);
#endif
-int term_to_Uint32(Eterm, Uint32*);
-
Uint32 big_to_uint32(Eterm b);
int term_equals_2pow32(Eterm);
diff --git a/erts/emulator/beam/break.c b/erts/emulator/beam/break.c
index 06d8c7cda8..80e871aaf6 100644
--- a/erts/emulator/beam/break.c
+++ b/erts/emulator/beam/break.c
@@ -394,8 +394,12 @@ print_process_info(fmtfn_t to, void *to_arg, Process *p, ErtsProcLocks orig_lock
erts_print(to, to_arg, "OldBinVHeap: %b64u\n", BIN_OLD_VHEAP(p));
erts_print(to, to_arg, "BinVHeap unused: %b64u\n",
BIN_VHEAP_SZ(p) - p->off_heap.overhead);
- erts_print(to, to_arg, "OldBinVHeap unused: %b64u\n",
- BIN_OLD_VHEAP_SZ(p) - BIN_OLD_VHEAP(p));
+ if (BIN_OLD_VHEAP_SZ(p) >= BIN_OLD_VHEAP(p)) {
+ erts_print(to, to_arg, "OldBinVHeap unused: %b64u\n",
+ BIN_OLD_VHEAP_SZ(p) - BIN_OLD_VHEAP(p));
+ } else {
+ erts_print(to, to_arg, "OldBinVHeap unused: overflow\n");
+ }
erts_print(to, to_arg, "Memory: %beu\n", erts_process_memory(p, !0));
if (garbing) {
diff --git a/erts/emulator/beam/bs_instrs.tab b/erts/emulator/beam/bs_instrs.tab
index 9cad2b03c5..bd1ad91e45 100644
--- a/erts/emulator/beam/bs_instrs.tab
+++ b/erts/emulator/beam/bs_instrs.tab
@@ -125,7 +125,7 @@ BS_GET_UNCHECKED_FIELD_SIZE(Bits, Unit, Fail, Dst) {
TEST_BIN_VHEAP(VNh, Nh, Live) {
Uint need = $Nh;
if (E - HTOP < need || MSO(c_p).overhead + $VNh >= BIN_VHEAP_SZ(c_p)) {
- SWAPOUT;
+ $GC_SWAPOUT();
PROCESS_MAIN_CHK_LOCKS(c_p);
FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, $Live, FCALLS);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index f9708304d4..27599f38e0 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -775,19 +775,25 @@ void init_dist(void)
static ERTS_INLINE ErtsDistOutputBuf *
alloc_dist_obuf(Uint size, Uint headers)
{
- int i;
+ Uint obuf_size = sizeof(ErtsDistOutputBuf)*(headers);
ErtsDistOutputBuf *obuf;
- Uint obuf_size = sizeof(ErtsDistOutputBuf)*(headers) +
- sizeof(byte)*size;
- Binary *bin = erts_bin_drv_alloc(obuf_size);
- obuf = (ErtsDistOutputBuf *) &bin->orig_bytes[size];
+ Binary *bin;
+ byte *extp;
+ int i;
+
+ bin = erts_bin_drv_alloc(obuf_size + size);
erts_refc_add(&bin->intern.refc, headers - 1, 1);
+
+ obuf = (ErtsDistOutputBuf *)&bin->orig_bytes[0];
+ extp = (byte *)&bin->orig_bytes[obuf_size];
+
for (i = 0; i < headers; i++) {
obuf[i].bin = bin;
- obuf[i].extp = (byte *)&bin->orig_bytes[0];
+ obuf[i].extp = extp;
#ifdef DEBUG
obuf[i].dbg_pattern = ERTS_DIST_OUTPUT_BUF_DBG_PATTERN;
- obuf[i].alloc_endp = obuf->extp + size;
+ obuf[i].ext_startp = extp;
+ obuf[i].alloc_endp = &extp[size];
ASSERT(bin == ErtsDistOutputBuf2Binary(obuf));
#endif
}
@@ -1360,7 +1366,7 @@ erts_dist_seq_tree_foreach_delete_yielding(DistSeqNode **root,
limit);
if (res > 0) {
if (ysp != &ys)
- erts_free(ERTS_ALC_T_ML_YIELD_STATE, ysp);
+ erts_free(ERTS_ALC_T_SEQ_YIELD_STATE, ysp);
*vyspp = NULL;
}
else {
@@ -2365,7 +2371,8 @@ erts_dsig_send(ErtsDSigSendContext *ctx)
(ctx->fragments-1) * ERTS_DIST_FRAGMENT_HEADER_SIZE,
ctx->fragments);
ctx->obuf->ext_start = &ctx->obuf->extp[0];
- ctx->obuf->ext_endp = &ctx->obuf->extp[0] + ctx->max_finalize_prepend + ctx->dhdr_ext_size;
+ ctx->obuf->ext_endp = &ctx->obuf->extp[0] + ctx->max_finalize_prepend
+ + ctx->dhdr_ext_size;
/* Encode internal version of dist header */
ctx->obuf->extp = erts_encode_ext_dist_header_setup(
@@ -2404,8 +2411,8 @@ erts_dsig_send(ErtsDSigSendContext *ctx)
case ERTS_DSIG_SEND_PHASE_FIN: {
ASSERT(ctx->obuf->extp < ctx->obuf->ext_endp);
- ASSERT(((byte*)&ctx->obuf->bin->orig_bytes[0]) <= ctx->obuf->extp - ctx->max_finalize_prepend);
- ASSERT(ctx->obuf->ext_endp <= ((byte*)ctx->obuf->bin->orig_bytes) + ctx->data_size + ctx->dhdr_ext_size);
+ ASSERT(ctx->obuf->ext_startp <= ctx->obuf->extp - ctx->max_finalize_prepend);
+ ASSERT(ctx->obuf->ext_endp <= (byte*)ctx->obuf->ext_startp + ctx->data_size + ctx->dhdr_ext_size);
ctx->data_size = ctx->obuf->ext_endp - ctx->obuf->extp;
@@ -3481,6 +3488,7 @@ dist_ctrl_get_data_1(BIF_ALIST_1)
pb->bytes = (byte*) obuf->extp;
pb->flags = 0;
res = make_binary(pb);
+ hp += PROC_BIN_SIZE;
} else {
hp = HAlloc(BIF_P, PROC_BIN_SIZE * 2 + 4 + hsz);
pb = (ProcBin *) (char *) hp;
@@ -3772,10 +3780,12 @@ int distribution_info(fmtfn_t to, void *arg) /* Called by break handler */
BIF_RETTYPE setnode_2(BIF_ALIST_2)
{
Process *net_kernel;
- Uint32 creation;
+ Uint creation;
/* valid creation ? */
- if(!term_to_Uint32(BIF_ARG_2, &creation))
+ if(!term_to_Uint(BIF_ARG_2, &creation))
+ goto error;
+ if(creation > 3)
goto error;
/* valid node name ? */
@@ -3819,7 +3829,7 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2)
erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
erts_thr_progress_block();
inc_no_nodes();
- erts_set_this_node(BIF_ARG_1, creation);
+ erts_set_this_node(BIF_ARG_1, (Uint32) creation);
erts_is_alive = 1;
send_nodes_mon_msgs(NULL, am_nodeup, BIF_ARG_1, am_visible, NIL);
erts_thr_progress_unblock();
diff --git a/erts/emulator/beam/dist.h b/erts/emulator/beam/dist.h
index f953a2ab8c..067028634b 100644
--- a/erts/emulator/beam/dist.h
+++ b/erts/emulator/beam/dist.h
@@ -54,12 +54,11 @@
#define DFLAG_DIST_MANDATORY (DFLAG_EXTENDED_REFERENCES \
| DFLAG_EXTENDED_PIDS_PORTS \
| DFLAG_UTF8_ATOMS \
- | DFLAG_NEW_FUN_TAGS \
- | DFLAG_BIG_CREATION)
+ | DFLAG_NEW_FUN_TAGS)
/*
* Additional optimistic flags when encoding toward pending connection.
- * If remote node (erl_interface) does not support these then we may need
+ * If remote node (erl_interface) does not supporting these then we may need
* to transcode messages enqueued before connection setup was finished.
*/
#define DFLAG_DIST_HOPEFULLY (DFLAG_EXPORT_PTR_TAG \
@@ -76,6 +75,7 @@
| DFLAG_SMALL_ATOM_TAGS \
| DFLAG_UTF8_ATOMS \
| DFLAG_MAP_TAG \
+ | DFLAG_BIG_CREATION \
| DFLAG_SEND_SENDER \
| DFLAG_BIG_SEQTRACE_LABELS \
| DFLAG_EXIT_PAYLOAD \
diff --git a/erts/emulator/beam/erl_alloc.c b/erts/emulator/beam/erl_alloc.c
index e6169ebeaa..b9f0334172 100644
--- a/erts/emulator/beam/erl_alloc.c
+++ b/erts/emulator/beam/erl_alloc.c
@@ -3904,7 +3904,7 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func)
{
Uint sz;
Uint found_type;
- UWord pre_pattern;
+ UWord pre_pattern, expected_pattern;
UWord post_pattern;
UWord *ui_ptr;
#ifdef HARD_DEBUG
@@ -3914,6 +3914,8 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func)
if (!ptr)
return NULL;
+ expected_pattern = MK_PATTERN(n);
+
ui_ptr = (UWord *) ptr;
pre_pattern = *(--ui_ptr);
*size = sz = *(--ui_ptr);
@@ -3922,7 +3924,13 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func)
#endif
found_type = GET_TYPE_OF_PATTERN(pre_pattern);
- if (pre_pattern != MK_PATTERN(n)) {
+
+ if (found_type != n) {
+ erts_exit(ERTS_ABORT_EXIT, "ERROR: Miss matching allocator types"
+ " used in alloc and free\n");
+ }
+
+ if (pre_pattern != expected_pattern) {
if ((FIXED_FENCE_PATTERN_MASK & pre_pattern) != FIXED_FENCE_PATTERN)
erts_exit(ERTS_ABORT_EXIT,
"ERROR: Fence at beginning of memory block (p=0x%u) "
@@ -3932,8 +3940,7 @@ check_memory_fence(void *ptr, Uint *size, ErtsAlcType_t n, int func)
sys_memcpy((void *) &post_pattern, (void *) (((char *)ptr)+sz), sizeof(UWord));
- if (post_pattern != MK_PATTERN(n)
- || pre_pattern != post_pattern) {
+ if (post_pattern != expected_pattern || pre_pattern != post_pattern) {
char fbuf[10];
char obuf[10];
char *ftype;
diff --git a/erts/emulator/beam/erl_bif_info.c b/erts/emulator/beam/erl_bif_info.c
index d542848460..0339589b79 100644
--- a/erts/emulator/beam/erl_bif_info.c
+++ b/erts/emulator/beam/erl_bif_info.c
@@ -2579,6 +2579,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
/* Need to be the only thread running... */
erts_proc_unlock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ BIF_P->scheduler_data->current_process = NULL;
erts_thr_progress_block();
if (BIF_ARG_1 == am_info)
@@ -2592,6 +2593,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
erts_thr_progress_unblock();
erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
+ BIF_P->scheduler_data->current_process = BIF_P;
ASSERT(dsbufp && dsbufp->str);
res = new_binary(BIF_P, (byte *) dsbufp->str, dsbufp->str_len);
@@ -2797,10 +2799,7 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
} else if (BIF_ARG_1 == am_threads) {
return am_true;
} else if (BIF_ARG_1 == am_creation) {
- Uint hsz = 0;
- erts_bld_uint(NULL, &hsz, erts_this_node->creation);
- hp = hsz ? HAlloc(BIF_P, hsz) : NULL;
- BIF_RET(erts_bld_uint(&hp, NULL, erts_this_node->creation));
+ return make_small(erts_this_node->creation);
} else if (BIF_ARG_1 == am_break_ignored) {
extern int ignore_break;
if (ignore_break)
@@ -3026,6 +3025,8 @@ BIF_RETTYPE system_info_1(BIF_ALIST_1)
BIF_RET(erts_nif_taints(BIF_P));
} else if (ERTS_IS_ATOM_STR("reader_groups_map", BIF_ARG_1)) {
BIF_RET(erts_get_reader_groups_map(BIF_P));
+ } else if (ERTS_IS_ATOM_STR("decentralized_counter_groups_map", BIF_ARG_1)) {
+ BIF_RET(erts_get_decentralized_counter_groups_map(BIF_P));
} else if (ERTS_IS_ATOM_STR("dist_buf_busy_limit", BIF_ARG_1)) {
Uint hsz = 0;
@@ -4695,6 +4696,14 @@ BIF_RETTYPE erts_debug_set_internal_state_2(BIF_ALIST_2)
BIF_RET(am_ok);
}
}
+ else if (ERTS_IS_ATOM_STR("ets_debug_random_split_join", BIF_ARG_1)) {
+ if (is_tuple(BIF_ARG_2)) {
+ Eterm* tpl = tuple_val(BIF_ARG_2);
+
+ if (erts_ets_debug_random_split_join(tpl[1], tpl[2] == am_true))
+ BIF_RET(am_ok);
+ }
+ }
else if (ERTS_IS_ATOM_STR("mbuf", BIF_ARG_1)) {
Uint sz = size_object(BIF_ARG_2);
ErlHeapFragment* frag = new_message_buffer(sz);
diff --git a/erts/emulator/beam/erl_cpu_topology.c b/erts/emulator/beam/erl_cpu_topology.c
index 6f8d2f8c35..6a4f43297e 100644
--- a/erts/emulator/beam/erl_cpu_topology.c
+++ b/erts/emulator/beam/erl_cpu_topology.c
@@ -34,6 +34,7 @@
#include "error.h"
#include "bif.h"
#include "erl_cpu_topology.h"
+#include "erl_flxctr.h"
#define ERTS_MAX_READER_GROUPS 64
@@ -58,6 +59,7 @@ static erts_cpu_info_t *cpuinfo;
static int max_main_threads;
static int reader_groups;
+static int decentralized_counter_groups;
static ErtsCpuBindData *scheduler2cpu_map;
static erts_rwmtx_t cpuinfo_rwmtx;
@@ -127,6 +129,8 @@ static erts_cpu_groups_map_t *cpu_groups_maps;
static erts_cpu_groups_map_t *reader_groups_map;
+static erts_cpu_groups_map_t *decentralized_counter_groups_map;
+
#define ERTS_TOPOLOGY_CG ERTS_TOPOLOGY_MAX_DEPTH
#define ERTS_MAX_CPU_TOPOLOGY_ID ((int) 0xffff)
@@ -138,6 +142,7 @@ static void cpu_bind_order_sort(erts_cpu_topology_t *cpudata,
static void write_schedulers_bind_change(erts_cpu_topology_t *cpudata, int size);
static void reader_groups_callback(int, ErtsSchedulerData *, int, void *);
+static void flxctr_groups_callback(int, ErtsSchedulerData *, int, void *);
static erts_cpu_groups_map_t *add_cpu_groups(int groups,
erts_cpu_groups_callback_t callback,
void *arg);
@@ -1646,7 +1651,8 @@ erts_get_logical_processors(int *conf, int *onln, int *avail)
}
void
-erts_pre_early_init_cpu_topology(int *max_rg_p,
+erts_pre_early_init_cpu_topology(int *max_dcg_p,
+ int *max_rg_p,
int *conf_p,
int *onln_p,
int *avail_p)
@@ -1654,6 +1660,7 @@ erts_pre_early_init_cpu_topology(int *max_rg_p,
cpu_groups_maps = NULL;
no_cpu_groups_callbacks = 0;
*max_rg_p = ERTS_MAX_READER_GROUPS;
+ *max_dcg_p = ERTS_MAX_FLXCTR_GROUPS;
cpuinfo = erts_cpu_info_create();
get_logical_processors(conf_p, onln_p, avail_p);
}
@@ -1662,7 +1669,9 @@ void
erts_early_init_cpu_topology(int no_schedulers,
int *max_main_threads_p,
int max_reader_groups,
- int *reader_groups_p)
+ int *reader_groups_p,
+ int max_decentralized_counter_groups,
+ int *decentralized_counter_groups_p)
{
user_cpudata = NULL;
user_cpudata_size = 0;
@@ -1687,6 +1696,12 @@ erts_early_init_cpu_topology(int no_schedulers,
max_main_threads = no_schedulers;
*max_main_threads_p = max_main_threads;
+ decentralized_counter_groups = max_main_threads;
+ if (decentralized_counter_groups <= 1 || max_decentralized_counter_groups <= 1)
+ decentralized_counter_groups = 1;
+ if (decentralized_counter_groups > max_decentralized_counter_groups)
+ decentralized_counter_groups = max_decentralized_counter_groups;
+ *decentralized_counter_groups_p = decentralized_counter_groups;
reader_groups = max_main_threads;
if (reader_groups <= 1 || max_reader_groups <= 1)
reader_groups = 0;
@@ -1718,6 +1733,9 @@ erts_init_cpu_topology(void)
reader_groups_map = add_cpu_groups(reader_groups,
reader_groups_callback,
NULL);
+ decentralized_counter_groups_map = add_cpu_groups(decentralized_counter_groups,
+ flxctr_groups_callback,
+ NULL);
if (cpu_bind_order == ERTS_CPU_BIND_NONE)
erts_rwmtx_rwunlock(&cpuinfo_rwmtx);
@@ -1789,6 +1807,15 @@ reader_groups_callback(int suspending,
erts_rwmtx_set_reader_group(suspending ? 0 : group+1);
}
+void
+flxctr_groups_callback(int suspending,
+ ErtsSchedulerData *esdp,
+ int group,
+ void *unused)
+{
+ erts_flxctr_set_slot(suspending ? 0 : group+1);
+}
+
static Eterm get_cpu_groups_map(Process *c_p,
erts_cpu_groups_map_t *map,
int offset);
@@ -1821,6 +1848,16 @@ erts_get_reader_groups_map(Process *c_p)
return res;
}
+Eterm
+erts_get_decentralized_counter_groups_map(Process *c_p)
+{
+ Eterm res;
+ erts_rwmtx_rlock(&cpuinfo_rwmtx);
+ res = get_cpu_groups_map(c_p, decentralized_counter_groups_map, 1);
+ erts_rwmtx_runlock(&cpuinfo_rwmtx);
+ return res;
+}
+
/*
* CPU groups
*/
diff --git a/erts/emulator/beam/erl_cpu_topology.h b/erts/emulator/beam/erl_cpu_topology.h
index 88bcad79ab..4a428d7972 100644
--- a/erts/emulator/beam/erl_cpu_topology.h
+++ b/erts/emulator/beam/erl_cpu_topology.h
@@ -27,14 +27,19 @@
#ifndef ERL_CPU_TOPOLOGY_H__
#define ERL_CPU_TOPOLOGY_H__
-void erts_pre_early_init_cpu_topology(int *max_rg_p,
- int *conf_p,
- int *onln_p,
- int *avail_p);
-void erts_early_init_cpu_topology(int no_schedulers,
- int *max_main_threads_p,
- int max_reader_groups,
- int *reader_groups_p);
+void
+erts_pre_early_init_cpu_topology(int *max_dcg_p,
+ int *max_rg_p,
+ int *conf_p,
+ int *onln_p,
+ int *avail_p);
+void
+erts_early_init_cpu_topology(int no_schedulers,
+ int *max_main_threads_p,
+ int max_reader_groups,
+ int *reader_groups_p,
+ int max_decentralized_counter_groups,
+ int *decentralized_counter_groups_p);
void erts_init_cpu_topology(void);
@@ -70,6 +75,7 @@ Eterm erts_bind_schedulers(Process *c_p, Eterm how);
Eterm erts_get_schedulers_binds(Process *c_p);
Eterm erts_get_reader_groups_map(Process *c_p);
+Eterm erts_get_decentralized_counter_groups_map(Process *c_p);
Eterm erts_set_cpu_topology(Process *c_p, Eterm term);
Eterm erts_get_cpu_topology_term(Process *c_p, Eterm which);
diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c
index 0a50af4d1a..d24f30f126 100644
--- a/erts/emulator/beam/erl_db.c
+++ b/erts/emulator/beam/erl_db.c
@@ -42,6 +42,7 @@
#include "bif.h"
#include "big.h"
#include "erl_binary.h"
+#include "bif.h"
erts_atomic_t erts_ets_misc_mem_size;
@@ -64,6 +65,11 @@ do { \
} \
}while(0)
+#define DB_GET_APPROX_NITEMS(DB) \
+ erts_flxctr_read_approx(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID)
+#define DB_GET_APPROX_MEM_CONSUMED(DB) \
+ erts_flxctr_read_approx(&(DB)->common.counters, ERTS_DB_TABLE_MEM_COUNTER_ID)
+
static BIF_RETTYPE db_bif_fail(Process* p, Uint freason,
Uint bif_ix, Export* bif_exp)
{
@@ -398,8 +404,9 @@ static void
free_dbtable(void *vtb)
{
DbTable *tb = (DbTable *) vtb;
-
- ASSERT(erts_atomic_read_nob(&tb->common.memory_size) == sizeof(DbTable));
+ ASSERT(erts_flxctr_is_snapshot_ongoing(&tb->common.counters) ||
+ sizeof(DbTable) == erts_flxctr_read_approx(&tb->common.counters,
+ ERTS_DB_TABLE_MEM_COUNTER_ID));
erts_rwmtx_destroy(&tb->common.rwlock);
erts_mtx_destroy(&tb->common.fixlock);
@@ -408,7 +415,8 @@ free_dbtable(void *vtb)
if (tb->common.btid)
erts_bin_release(tb->common.btid);
- erts_db_free(ERTS_ALC_T_DB_TABLE, tb, (void *) tb, sizeof(DbTable));
+ erts_flxctr_destroy(&tb->common.counters, ERTS_ALC_T_DB_TABLE);
+ erts_free(ERTS_ALC_T_DB_TABLE, tb);
}
static void schedule_free_dbtable(DbTable* tb)
@@ -1731,12 +1739,16 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
*/
{
DbTable init_tb;
-
- erts_atomic_init_nob(&init_tb.common.memory_size, 0);
+ erts_flxctr_init(&init_tb.common.counters, 0, 2, ERTS_ALC_T_DB_TABLE);
tb = (DbTable*) erts_db_alloc(ERTS_ALC_T_DB_TABLE,
&init_tb, sizeof(DbTable));
- erts_atomic_init_nob(&tb->common.memory_size,
- erts_atomic_read_nob(&init_tb.common.memory_size));
+ erts_flxctr_init(&tb->common.counters,
+ status & DB_CA_ORDERED_SET,
+ 2,
+ ERTS_ALC_T_DB_TABLE);
+ erts_flxctr_add(&tb->common.counters,
+ ERTS_DB_TABLE_MEM_COUNTER_ID,
+ DB_GET_APPROX_MEM_CONSUMED(&init_tb));
}
tb->common.meth = meth;
@@ -1750,8 +1762,6 @@ BIF_RETTYPE ets_new_2(BIF_ALIST_2)
tb->common.owner = BIF_P->common.id;
set_heir(BIF_P, tb, heir, heir_data);
- erts_atomic_init_nob(&tb->common.nitems, 0);
-
tb->common.fixing_procs = NULL;
tb->common.compress = is_compressed;
#ifdef ETS_DBG_FORCE_TRAP
@@ -2128,19 +2138,18 @@ BIF_RETTYPE ets_internal_delete_all_2(BIF_ALIST_2)
{
SWord initial_reds = ERTS_BIF_REDS_LEFT(BIF_P);
SWord reds = initial_reds;
- Eterm nitems;
+ Eterm nitems_holder = THE_NON_VALUE;
DbTable* tb;
-
CHECK_TABLES();
DB_BIF_GET_TABLE(tb, DB_WRITE, LCK_WRITE, BIF_ets_internal_delete_all_2);
if (BIF_ARG_2 == am_undefined) {
- nitems = erts_make_integer(erts_atomic_read_nob(&tb->common.nitems),
- BIF_P);
-
- reds = tb->common.meth->db_delete_all_objects(BIF_P, tb, reds);
-
+ reds = tb->common.meth->db_delete_all_objects(BIF_P,
+ tb,
+ reds,
+ &nitems_holder);
+ ASSERT(nitems_holder != THE_NON_VALUE);
ASSERT(!(tb->common.status & DB_BUSY));
if (reds < 0) {
@@ -2159,7 +2168,7 @@ BIF_RETTYPE ets_internal_delete_all_2(BIF_ALIST_2)
db_unlock(tb, LCK_WRITE);
BUMP_ALL_REDS(BIF_P);
BIF_TRAP2(bif_export[BIF_ets_internal_delete_all_2], BIF_P,
- BIF_ARG_1, nitems);
+ BIF_ARG_1, nitems_holder);
}
else {
/* Done, no trapping needed */
@@ -2169,15 +2178,19 @@ BIF_RETTYPE ets_internal_delete_all_2(BIF_ALIST_2)
}
else {
/*
- * The table lookup succeeded and second argument is nitems
+ * The table lookup succeeded and second argument is nitems_holder
* and not 'undefined', which means we have trapped at least once
* and are now done.
*/
- nitems = BIF_ARG_2;
+ nitems_holder = BIF_ARG_2;
}
-
db_unlock(tb, LCK_WRITE);
+ {
+ Eterm nitems =
+ tb->common.meth->db_delete_all_objects_get_nitems_from_holder(BIF_P,
+ nitems_holder);
BIF_RET(nitems);
+ }
}
static void delete_all_objects_continue(Process* p, DbTable* tb)
@@ -2190,7 +2203,7 @@ static void delete_all_objects_continue(Process* p, DbTable* tb)
if ((tb->common.status & (DB_DELETE|DB_BUSY)) != DB_BUSY)
return;
- reds = tb->common.meth->db_delete_all_objects(p, tb, reds);
+ reds = tb->common.meth->db_delete_all_objects(p, tb, reds, NULL);
if (reds < 0) {
BUMP_ALL_REDS(p);
@@ -3277,13 +3290,29 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1)
int i;
Eterm* hp;
Uint freason;
+ Sint size = -1;
+ Sint memory = -1;
+ Eterm table;
+ int is_ctrs_read_result_set = 0;
/*Process* rp = NULL;*/
/* If/when we implement lockless private tables:
Eterm owner;
*/
-
- if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_INFO, LCK_READ, &freason)) == NULL) {
- if (freason == BADARG && (is_atom(BIF_ARG_1) || is_ref(BIF_ARG_1)))
+ if(is_tuple(BIF_ARG_1) &&
+ is_tuple_arity(BIF_ARG_1, 2) &&
+ erts_flxctr_is_snapshot_result(tuple_val(BIF_ARG_1)[1])) {
+ Eterm counter_read_result = tuple_val(BIF_ARG_1)[1];
+ table = tuple_val(BIF_ARG_1)[2];
+ size = erts_flxctr_get_snapshot_result_after_trap(counter_read_result,
+ ERTS_DB_TABLE_NITEMS_COUNTER_ID);
+ memory = erts_flxctr_get_snapshot_result_after_trap(counter_read_result,
+ ERTS_DB_TABLE_MEM_COUNTER_ID);
+ is_ctrs_read_result_set = 1;
+ } else {
+ table = BIF_ARG_1;
+ }
+ if ((tb = db_get_table(BIF_P, table, DB_INFO, LCK_READ, &freason)) == NULL) {
+ if (freason == BADARG && (is_atom(table) || is_ref(table)))
BIF_RET(am_undefined);
else
return db_bif_fail(BIF_P, freason, BIF_ets_info_1, NULL);
@@ -3314,9 +3343,35 @@ BIF_RETTYPE ets_info_1(BIF_ALIST_1)
BIF_ERROR(BIF_P, BADARG);
}
}*/
+
+ if (!is_ctrs_read_result_set) {
+ ErtsFlxCtrSnapshotResult res =
+ erts_flxctr_snapshot(&tb->common.counters, ERTS_ALC_T_DB_TABLE, BIF_P);
+ if (ERTS_FLXCTR_GET_RESULT_AFTER_TRAP == res.type) {
+ Eterm tuple;
+ db_unlock(tb, LCK_READ);
+ hp = HAlloc(BIF_P, 3);
+ tuple = TUPLE2(hp, res.trap_resume_state, table);
+ BIF_TRAP1(bif_export[BIF_ets_info_1], BIF_P, tuple);
+ } else if (res.type == ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP) {
+ db_unlock(tb, LCK_READ);
+ BIF_TRAP1(bif_export[BIF_ets_info_1], BIF_P, table);
+ } else {
+ size = res.result[ERTS_DB_TABLE_NITEMS_COUNTER_ID];
+ memory = res.result[ERTS_DB_TABLE_MEM_COUNTER_ID];
+ is_ctrs_read_result_set = 1;
+ }
+ }
for (i = 0; i < sizeof(fields)/sizeof(Eterm); i++) {
- results[i] = table_info(BIF_P, tb, fields[i]);
- ASSERT(is_value(results[i]));
+ if (is_ctrs_read_result_set && am_size == fields[i]) {
+ results[i] = erts_make_integer(size, BIF_P);
+ } else if (is_ctrs_read_result_set && am_memory == fields[i]) {
+ Sint words = (Sint) ((memory + sizeof(Sint) - 1) / sizeof(Sint));
+ results[i] = erts_make_integer(words, BIF_P);
+ } else {
+ results[i] = table_info(BIF_P, tb, fields[i]);
+ ASSERT(is_value(results[i]));
+ }
}
db_unlock(tb, LCK_READ);
@@ -3344,14 +3399,43 @@ BIF_RETTYPE ets_info_2(BIF_ALIST_2)
DbTable* tb;
Eterm ret = THE_NON_VALUE;
Uint freason;
-
+ if (erts_flxctr_is_snapshot_result(BIF_ARG_1)) {
+ Sint res;
+ if (am_memory == BIF_ARG_2) {
+ res = erts_flxctr_get_snapshot_result_after_trap(BIF_ARG_1,
+ ERTS_DB_TABLE_MEM_COUNTER_ID);
+ res = (Sint) ((res + sizeof(Sint) - 1) / sizeof(Sint));
+ } else {
+ res = erts_flxctr_get_snapshot_result_after_trap(BIF_ARG_1,
+ ERTS_DB_TABLE_NITEMS_COUNTER_ID);
+ }
+ BIF_RET(erts_make_integer(res, BIF_P));
+ }
if ((tb = db_get_table(BIF_P, BIF_ARG_1, DB_INFO, LCK_READ, &freason)) == NULL) {
if (freason == BADARG && (is_atom(BIF_ARG_1) || is_ref(BIF_ARG_1)))
BIF_RET(am_undefined);
else
return db_bif_fail(BIF_P, freason, BIF_ets_info_2, NULL);
}
- ret = table_info(BIF_P, tb, BIF_ARG_2);
+ if (BIF_ARG_2 == am_size || BIF_ARG_2 == am_memory) {
+ ErtsFlxCtrSnapshotResult res =
+ erts_flxctr_snapshot(&tb->common.counters, ERTS_ALC_T_DB_TABLE, BIF_P);
+ if (ERTS_FLXCTR_GET_RESULT_AFTER_TRAP == res.type) {
+ db_unlock(tb, LCK_READ);
+ BIF_TRAP2(bif_export[BIF_ets_info_2], BIF_P, res.trap_resume_state, BIF_ARG_2);
+ } else if (res.type == ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP) {
+ db_unlock(tb, LCK_READ);
+ BIF_TRAP2(bif_export[BIF_ets_info_2], BIF_P, BIF_ARG_1, BIF_ARG_2);
+ } else if (BIF_ARG_2 == am_size) {
+ ret = erts_make_integer(res.result[ERTS_DB_TABLE_NITEMS_COUNTER_ID], BIF_P);
+ } else { /* BIF_ARG_2 == am_memory */
+ Sint r = res.result[ERTS_DB_TABLE_MEM_COUNTER_ID];
+ r = (Sint) ((r + sizeof(Sint) - 1) / sizeof(Sint));
+ ret = erts_make_integer(r, BIF_P);
+ }
+ } else {
+ ret = table_info(BIF_P, tb, BIF_ARG_2);
+ }
db_unlock(tb, LCK_READ);
if (is_non_value(ret)) {
BIF_ERROR(BIF_P, BADARG);
@@ -4121,7 +4205,8 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
int use_monotonic;
if (What == am_size) {
- ret = make_small(erts_atomic_read_nob(&tb->common.nitems));
+ Uint size = (Uint) (DB_GET_APPROX_NITEMS(tb));
+ ret = erts_make_integer(size, p);
} else if (What == am_type) {
if (tb->common.status & DB_SET) {
ret = am_set;
@@ -4136,7 +4221,7 @@ static Eterm table_info(Process* p, DbTable* tb, Eterm What)
ret = am_bag;
}
} else if (What == am_memory) {
- Uint words = (Uint) ((erts_atomic_read_nob(&tb->common.memory_size)
+ Uint words = (Uint) ((DB_GET_APPROX_MEM_CONSUMED(tb)
+ sizeof(Uint)
- 1)
/ sizeof(Uint));
@@ -4294,9 +4379,9 @@ static void print_table(fmtfn_t to, void *to_arg, int show, DbTable* tb)
tb->common.meth->db_print(to, to_arg, show, tb);
- erts_print(to, to_arg, "Objects: %d\n", (int)erts_atomic_read_nob(&tb->common.nitems));
+ erts_print(to, to_arg, "Objects: %d\n", (int)DB_GET_APPROX_NITEMS(tb));
erts_print(to, to_arg, "Words: %bpu\n",
- (Uint) ((erts_atomic_read_nob(&tb->common.memory_size)
+ (Uint) ((DB_GET_APPROX_MEM_CONSUMED(tb)
+ sizeof(Uint)
- 1)
/ sizeof(Uint)));
@@ -4488,3 +4573,16 @@ int erts_ets_force_split(Eterm tid, int on)
db_unlock(tb, LCK_WRITE);
return 1;
}
+
+int erts_ets_debug_random_split_join(Eterm tid, int on)
+{
+ DbTable* tb = tid2tab(tid);
+ if (!tb || !IS_CATREE_TABLE(tb->common.type))
+ return 0;
+
+ db_lock(tb, LCK_WRITE);
+ if (!(tb->common.status & DB_DELETE))
+ db_catree_debug_random_split_join(&tb->catree, on);
+ db_unlock(tb, LCK_WRITE);
+ return 1;
+}
diff --git a/erts/emulator/beam/erl_db.h b/erts/emulator/beam/erl_db.h
index dc77fbb60c..b3dc1b9ba3 100644
--- a/erts/emulator/beam/erl_db.h
+++ b/erts/emulator/beam/erl_db.h
@@ -131,6 +131,7 @@ extern erts_atomic_t erts_ets_misc_mem_size;
Eterm erts_ets_colliding_names(Process*, Eterm name, Uint cnt);
int erts_ets_force_split(Eterm tid, int on);
+int erts_ets_debug_random_split_join(Eterm tid, int on);
Uint erts_db_get_max_tabs(void);
Eterm erts_db_make_tid(Process *c_p, DbTableCommon *tb);
@@ -160,7 +161,9 @@ do { \
erts_aint_t sz__ = (((erts_aint_t) (ALLOC_SZ)) \
- ((erts_aint_t) (FREE_SZ))); \
ASSERT((TAB)); \
- erts_atomic_add_nob(&(TAB)->common.memory_size, sz__); \
+ erts_flxctr_add(&(TAB)->common.counters, \
+ ERTS_DB_TABLE_MEM_COUNTER_ID, \
+ sz__); \
} while (0)
#define ERTS_ETS_MISC_MEM_ADD(SZ) \
@@ -305,10 +308,10 @@ erts_db_free(ErtsAlcType_t type, DbTable *tab, void *ptr, Uint size)
ASSERT(ptr != 0);
ASSERT(size == ERTS_ALC_DBG_BLK_SZ(ptr));
ERTS_DB_ALC_MEM_UPDATE_(tab, size, 0);
-
- ASSERT(((void *) tab) != ptr
- || erts_atomic_read_nob(&tab->common.memory_size) == 0);
-
+ ASSERT(((void *) tab) != ptr ||
+ tab->common.counters.is_decentralized ||
+ 0 == erts_flxctr_read_centralized(&tab->common.counters,
+ ERTS_DB_TABLE_MEM_COUNTER_ID));
erts_free(type, ptr);
}
diff --git a/erts/emulator/beam/erl_db_catree.c b/erts/emulator/beam/erl_db_catree.c
index 0402c6b7b4..e0d5e44f58 100644
--- a/erts/emulator/beam/erl_db_catree.c
+++ b/erts/emulator/beam/erl_db_catree.c
@@ -149,7 +149,12 @@ static SWord db_free_table_continue_catree(DbTable *tbl, SWord);
static void db_foreach_offheap_catree(DbTable *,
void (*)(ErlOffHeap *, void *),
void *);
-static SWord db_delete_all_objects_catree(Process* p, DbTable* tbl, SWord reds);
+static SWord db_delete_all_objects_catree(Process* p,
+ DbTable* tbl,
+ SWord reds,
+ Eterm* nitems_holder_wb);
+static Eterm db_delete_all_objects_get_nitems_from_holder_catree(Process* p,
+ Eterm nitems_holder);
static int
db_lookup_dbterm_catree(Process *, DbTable *, Eterm key, Eterm obj,
DbUpdateHandle*);
@@ -191,6 +196,7 @@ DbTableMethod db_catree =
db_select_replace_continue_catree,
db_take_catree,
db_delete_all_objects_catree,
+ db_delete_all_objects_get_nitems_from_holder_catree,
db_free_table_catree,
db_free_table_continue_catree,
db_print_catree,
@@ -641,7 +647,8 @@ static int dbg_fastrand(void)
static void dbg_provoke_random_splitjoin(DbTableCATree* tb,
DbTableCATreeNode* base_node)
{
- if (tb->common.status & DB_CATREE_FORCE_SPLIT)
+ if (tb->common.status & DB_CATREE_FORCE_SPLIT ||
+ !(tb->common.status & DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN))
return;
switch (dbg_fastrand() % 8) {
@@ -1357,6 +1364,8 @@ static SWord do_free_base_node_cont(DbTableCATree *tb, SWord num_left)
PUSH_NODE(&tb->free_stack_elems, root);
root = p;
} else {
+ DEC_NITEMS((DbTable*)tb);
+ tb->nr_of_deleted_items++;
free_term((DbTable*)tb, root);
if (--num_left >= 0) {
break;
@@ -1397,6 +1406,10 @@ int db_create_catree(Process *p, DbTable *tbl)
root = create_base_node(tb, NULL);
tb->deletion = 0;
tb->base_nodes_to_free_list = NULL;
+ tb->nr_of_deleted_items = 0;
+#ifdef DEBUG
+ tbl->common.status |= DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN;
+#endif
erts_atomic_init_relb(&(tb->root), (erts_aint_t)root);
return DB_ERROR_NONE;
}
@@ -2050,6 +2063,7 @@ static SWord db_free_table_continue_catree(DbTable *tbl, SWord reds)
PUSH_NODE(&tb->free_stack_rnodes, GET_ROOT(tb));
tb->is_routing_nodes_freed = 0;
tb->base_nodes_to_free_list = NULL;
+ tb->nr_of_deleted_items = 0;
}
if ( ! tb->is_routing_nodes_freed ) {
reds = do_free_routing_nodes_catree_cont(tb, reds);
@@ -2079,13 +2093,57 @@ static SWord db_free_table_continue_catree(DbTable *tbl, SWord reds)
return 1;
}
-static SWord db_delete_all_objects_catree(Process* p, DbTable* tbl, SWord reds)
+static
+int db_catree_nr_of_items_deleted_wb_dtor(Binary *context_bin) {
+ (void)context_bin;
+ return 1;
+}
+
+typedef struct {
+ Uint nr_of_deleted_items;
+} DbCATreeNrOfItemsDeletedWb;
+
+static Eterm
+create_and_install_num_of_deleted_items_wb_bin(Process *p, DbTableCATree *tb)
+{
+ Binary* bin =
+ erts_create_magic_binary(sizeof(DbCATreeNrOfItemsDeletedWb),
+ db_catree_nr_of_items_deleted_wb_dtor);
+ DbCATreeNrOfItemsDeletedWb* data = ERTS_MAGIC_BIN_DATA(bin);
+ Eterm* hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE);
+ Eterm mref = erts_mk_magic_ref(&hp, &MSO(p), bin);
+ data->nr_of_deleted_items = 0;
+ tb->nr_of_deleted_items_wb = bin;
+ erts_refc_inctest(&bin->intern.refc, 2);
+ return mref;
+}
+
+static Eterm db_delete_all_objects_get_nitems_from_holder_catree(Process* p,
+ Eterm mref)
+{
+ Binary* bin = erts_magic_ref2bin(mref);
+ DbCATreeNrOfItemsDeletedWb* data = ERTS_MAGIC_BIN_DATA(bin);
+ return erts_make_integer(data->nr_of_deleted_items, p);
+}
+
+static SWord db_delete_all_objects_catree(Process* p,
+ DbTable* tbl,
+ SWord reds,
+ Eterm* nitems_holder_wb)
{
+ DbTableCATree *tb = &tbl->catree;
+ DbCATreeNrOfItemsDeletedWb* data;
+ if (!tb->deletion) {
+ *nitems_holder_wb =
+ create_and_install_num_of_deleted_items_wb_bin(p, tb);
+ }
reds = db_free_table_continue_catree(tbl, reds);
if (reds < 0)
return reds;
+ data = ERTS_MAGIC_BIN_DATA(tb->nr_of_deleted_items_wb);
+ data->nr_of_deleted_items = tb->nr_of_deleted_items;
+ erts_bin_release(tb->nr_of_deleted_items_wb);
db_create_catree(p, tbl);
- erts_atomic_set_nob(&tbl->catree.common.nitems, 0);
return reds;
}
@@ -2203,6 +2261,14 @@ void db_catree_force_split(DbTableCATree* tb, int on)
tb->common.status &= ~DB_CATREE_FORCE_SPLIT;
}
+void db_catree_debug_random_split_join(DbTableCATree* tb, int on)
+{
+ if (on)
+ tb->common.status |= DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN;
+ else
+ tb->common.status &= ~DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN;
+}
+
void db_calc_stats_catree(DbTableCATree* tb, DbCATreeStats* stats)
{
DbTableCATreeNode* stack[ERL_DB_CATREE_MAX_ROUTE_NODE_LAYER_HEIGHT];
diff --git a/erts/emulator/beam/erl_db_catree.h b/erts/emulator/beam/erl_db_catree.h
index 418837be8e..cf3498dabb 100644
--- a/erts/emulator/beam/erl_db_catree.h
+++ b/erts/emulator/beam/erl_db_catree.h
@@ -87,6 +87,10 @@ typedef struct db_table_catree {
CATreeNodeStack free_stack_rnodes;
DbTableCATreeNode *base_nodes_to_free_list;
int is_routing_nodes_freed;
+ /* The fields below are used by delete_all_objects and
+ select_delete(DeleteAll)*/
+ Uint nr_of_deleted_items;
+ Binary* nr_of_deleted_items_wb;
} DbTableCATree;
typedef struct {
@@ -104,7 +108,6 @@ void db_initialize_catree(void);
int db_create_catree(Process *p, DbTable *tbl);
-
TreeDbTerm** catree_find_root(Eterm key, CATreeRootIterator*);
TreeDbTerm** catree_find_next_from_pb_key_root(Eterm key, CATreeRootIterator*);
@@ -121,6 +124,7 @@ void erts_lcnt_enable_db_catree_lock_count(DbTableCATree *tb, int enable);
#endif
void db_catree_force_split(DbTableCATree*, int on);
+void db_catree_debug_random_split_join(DbTableCATree*, int on);
typedef struct {
Uint route_nodes;
diff --git a/erts/emulator/beam/erl_db_hash.c b/erts/emulator/beam/erl_db_hash.c
index f225730029..ceaccf7e44 100644
--- a/erts/emulator/beam/erl_db_hash.c
+++ b/erts/emulator/beam/erl_db_hash.c
@@ -85,6 +85,14 @@
#include "erl_db_hash.h"
+#define ADD_NITEMS(DB, TO_ADD) \
+ erts_flxctr_add(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID, TO_ADD)
+#define INC_NITEMS(DB) \
+ erts_flxctr_inc_read_centralized(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID)
+#define DEC_NITEMS(DB) \
+ erts_flxctr_dec_read_centralized(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID)
+#define RESET_NITEMS(DB) \
+ erts_flxctr_reset(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID)
/*
* The following symbols can be manipulated to "tune" the linear hash array
*/
@@ -121,7 +129,9 @@
: ((struct segment**) erts_atomic_read_nob(&(tb)->segtab)))
#endif
#define NACTIVE(tb) ((int)erts_atomic_read_nob(&(tb)->nactive))
-#define NITEMS(tb) ((int)erts_atomic_read_nob(&(tb)->common.nitems))
+#define NITEMS(tb) \
+ ((Sint)erts_flxctr_read_centralized(&(tb)->common.counters, \
+ ERTS_DB_TABLE_NITEMS_COUNTER_ID))
#define SLOT_IX_TO_SEG_IX(i) (((i)+(EXT_SEGSZ-FIRST_SEGSZ)) >> EXT_SEGSZ_EXP)
@@ -444,7 +454,12 @@ static void db_foreach_offheap_hash(DbTable *,
void (*)(ErlOffHeap *, void *),
void *);
-static SWord db_delete_all_objects_hash(Process* p, DbTable* tbl, SWord reds);
+static SWord db_delete_all_objects_hash(Process* p,
+ DbTable* tbl,
+ SWord reds,
+ Eterm* nitems_holder_wb);
+static Eterm db_delete_all_objects_get_nitems_from_holder_hash(Process* p,
+ Eterm nitems_holder);
#ifdef HARDDEBUG
static void db_check_table_hash(DbTableHash *tb);
#endif
@@ -548,6 +563,7 @@ DbTableMethod db_hash =
db_select_replace_continue_hash,
db_take_hash,
db_delete_all_objects_hash,
+ db_delete_all_objects_get_nitems_from_holder_hash,
db_free_empty_table_hash,
db_free_table_continue_hash,
db_print_hash,
@@ -806,7 +822,7 @@ int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail)
if (tb->common.status & DB_SET) {
HashDbTerm* bnext = b->next;
if (is_pseudo_deleted(b)) {
- erts_atomic_inc_nob(&tb->common.nitems);
+ INC_NITEMS(tb);
b->pseudo_deleted = 0;
}
else if (key_clash_fail) {
@@ -835,7 +851,7 @@ int db_put_hash(DbTable *tbl, Eterm obj, int key_clash_fail)
do {
if (db_eq(&tb->common,obj,&q->dbterm)) {
if (is_pseudo_deleted(q)) {
- erts_atomic_inc_nob(&tb->common.nitems);
+ INC_NITEMS(tb);
q->pseudo_deleted = 0;
ASSERT(q->hvalue == hval);
if (q != b) { /* must move to preserve key insertion order */
@@ -858,7 +874,7 @@ Lnew:
q->pseudo_deleted = 0;
q->next = b;
*bp = q;
- nitems = erts_atomic_inc_read_nob(&tb->common.nitems);
+ nitems = INC_NITEMS(tb);
WUNLOCK_HASH(lck);
{
int nactive = NACTIVE(tb);
@@ -1056,7 +1072,7 @@ int db_erase_hash(DbTable *tbl, Eterm key, Eterm *ret)
}
WUNLOCK_HASH(lck);
if (nitems_diff) {
- erts_atomic_add_nob(&tb->common.nitems, nitems_diff);
+ ADD_NITEMS(tb, nitems_diff);
try_shrink(tb);
}
free_term_list(tb, free_us);
@@ -1117,7 +1133,7 @@ static int db_erase_object_hash(DbTable *tbl, Eterm object, Eterm *ret)
}
WUNLOCK_HASH(lck);
if (nitems_diff) {
- erts_atomic_add_nob(&tb->common.nitems, nitems_diff);
+ ADD_NITEMS(tb, nitems_diff);
try_shrink(tb);
}
free_term_list(tb, free_us);
@@ -2023,7 +2039,7 @@ static int select_delete_on_match_res(traverse_context_t* ctx_base, Sint slot_ix
del->next = ctx->free_us;
ctx->free_us = del;
}
- erts_atomic_dec_nob(&ctx->base.tb->common.nitems);
+ DEC_NITEMS(ctx->base.tb);
return 1;
}
@@ -2300,7 +2316,7 @@ static int db_take_hash(Process *p, DbTable *tbl, Eterm key, Eterm *ret)
}
WUNLOCK_HASH(lck);
if (nitems_diff) {
- erts_atomic_add_nob(&tb->common.nitems, nitems_diff);
+ ADD_NITEMS(tb, nitems_diff);
try_shrink(tb);
}
free_term_list(tb, free_us);
@@ -2360,7 +2376,7 @@ static SWord db_mark_all_deleted_hash(DbTable *tbl, SWord reds)
fixdel->slot = NACTIVE(tb) - 1;
fixdel->all = 1;
fixdel->trap = 0;
- erts_atomic_set_nob(&tb->common.nitems, 0);
+ RESET_NITEMS(tb);
return loops < 0 ? 0 : loops / LOOPS_PER_REDUCTION;
}
@@ -2468,7 +2484,8 @@ static SWord db_free_table_continue_hash(DbTable *tbl, SWord reds)
(void*)tb->locks, sizeof(DbTableHashFineLocks));
tb->locks = NULL;
}
- ASSERT(erts_atomic_read_nob(&tb->common.memory_size) == sizeof(DbTable));
+ ASSERT(sizeof(DbTable) == erts_flxctr_read_approx(&tb->common.counters,
+ ERTS_DB_TABLE_MEM_COUNTER_ID));
return reds; /* Done */
}
@@ -3080,7 +3097,7 @@ db_lookup_dbterm_hash(Process *p, DbTable *tbl, Eterm key, Eterm obj,
ASSERT(q->hvalue == hval);
q->pseudo_deleted = 0;
*bp = b = q;
- erts_atomic_inc_nob(&tb->common.nitems);
+ INC_NITEMS(tb);
}
HRelease(p, hend, htop);
@@ -3123,7 +3140,7 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle)
}
WUNLOCK_HASH(lck);
- erts_atomic_dec_nob(&tb->common.nitems);
+ DEC_NITEMS(tb);
try_shrink(tb);
} else {
if (handle->flags & DB_MUST_RESIZE) {
@@ -3132,7 +3149,7 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle)
}
if (handle->flags & DB_INC_TRY_GROW) {
int nactive;
- int nitems = erts_atomic_inc_read_nob(&tb->common.nitems);
+ int nitems = INC_NITEMS(tb);
WUNLOCK_HASH(lck);
nactive = NACTIVE(tb);
@@ -3153,8 +3170,17 @@ db_finalize_dbterm_hash(int cret, DbUpdateHandle* handle)
return;
}
-static SWord db_delete_all_objects_hash(Process* p, DbTable* tbl, SWord reds)
+static SWord db_delete_all_objects_hash(Process* p,
+ DbTable* tbl,
+ SWord reds,
+ Eterm* nitems_holder_wb)
{
+ if (nitems_holder_wb != NULL) {
+ Uint nr_of_items =
+ erts_flxctr_read_centralized(&tbl->common.counters,
+ ERTS_DB_TABLE_NITEMS_COUNTER_ID);
+ *nitems_holder_wb = erts_make_integer(nr_of_items, p);
+ }
if (IS_FIXED(tbl)) {
reds = db_mark_all_deleted_hash(tbl, reds);
} else {
@@ -3163,11 +3189,16 @@ static SWord db_delete_all_objects_hash(Process* p, DbTable* tbl, SWord reds)
return reds;
db_create_hash(p, tbl);
- erts_atomic_set_nob(&tbl->hash.common.nitems, 0);
+ RESET_NITEMS(tbl);
}
return reds;
}
+static Eterm db_delete_all_objects_get_nitems_from_holder_hash(Process* p,
+ Eterm nitems_holder){
+ return nitems_holder;
+}
+
void db_foreach_offheap_hash(DbTable *tbl,
void (*func)(ErlOffHeap *, void *),
void * arg)
diff --git a/erts/emulator/beam/erl_db_tree.c b/erts/emulator/beam/erl_db_tree.c
index f9ba04f399..492ea81b63 100644
--- a/erts/emulator/beam/erl_db_tree.c
+++ b/erts/emulator/beam/erl_db_tree.c
@@ -51,9 +51,20 @@
#include "erl_db_tree_util.h"
#define GETKEY_WITH_POS(Keypos, Tplp) (*((Tplp) + Keypos))
-#define NITEMS(tb) ((int)erts_atomic_read_nob(&(tb)->common.nitems))
-#define TREE_MAX_ELEMENTS 0xFFFFFFFFUL
+#define NITEMS_CENTRALIZED(tb) \
+ ((Sint)erts_flxctr_read_centralized(&(tb)->common.counters, \
+ ERTS_DB_TABLE_NITEMS_COUNTER_ID))
+#define ADD_NITEMS(DB, TO_ADD) \
+ erts_flxctr_add(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID, TO_ADD)
+#define INC_NITEMS(DB) \
+ erts_flxctr_inc(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID)
+#define INC_NITEMS_CENTRALIZED(DB) \
+ erts_flxctr_inc_read_centralized(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID)
+#define RESET_NITEMS(DB) \
+ erts_flxctr_reset(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID)
+#define IS_CENTRALIZED_CTR(tb) (!(tb)->common.counters.is_decentralized)
+#define APPROX_MEM_CONSUMED(tb) erts_flxctr_read_approx(&(tb)->common.counters, ERTS_DB_TABLE_MEM_COUNTER_ID)
#define TOPN_NODE(Dtt, Pos) \
(((Pos) < Dtt->pos) ? \
@@ -296,7 +307,7 @@ int tree_balance_right(TreeDbTerm **this);
static int delsub(TreeDbTerm **this);
static TreeDbTerm *slot_search(Process *p, TreeDbTerm *root, Sint slot,
DbTable *tb, DbTableTree *stack_container,
- CATreeRootIterator *iter);
+ CATreeRootIterator *iter, int* is_EOT);
static TreeDbTerm *find_node(DbTableCommon *tb, TreeDbTerm *root,
Eterm key, DbTableTree *stack_container);
static TreeDbTerm **find_node2(DbTableCommon *tb, TreeDbTerm **root, Eterm key);
@@ -433,8 +444,12 @@ static void db_foreach_offheap_tree(DbTable *,
void (*)(ErlOffHeap *, void *),
void *);
-static SWord db_delete_all_objects_tree(Process* p, DbTable* tbl, SWord reds);
-
+static SWord db_delete_all_objects_tree(Process* p,
+ DbTable* tbl,
+ SWord reds,
+ Eterm* nitems_holder_wb);
+static Eterm db_delete_all_objects_get_nitems_from_holder_tree(Process* p,
+ Eterm nitems_holder);
#ifdef HARDDEBUG
static void db_check_table_tree(DbTable *tbl);
#endif
@@ -478,6 +493,7 @@ DbTableMethod db_tree =
db_select_replace_continue_tree,
db_take_tree,
db_delete_all_objects_tree,
+ db_delete_all_objects_get_nitems_from_holder_tree,
db_free_empty_table_tree,
db_free_table_continue_tree,
db_print_tree,
@@ -595,7 +611,8 @@ int db_last_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root,
}
if (stack) {
PUSH_NODE(stack, this);
- stack->slot = NITEMS(tbl);
+ /* Always centralized counters when static stack is used */
+ stack->slot = NITEMS_CENTRALIZED(tbl);
release_stack(tbl,stack_container,stack);
}
*ret = db_copy_key(p, tbl, &this->dbterm);
@@ -661,10 +678,7 @@ int db_put_tree_common(DbTableCommon *tb, TreeDbTerm **root, Eterm obj,
for (;;)
if (!*this) { /* Found our place */
state = 1;
- if (erts_atomic_inc_read_nob(&tb->nitems) >= TREE_MAX_ELEMENTS) {
- erts_atomic_dec_nob(&tb->nitems);
- return DB_ERROR_SYSRES;
- }
+ INC_NITEMS(((DbTable*)tb));
*this = new_dbterm(tb, obj);
(*this)->balance = 0;
(*this)->left = (*this)->right = NULL;
@@ -888,7 +902,7 @@ int db_slot_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root,
TreeDbTerm *st;
Eterm *hp, *hend;
Eterm copy;
-
+ int is_EOT = 0;
/*
* The notion of a "slot" is not natural in a tree, but we try to
* simulate it by giving the n'th node in the tree instead.
@@ -899,10 +913,10 @@ int db_slot_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root,
if (is_not_small(slot_term) ||
((slot = signed_val(slot_term)) < 0) ||
- (slot > NITEMS(tbl)))
+ (IS_CENTRALIZED_CTR(tbl) && slot > NITEMS_CENTRALIZED(tbl)))
return DB_ERROR_BADPARAM;
- if (slot == NITEMS(tbl)) {
+ if (IS_CENTRALIZED_CTR(tbl) && slot == NITEMS_CENTRALIZED(tbl)) {
*ret = am_EOT;
return DB_ERROR_NONE;
}
@@ -912,7 +926,11 @@ int db_slot_tree_common(Process *p, DbTable *tbl, TreeDbTerm *root,
* are counted from 1 and up.
*/
++slot;
- st = slot_search(p, root, slot, tbl, stack_container, iter);
+ st = slot_search(p, root, slot, tbl, stack_container, iter, &is_EOT);
+ if (is_EOT) {
+ *ret = am_EOT;
+ return DB_ERROR_NONE;
+ }
if (st == NULL) {
*ret = am_false;
return DB_ERROR_UNSPEC;
@@ -2244,7 +2262,8 @@ void db_print_tree_common(fmtfn_t to, void *to_arg,
erts_print(to, to_arg, "\n"
"------------------------------------------------\n");
#else
- erts_print(to, to_arg, "Ordered set (AVL tree), Elements: %d\n", NITEMS(tbl));
+ erts_print(to, to_arg, "Ordered set (AVL tree), Elements: %d\n",
+ erts_flxctr_read_approx(&tbl->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID));
#endif
}
@@ -2281,24 +2300,41 @@ static SWord db_free_table_continue_tree(DbTable *tbl, SWord reds)
(DbTable *) tb,
(void *) tb->static_stack.array,
sizeof(TreeDbTerm *) * STACK_NEED);
- ASSERT((erts_atomic_read_nob(&tb->common.memory_size)
- == sizeof(DbTable)) ||
- (erts_atomic_read_nob(&tb->common.memory_size)
- == (sizeof(DbTable) + sizeof(DbFixation))));
+ ASSERT(erts_flxctr_is_snapshot_ongoing(&tb->common.counters) ||
+ ((APPROX_MEM_CONSUMED(tb)
+ == sizeof(DbTable)) ||
+ (APPROX_MEM_CONSUMED(tb)
+ == (sizeof(DbTable) + sizeof(DbFixation)))));
}
return reds;
}
-static SWord db_delete_all_objects_tree(Process* p, DbTable* tbl, SWord reds)
+static SWord db_delete_all_objects_tree(Process* p,
+ DbTable* tbl,
+ SWord reds,
+ Eterm* nitems_holder_wb)
{
+ if (nitems_holder_wb != NULL) {
+ Uint nr_of_items =
+ erts_flxctr_read_centralized(&tbl->common.counters,
+ ERTS_DB_TABLE_NITEMS_COUNTER_ID);
+ *nitems_holder_wb = erts_make_integer(nr_of_items, p);
+ }
reds = db_free_table_continue_tree(tbl, reds);
if (reds < 0)
return reds;
db_create_tree(p, tbl);
- erts_atomic_set_nob(&tbl->tree.common.nitems, 0);
+ RESET_NITEMS(tbl);
return reds;
}
+static Eterm db_delete_all_objects_get_nitems_from_holder_tree(Process* p,
+ Eterm holder)
+{
+ (void)p;
+ return holder;
+}
+
static void do_db_tree_foreach_offheap(TreeDbTerm *,
void (*)(ErlOffHeap *, void *),
void *);
@@ -2383,7 +2419,7 @@ static TreeDbTerm *linkout_tree(DbTableCommon *tb, TreeDbTerm **root,
tstack[tpos++] = this;
state = delsub(this);
}
- erts_atomic_dec_nob(&tb->nitems);
+ DEC_NITEMS(((DbTable*)tb));
break;
}
}
@@ -2450,7 +2486,7 @@ static TreeDbTerm *linkout_object_tree(DbTableCommon *tb, TreeDbTerm **root,
tstack[tpos++] = this;
state = delsub(this);
}
- erts_atomic_dec_nob(&tb->nitems);
+ DEC_NITEMS(((DbTable*)tb));
break;
}
}
@@ -2745,7 +2781,8 @@ static int delsub(TreeDbTerm **this)
static TreeDbTerm *slot_search(Process *p, TreeDbTerm *root,
Sint slot, DbTable *tb,
DbTableTree *stack_container,
- CATreeRootIterator *iter)
+ CATreeRootIterator *iter,
+ int* is_EOT)
{
TreeDbTerm *this;
TreeDbTerm *tmp;
@@ -2837,8 +2874,12 @@ static TreeDbTerm *slot_search(Process *p, TreeDbTerm *root,
break;
next_root:
- if (!iter)
+ if (!iter) {
+ if (stack->slot == (slot-1)) {
+ *is_EOT = 1;
+ }
break; /* EOT */
+ }
ASSERT(slot > stack->slot);
if (lastobj) {
@@ -2846,8 +2887,12 @@ next_root:
lastobj = NULL;
}
pp = catree_find_next_root(iter, &lastkey);
- if (!pp)
+ if (!pp) {
+ if (stack->slot == (slot-1)) {
+ *is_EOT = 1;
+ }
break; /* EOT */
+ }
root = *pp;
stack->pos = 0;
find_next(&tb->common, root, stack, lastkey);
diff --git a/erts/emulator/beam/erl_db_tree_util.h b/erts/emulator/beam/erl_db_tree_util.h
index 02df74678d..ba4a8f79e5 100644
--- a/erts/emulator/beam/erl_db_tree_util.h
+++ b/erts/emulator/beam/erl_db_tree_util.h
@@ -25,6 +25,8 @@
** Internal functions and macros used by both the CA tree and the AVL tree
*/
+
+#if defined(ARCH_32)
/*
** A stack of this size is enough for an AVL tree with more than
** 0xFFFFFFFF elements. May be subject to change if
@@ -34,8 +36,19 @@
** Where n denotes the number of nodes, h(n) the height of the tree
** with n nodes and log is the binary logarithm.
*/
-
#define STACK_NEED 50
+#elif defined(ARCH_64)
+/*
+** A stack of this size is enough for an AVL tree with more than
+** 2^61 elements.
+** The Maximal height of an AVL tree is calculated as above.
+*/
+#define STACK_NEED 90
+#else
+#error "Unsported architecture"
+#endif
+
+
#define PUSH_NODE(Dtt, Tdt) \
((Dtt)->array[(Dtt)->pos++] = Tdt)
@@ -50,6 +63,9 @@
#define EMPTY_NODE(Dtt) (TOP_NODE(Dtt) == NULL)
+#define DEC_NITEMS(DB) \
+ erts_flxctr_dec(&(DB)->common.counters, ERTS_DB_TABLE_NITEMS_COUNTER_ID)
+
static ERTS_INLINE void free_term(DbTable *tb, TreeDbTerm* p)
{
db_free_term(tb, p, offsetof(TreeDbTerm, dbterm));
diff --git a/erts/emulator/beam/erl_db_util.h b/erts/emulator/beam/erl_db_util.h
index e3d3c0e804..02d4dd6c9a 100644
--- a/erts/emulator/beam/erl_db_util.h
+++ b/erts/emulator/beam/erl_db_util.h
@@ -21,6 +21,7 @@
#ifndef _DB_UTIL_H
#define _DB_UTIL_H
+#include "erl_flxctr.h"
#include "global.h"
#include "erl_message.h"
#include "erl_bif_unique.h"
@@ -207,8 +208,12 @@ typedef struct db_table_method
enum DbIterSafety*);
int (*db_take)(Process *, DbTable *, Eterm, Eterm *);
- SWord (*db_delete_all_objects)(Process* p, DbTable* db, SWord reds);
-
+ SWord (*db_delete_all_objects)(Process* p,
+ DbTable* db,
+ SWord reds,
+ Eterm* nitems_holder_wb);
+ Eterm (*db_delete_all_objects_get_nitems_from_holder)(Process* p,
+ Eterm nitems_holder);
int (*db_free_empty_table)(DbTable* db);
SWord (*db_free_table_continue)(DbTable* db, SWord reds);
@@ -257,6 +262,9 @@ typedef struct {
DbTable *prev;
} DbTableList;
+#define ERTS_DB_TABLE_NITEMS_COUNTER_ID 0
+#define ERTS_DB_TABLE_MEM_COUNTER_ID 1
+
/*
* This structure contains data for all different types of database
* tables. Note that these fields must match the same fields
@@ -281,8 +289,11 @@ typedef struct db_table_common {
Eterm the_name; /* an atom */
Binary *btid;
DbTableMethod* meth; /* table methods */
- erts_atomic_t nitems; /* Total number of items in table */
- erts_atomic_t memory_size;/* Total memory size. NOTE: in bytes! */
+ /* The ErtsFlxCtr below contains:
+ * - Total number of items in table
+ * - Total memory size (NOTE: in bytes!) */
+ ErtsFlxCtr counters;
+ char extra_for_flxctr[ERTS_FLXCTR_NR_OF_EXTRA_BYTES(2)];
struct { /* Last fixation time */
ErtsMonotonicTime monotonic;
ErtsMonotonicTime offset;
@@ -315,6 +326,7 @@ typedef struct db_table_common {
#define DB_BUSY (1 << 12)
#define DB_CATREE_FORCE_SPLIT (1 << 31) /* erts_debug */
+#define DB_CATREE_DEBUG_RANDOM_SPLIT_JOIN (1 << 30) /* erts_debug */
#define IS_HASH_TABLE(Status) (!!((Status) & \
(DB_BAG | DB_SET | DB_DUPLICATE_BAG)))
diff --git a/erts/emulator/beam/erl_flxctr.c b/erts/emulator/beam/erl_flxctr.c
new file mode 100644
index 0000000000..35f4a21508
--- /dev/null
+++ b/erts/emulator/beam/erl_flxctr.c
@@ -0,0 +1,370 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * Author: Kjell Winblad
+ */
+
+#include "erl_flxctr.h"
+
+static int reader_groups_array_size = 0;
+#define ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS (reader_groups_array_size)
+
+static int erts_flxctr_read_ctx_bin_dtor(Binary *context_bin);
+static int erts_flxctr_wait_dtor(Binary *context_bin);
+
+typedef struct {
+ ErtsThrPrgrLaterOp later_op;
+ Process* process;
+ ErtsFlxCtrDecentralizedCtrArray* array;
+ ErtsFlxCtrDecentralizedCtrArray* next_array;
+ ErtsAlcType_t alloc_type;
+ int nr_of_counters;
+ Sint result[ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE];
+} DecentralizedReadSnapshotInfo;
+
+typedef enum {
+ ERTS_FLXCTR_SNAPSHOT_NOT_ONGOING = 0,
+ ERTS_FLXCTR_SNAPSHOT_ONGOING = 1,
+ ERTS_FLXCTR_SNAPSHOT_ONGOING_TP_THREAD_DO_FREE = 2
+} erts_flxctr_snapshot_status;
+
+static void
+thr_prg_wake_up_and_count(void* bin_p)
+{
+ Binary* bin = bin_p;
+ DecentralizedReadSnapshotInfo* info = ERTS_MAGIC_BIN_DATA(bin);
+ Process* p = info->process;
+ ErtsFlxCtrDecentralizedCtrArray* array = info->array;
+ ErtsFlxCtrDecentralizedCtrArray* next = info->next_array;
+ int i, sched;
+ /* Reset result array */
+ for (i = 0; i < info->nr_of_counters; i++) {
+ info->result[i] = 0;
+ }
+ /* Read result from snapshot */
+ for (sched = 0; sched < ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS; sched++) {
+ for (i = 0; i < info->nr_of_counters; i++) {
+ info->result[i] = info->result[i] +
+ erts_atomic_read_nob(&array->array[sched].counters[i]);
+ }
+ }
+ /* Update the next decentralized counter array */
+ for (i = 0; i < info->nr_of_counters; i++) {
+ erts_atomic_add_nob(&next->array[0].counters[i], info->result[i]);
+ }
+ /* Announce that the snapshot is done */
+ {
+ Sint expected = ERTS_FLXCTR_SNAPSHOT_ONGOING;
+ if (expected != erts_atomic_cmpxchg_mb(&next->snapshot_status,
+ ERTS_FLXCTR_SNAPSHOT_NOT_ONGOING,
+ expected)) {
+ /* The CAS failed which means that this thread need to free the next array. */
+ erts_free(info->alloc_type, next->block_start);
+ }
+ }
+ /* Resume the process that requested the snapshot */
+ erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ if (!ERTS_PROC_IS_EXITING(p)) {
+ erts_resume(p, ERTS_PROC_LOCK_STATUS);
+ }
+ /* Free the memory that is no longer needed */
+ erts_free(info->alloc_type, array->block_start);
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ erts_proc_dec_refc(p);
+ erts_bin_release(bin);
+}
+
+typedef struct {
+ ErtsThrPrgrLaterOp later_op;
+ Process* process;
+} ErtsFlxCtrWakeUpLaterInfo;
+
+static void
+thr_prg_wake_up_later(void* bin_p)
+{
+ Binary* bin = bin_p;
+ ErtsFlxCtrWakeUpLaterInfo* info = ERTS_MAGIC_BIN_DATA(bin);
+ Process* p = info->process;
+ /* Resume the requesting process */
+ erts_proc_lock(p, ERTS_PROC_LOCK_STATUS);
+ if (!ERTS_PROC_IS_EXITING(p)) {
+ erts_resume(p, ERTS_PROC_LOCK_STATUS);
+ }
+ erts_proc_unlock(p, ERTS_PROC_LOCK_STATUS);
+ /* Free data */
+ erts_proc_dec_refc(p);
+ erts_bin_release(bin);
+}
+
+static
+int erts_flxctr_read_ctx_bin_dtor(Binary *context_bin) {
+ (void)context_bin;
+ return 1;
+}
+
+static
+int erts_flxctr_wait_dtor(Binary *context_bin) {
+ (void)context_bin;
+ return 1;
+}
+
+static void suspend_until_thr_prg(Process* p)
+{
+ Binary* state_bin;
+ ErtsFlxCtrWakeUpLaterInfo* info;
+ state_bin = erts_create_magic_binary(sizeof(ErtsFlxCtrWakeUpLaterInfo),
+ erts_flxctr_wait_dtor);
+ info = ERTS_MAGIC_BIN_DATA(state_bin);
+ info->process = p;
+ erts_refc_inctest(&state_bin->intern.refc, 1);
+ erts_suspend(p, ERTS_PROC_LOCK_MAIN, NULL);
+ erts_proc_inc_refc(p);
+ ERTS_VBUMP_ALL_REDS(p);
+ erts_schedule_thr_prgr_later_op(thr_prg_wake_up_later, state_bin, &info->later_op);
+}
+
+
+static ErtsFlxCtrDecentralizedCtrArray*
+create_decentralized_ctr_array(ErtsAlcType_t alloc_type, Uint nr_of_counters) {
+ /* Allocate an ErtsFlxCtrDecentralizedCtrArray and make sure that
+ the array field is located at the start of a cache line */
+ char* bytes =
+ erts_alloc(alloc_type,
+ sizeof(ErtsFlxCtrDecentralizedCtrArray) +
+ (sizeof(ErtsFlxCtrDecentralizedCtrArrayElem) *
+ ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS) +
+ ERTS_CACHE_LINE_SIZE);
+ void* block_start = bytes;
+ int bytes_to_next_cacheline_border;
+ ErtsFlxCtrDecentralizedCtrArray* array;
+ int i, sched;
+ bytes = &bytes[offsetof(ErtsFlxCtrDecentralizedCtrArray, array)];
+ bytes_to_next_cacheline_border =
+ ERTS_CACHE_LINE_SIZE - (((Uint)bytes) % ERTS_CACHE_LINE_SIZE);
+ array = (ErtsFlxCtrDecentralizedCtrArray*)
+ (&bytes[bytes_to_next_cacheline_border -
+ (int)offsetof(ErtsFlxCtrDecentralizedCtrArray, array)]);
+ ASSERT(((Uint)array->array) % ERTS_CACHE_LINE_SIZE == 0);
+ ASSERT(((Uint)array - (Uint)block_start) <= ERTS_CACHE_LINE_SIZE);
+ /* Initialize fields */
+ erts_atomic_init_nob(&array->snapshot_status, ERTS_FLXCTR_SNAPSHOT_ONGOING);
+ for (sched = 0; sched < ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS; sched++) {
+ for (i = 0; i < nr_of_counters; i++) {
+ erts_atomic_init_nob(&array->array[sched].counters[i], 0);
+ }
+ }
+ array->block_start = block_start;
+ return array;
+}
+
+void erts_flxctr_setup(int decentralized_counter_groups)
+{
+ reader_groups_array_size = decentralized_counter_groups+1;
+}
+
+void erts_flxctr_init(ErtsFlxCtr* c,
+ int is_decentralized,
+ Uint nr_of_counters,
+ ErtsAlcType_t alloc_type)
+{
+ ASSERT(nr_of_counters <= ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE);
+ c->is_decentralized = is_decentralized;
+ c->nr_of_counters = nr_of_counters;
+ if (c->is_decentralized) {
+ ErtsFlxCtrDecentralizedCtrArray* array =
+ create_decentralized_ctr_array(alloc_type, nr_of_counters);
+ erts_atomic_set_nob(&array->snapshot_status,
+ ERTS_FLXCTR_SNAPSHOT_NOT_ONGOING);
+ erts_atomic_init_nob(&c->u.counters_ptr, (Sint)array);
+ ASSERT(((Uint)array->array) % ERTS_CACHE_LINE_SIZE == 0);
+ } else {
+ int i;
+ for (i = 0; i < nr_of_counters; i++) {
+ erts_atomic_init_nob(&c->u.counters[i], 0);
+ }
+ }
+}
+
+void erts_flxctr_destroy(ErtsFlxCtr* c, ErtsAlcType_t type)
+{
+ if (c->is_decentralized) {
+ if (erts_flxctr_is_snapshot_ongoing(c)) {
+ ErtsFlxCtrDecentralizedCtrArray* array =
+ ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c);
+ /* Try to delegate the resposibilty of freeing to
+ thr_prg_wake_up_and_count */
+ Sint expected = ERTS_FLXCTR_SNAPSHOT_ONGOING;
+ if (expected !=
+ erts_atomic_cmpxchg_mb(&array->snapshot_status,
+ ERTS_FLXCTR_SNAPSHOT_ONGOING_TP_THREAD_DO_FREE,
+ expected)) {
+ /* The delegation was unsuccessful which means that no
+ snapshot is ongoing anymore and the freeing needs
+ to be done here */
+ ERTS_ASSERT(!erts_flxctr_is_snapshot_ongoing(c));
+ erts_free(type, array->block_start);
+ }
+ } else {
+ erts_free(type, ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c)->block_start);
+ }
+ }
+}
+
+ErtsFlxCtrSnapshotResult
+erts_flxctr_snapshot(ErtsFlxCtr* c,
+ ErtsAlcType_t alloc_type,
+ Process* p)
+{
+ if (c->is_decentralized) {
+ ErtsFlxCtrDecentralizedCtrArray* array = ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c);
+ if (erts_flxctr_is_snapshot_ongoing(c)) {
+ /* Let the caller try again later */
+ ErtsFlxCtrSnapshotResult res =
+ {.type = ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP};
+ suspend_until_thr_prg(p);
+ return res;
+ } else {
+ Eterm* hp;
+ Binary* state_bin;
+ Eterm state_mref;
+ DecentralizedReadSnapshotInfo* info;
+ ErtsFlxCtrDecentralizedCtrArray* new_array =
+ create_decentralized_ctr_array(alloc_type, c->nr_of_counters);
+ int success =
+ ((Sint)array) == erts_atomic_cmpxchg_mb(&c->u.counters_ptr,
+ (Sint)new_array,
+ (Sint)array);
+ if (!success) {
+ /* Let the caller try again later */
+ ErtsFlxCtrSnapshotResult res =
+ {.type = ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP};
+ suspend_until_thr_prg(p);
+ erts_free(alloc_type, new_array->block_start);
+ return res;
+ }
+ /* Create binary with info about the operation that can be
+ sent to the caller and to a thread progress function */
+ state_bin =
+ erts_create_magic_binary(sizeof(DecentralizedReadSnapshotInfo),
+ erts_flxctr_read_ctx_bin_dtor);
+ hp = HAlloc(p, ERTS_MAGIC_REF_THING_SIZE);
+ state_mref = erts_mk_magic_ref(&hp, &MSO(p), state_bin);
+ info = ERTS_MAGIC_BIN_DATA(state_bin);
+ info->alloc_type = alloc_type;
+ info->array = array;
+ info->next_array = new_array;
+ info->process = p;
+ info->nr_of_counters = c->nr_of_counters;
+ erts_proc_inc_refc(p);
+ erts_refc_inctest(&state_bin->intern.refc, 2);
+ erts_suspend(p, ERTS_PROC_LOCK_MAIN, NULL);
+ ERTS_VBUMP_ALL_REDS(p);
+ erts_schedule_thr_prgr_later_op(thr_prg_wake_up_and_count,
+ state_bin,
+ &info->later_op);
+ {
+ ErtsFlxCtrSnapshotResult res = {
+ .type = ERTS_FLXCTR_GET_RESULT_AFTER_TRAP,
+ .trap_resume_state = state_mref};
+ return res;
+ }
+ }
+ } else {
+ ErtsFlxCtrSnapshotResult res;
+ int i;
+ res.type = ERTS_FLXCTR_DONE;
+ for (i = 0; i < c->nr_of_counters; i++){
+ res.result[i] = erts_flxctr_read_centralized(c, i);
+ }
+ return res;
+ }
+}
+
+
+Sint erts_flxctr_get_snapshot_result_after_trap(Eterm result_holder,
+ Uint counter_nr)
+{
+ Binary* bin = erts_magic_ref2bin(result_holder);
+ DecentralizedReadSnapshotInfo* data = ERTS_MAGIC_BIN_DATA(bin);;
+ return data->result[counter_nr];
+}
+
+int erts_flxctr_is_snapshot_result(Eterm term)
+{
+ if (is_internal_magic_ref(term)) {
+ Binary* bin = erts_magic_ref2bin(term);
+ return ERTS_MAGIC_BIN_DESTRUCTOR(bin) == erts_flxctr_read_ctx_bin_dtor;
+ } else return 0;
+}
+
+Sint erts_flxctr_read_approx(ErtsFlxCtr* c,
+ Uint counter_nr)
+{
+ if (c->is_decentralized) {
+ ErtsFlxCtrDecentralizedCtrArray* counter = ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c);
+ Sint sum = 0;
+ int sched;
+ for (sched = 0; sched < ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS; sched++) {
+ sum = sum + erts_atomic_read_nob(&counter->array[sched].counters[counter_nr]);
+ }
+ return sum;
+ } else {
+ return erts_flxctr_read_centralized(c, counter_nr);
+ }
+}
+
+int erts_flxctr_is_snapshot_ongoing(ErtsFlxCtr* c)
+{
+ return c->is_decentralized &&
+ (ERTS_FLXCTR_SNAPSHOT_NOT_ONGOING !=
+ erts_atomic_read_acqb(&ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c)->snapshot_status));
+}
+
+int erts_flxctr_suspend_until_thr_prg_if_snapshot_ongoing(ErtsFlxCtr* c, Process* p)
+{
+ if (erts_flxctr_is_snapshot_ongoing(c)) {
+ suspend_until_thr_prg(p);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void erts_flxctr_reset(ErtsFlxCtr* c,
+ Uint counter_nr)
+{
+ if (c->is_decentralized) {
+ int sched;
+ ErtsFlxCtrDecentralizedCtrArray* counter =
+ ERTS_FLXCTR_GET_CTR_ARRAY_PTR(c);
+ for (sched = 0; sched < ERTS_FLXCTR_DECENTRALIZED_NO_SLOTS; sched++) {
+ erts_atomic_set_nob(&counter->array[sched].counters[counter_nr], 0);
+ }
+ } else {
+ erts_atomic_set_nob(&c->u.counters[counter_nr], 0);
+ }
+}
+
+
+void erts_flxctr_set_slot(int group) {
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ esdp->flxctr_slot_no = group;
+}
diff --git a/erts/emulator/beam/erl_flxctr.h b/erts/emulator/beam/erl_flxctr.h
new file mode 100644
index 0000000000..5cab02b9eb
--- /dev/null
+++ b/erts/emulator/beam/erl_flxctr.h
@@ -0,0 +1,406 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2019. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/**
+ * @file erl_flxctr.h
+ *
+ * @brief This file contains the API of a flexible counter. The
+ * counter can be configured during its initialization to be
+ * centralized or decentralized. The centralized configuration makes
+ * it possible to read the counter value extremely efficiently, but
+ * updates of the counter value can easily cause contention. The
+ * decentralized configuration has the reverse trade-off (i.e.,
+ * updates are efficient and scalable but reading the counter value is
+ * slow and may cause contention).
+ *
+ * @author Kjell Winblad
+ */
+
+#ifndef ERL_FLXCTR_H__
+#define ERL_FLXCTR_H__
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include "sys.h"
+#include "erl_vm.h"
+#include "global.h"
+#include "error.h"
+#include "bif.h"
+#include "big.h"
+#include "erl_binary.h"
+#include "bif.h"
+#include <stddef.h>
+
+/* Public Interface */
+
+#define ERTS_MAX_FLXCTR_GROUPS 256
+#define ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE (ERTS_CACHE_LINE_SIZE / sizeof(erts_atomic_t))
+
+typedef struct {
+ int nr_of_counters;
+ int is_decentralized;
+ union {
+ erts_atomic_t counters_ptr;
+ erts_atomic_t counters[1];
+ } u;
+} ErtsFlxCtr;
+
+#define ERTS_FLXCTR_NR_OF_EXTRA_BYTES(NR_OF_COUNTERS) \
+ ((NR_OF_COUNTERS-1) * sizeof(erts_atomic_t))
+
+/* Called by early_init */
+void erts_flxctr_setup(int decentralized_counter_groups);
+
+/**
+ * @brief Initializes an ErtsFlxCtr. The macro
+ * ERTS_FLXCTR_NR_OF_EXTRA_BYTES should be used to determine how much
+ * extra space that needs to be allocated directly after the
+ * ErtsFlxCtr when is_decentralized is set to zero. Each ErtsFlxCtr
+ * instance may contain up to ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE
+ * counters. These counters are numbered from zero to
+ * (ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE-1). Most of the functions in
+ * this module take a parameter named counter_nr that controls which
+ * of the ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE counters in the given
+ * ErtsFlxCtr that should be operated on.
+ *
+ * @param c The counter to initialize
+ * @param is_decentralized Non-zero value to make c decentralized
+ * @param nr_of_counters The number of counters included in c
+ * (max ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE)
+ * @param alloc_type
+ */
+void erts_flxctr_init(ErtsFlxCtr* c,
+ int is_decentralized,
+ Uint nr_of_counters,
+ ErtsAlcType_t alloc_type);
+
+/**
+ * @brief Destroys an initialized counter.
+ *
+ * @param c The counter that should be destroyed
+ * @param alloc_type The allocation type (needs to be the same as the
+ * one passed to erts_flxctr_init when c was
+ * initialized)
+ */
+void erts_flxctr_destroy(ErtsFlxCtr* c, ErtsAlcType_t alloc_type);
+
+/**
+ * @brief Adds to_add to the counter with counter_nr in c
+ *
+ * @param c the ErtsFlxCtr to operate on
+ * @param counter_nr The number of the counter in c to modify
+ * @param to_add The amount that should be added to the specified counter
+ */
+ERTS_GLB_INLINE
+void erts_flxctr_add(ErtsFlxCtr* c,
+ Uint counter_nr,
+ int to_add);
+
+/**
+ * @brief Increases the specified counter by 1
+ *
+ * @param c The ErtsFlxCtr instance to operate on
+ * @param counter_nr The number of the counter within c to operate on
+ */
+ERTS_GLB_INLINE
+void erts_flxctr_inc(ErtsFlxCtr* c,
+ Uint counter_nr);
+
+/**
+ * @brief Decreases the specified counter by 1
+ */
+ERTS_GLB_INLINE
+void erts_flxctr_dec(ErtsFlxCtr* c,
+ Uint counter_nr);
+
+/**
+ * @brief This function tries to return the current value of the
+ * specified counter but may return an incorrect result if the counter
+ * is decentralized and other threads are accessing the counter
+ * concurrently.
+ *
+ * @param c The ErtsFlxCtr instance to operate on
+ * @param counter_nr The number of the counter within c to operate on
+ *
+ * @return A snapshot of the specifed counter if c is centralized or a
+ * possibly incorrect estimate of the counter value if c is
+ * decentralized
+ */
+Sint erts_flxctr_read_approx(ErtsFlxCtr* c,
+ Uint counter_nr);
+
+/**
+ * @brief This function can only be used together with an ErtsFlxCtr
+ * that is configured to be centralized. The function increments the
+ * specified counter by 1 and returns the value of the counter after
+ * the increment.
+ */
+ERTS_GLB_INLINE
+Sint erts_flxctr_inc_read_centralized(ErtsFlxCtr* c,
+ Uint counter_nr);
+
+/**
+ * @brief This function can only be used together with a ErtsFlxCtr
+ * that is configured to be centralized. The function decrements the
+ * specified counter by 1 and returns the value of the counter after
+ * the operation.
+ */
+ERTS_GLB_INLINE
+Sint erts_flxctr_dec_read_centralized(ErtsFlxCtr* c,
+ Uint counter_nr);
+
+/**
+ * @brief This function can only be used together with an ErtsFlxCtr
+ * that is configured to be centralized. The function returns the
+ * current value of the specified counter.
+ */
+ERTS_GLB_INLINE
+Sint erts_flxctr_read_centralized(ErtsFlxCtr* c,
+ Uint counter_nr);
+
+
+typedef enum {
+ ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP,
+ ERTS_FLXCTR_DONE,
+ ERTS_FLXCTR_GET_RESULT_AFTER_TRAP
+} ErtsFlxctrSnapshotResultType;
+
+typedef struct {
+ ErtsFlxctrSnapshotResultType type;
+ Eterm trap_resume_state;
+ Sint result[ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE];
+} ErtsFlxCtrSnapshotResult;
+
+/**
+ * @brief This function initiates an atomic snapshot of an ErtsFlxCtr
+ * to read out the values of one or more of the counters that are
+ * stored in the given ErtsFlxCtr. The caller needs to perform
+ * different actions after the return of this function depending on
+ * the value of the type field in the returned struct:
+ *
+ * - The caller needs to trap and try again after the trap if the
+ * return value has the type ERTS_FLXCTR_TRY_AGAIN_AFTER_TRAP.
+ *
+ * - The caller can get the result directly from the result field of
+ * the returned struct if the return value has the type
+ * ERTS_FLXCTR_DONE. The value at index i in the result field
+ * correspond to counter number i.
+ *
+ * - Finally, if the return value has the type
+ * ERTS_FLXCTR_GET_RESULT_AFTER_TRAP, then the caller needs to save
+ * the value of the field trap_resume_state from the returned struct
+ * and trap. After the trap, the values of the counters can be
+ * obtained by using the function
+ * erts_flxctr_get_snapshot_result_after_trap. Note that the
+ * function erts_flxctr_is_snapshot_result can be used to check if a
+ * value is obtained from the trap_resume_state field in the
+ * returned struct (this can be useful when the calling function
+ * wakes up again after the trap).
+ *
+ * The snapshot operation that is initiated by this function should be
+ * considered to be ongoing from the issuing of this function until a
+ * struct with the type field set to ERTS_FLXCTR_DONE has been
+ * returned from the function or until the caller of this function has
+ * woken up after trapping.
+ *
+ * @param c The ErtsFlxCtr that the snapshot shall be taken from
+ * @param alloc_type The allocation type (needs to be the same as the
+ * type passed to erts_flxctr_init when c was
+ * initialized)
+ * @param p The Erlang process that is doing the call
+ *
+ * @return See the description above
+ *
+ */
+ErtsFlxCtrSnapshotResult
+erts_flxctr_snapshot(ErtsFlxCtr* c,
+ ErtsAlcType_t alloc_type,
+ Process* p);
+
+/**
+ * @brief Checks if the parameter term is a snapshot result (i.e.,
+ * something obtained from the trap_resume_state field of an
+ * ErtsFlxCtrSnapshotResult struct that has been returned from
+ * erts_flxctr_snapshot).
+ *
+ * @param term The term to check
+ *
+ * @return A nonzero value iff the term is a snapshot result
+ */
+int erts_flxctr_is_snapshot_result(Eterm term);
+
+/**
+ * @brief Returns the result of a snapshot for a counter given a
+ * snapshot result returned by a call to erts_flxctr_snapshot (i.e.,
+ * the value stored in the trap_resume_state field of a struct
+ * returned by erts_flxctr_snapshot). The caller needs to trap between
+ * the return of erts_flxctr_snapshot and the call to this function.
+ */
+Sint erts_flxctr_get_snapshot_result_after_trap(Eterm trap_resume_state,
+ Uint counter_nr);
+
+/**
+ * @brief Resets the specified counter to 0. This function is unsafe
+ * to call while a snapshot operation may be active (initiated with
+ * the erts_flxctr_snapshot function).
+ */
+void erts_flxctr_reset(ErtsFlxCtr* c,
+ Uint counter_nr);
+
+/**
+ * @brief Checks if a snapshot operation is active (snapshots are
+ * initiated with the erts_flxctr_snapshot function).
+ *
+ * @return nonzero value iff a snapshot was active at some point
+ * between the invocation and return of the function
+ */
+int erts_flxctr_is_snapshot_ongoing(ErtsFlxCtr* c);
+
+/**
+ * @brief This function checks if a snapshot operation is ongoing
+ * (snapshots are initiated with the erts_flxctr_snapshot function)
+ * and suspend the given process until thread progress has happened if
+ * it detected an ongoing snapshot operation. The caller needs to trap
+ * if a non-zero value is returned.
+ *
+ * @param c The ErtsFlxCtr to check
+ * @param p The calling process
+ *
+ * @return nonzero value if the given process has got suspended
+ */
+int erts_flxctr_suspend_until_thr_prg_if_snapshot_ongoing(ErtsFlxCtr* c, Process* p);
+
+/* End: Public Interface */
+
+/* Internal Declarations */
+
+#define ERTS_FLXCTR_GET_CTR_ARRAY_PTR(C) \
+ ((ErtsFlxCtrDecentralizedCtrArray*) erts_atomic_read_acqb(&(C)->u.counters_ptr))
+#define ERTS_FLXCTR_GET_CTR_PTR(C, SCHEDULER_ID, COUNTER_ID) \
+ &(ERTS_FLXCTR_GET_CTR_ARRAY_PTR(C))->array[SCHEDULER_ID].counters[COUNTER_ID]
+
+
+typedef union {
+ erts_atomic_t counters[ERTS_FLXCTR_ATOMICS_PER_CACHE_LINE];
+ char pad[ERTS_CACHE_LINE_SIZE];
+} ErtsFlxCtrDecentralizedCtrArrayElem;
+
+typedef struct ErtsFlxCtrDecentralizedCtrArray {
+ void* block_start;
+ erts_atomic_t snapshot_status;
+ ErtsFlxCtrDecentralizedCtrArrayElem array[];
+} ErtsFlxCtrDecentralizedCtrArray;
+
+void erts_flxctr_set_slot(int group);
+
+ERTS_GLB_INLINE
+int erts_flxctr_get_slot_index(void);
+
+/* End: Internal Declarations */
+
+
+/* Implementation of inlined functions */
+
+#if ERTS_GLB_INLINE_INCL_FUNC_DEF
+
+ERTS_GLB_INLINE
+int erts_flxctr_get_slot_index(void)
+{
+ ErtsSchedulerData *esdp = erts_get_scheduler_data();
+ ASSERT(esdp && !ERTS_SCHEDULER_IS_DIRTY(esdp));
+ ASSERT(esdp->flxctr_slot_no > 0);
+ return esdp->flxctr_slot_no;
+}
+
+ERTS_GLB_INLINE
+void erts_flxctr_add(ErtsFlxCtr* c,
+ Uint counter_nr,
+ int to_add)
+{
+ ASSERT(counter_nr < c->nr_of_counters);
+ if (c->is_decentralized) {
+ erts_atomic_add_nob(ERTS_FLXCTR_GET_CTR_PTR(c,
+ erts_flxctr_get_slot_index(),
+ counter_nr),
+ to_add);
+ } else {
+ erts_atomic_add_nob(&c->u.counters[counter_nr], to_add);
+ }
+}
+
+ERTS_GLB_INLINE
+void erts_flxctr_inc(ErtsFlxCtr* c,
+ Uint counter_nr)
+{
+ ASSERT(counter_nr < c->nr_of_counters);
+ if (c->is_decentralized) {
+ erts_atomic_inc_nob(ERTS_FLXCTR_GET_CTR_PTR(c,
+ erts_flxctr_get_slot_index(),
+ counter_nr));
+ } else {
+ erts_atomic_inc_read_nob(&c->u.counters[counter_nr]);
+ }
+}
+
+ERTS_GLB_INLINE
+void erts_flxctr_dec(ErtsFlxCtr* c,
+ Uint counter_nr)
+{
+ ASSERT(counter_nr < c->nr_of_counters);
+ if (c->is_decentralized) {
+ erts_atomic_dec_nob(ERTS_FLXCTR_GET_CTR_PTR(c,
+ erts_flxctr_get_slot_index(),
+ counter_nr));
+ } else {
+ erts_atomic_dec_nob(&c->u.counters[counter_nr]);
+ }
+}
+
+ERTS_GLB_INLINE
+Sint erts_flxctr_inc_read_centralized(ErtsFlxCtr* c,
+ Uint counter_nr)
+{
+ ASSERT(counter_nr < c->nr_of_counters);
+ ASSERT(!c->is_decentralized);
+ return erts_atomic_inc_read_nob(&c->u.counters[counter_nr]);
+}
+
+ERTS_GLB_INLINE
+Sint erts_flxctr_dec_read_centralized(ErtsFlxCtr* c,
+ Uint counter_nr)
+{
+ ASSERT(counter_nr < c->nr_of_counters);
+ ASSERT(!c->is_decentralized);
+ return erts_atomic_dec_read_nob(&c->u.counters[counter_nr]);
+}
+
+ERTS_GLB_INLINE
+Sint erts_flxctr_read_centralized(ErtsFlxCtr* c,
+ Uint counter_nr)
+{
+ ASSERT(counter_nr < c->nr_of_counters);
+ ASSERT(!c->is_decentralized);
+ return erts_atomic_read_nob(&((erts_atomic_t*)(c->u.counters))[counter_nr]);
+}
+
+#endif /* #if ERTS_GLB_INLINE_INCL_FUNC_DEF */
+
+#endif /* ERL_FLXCTR_H__ */
diff --git a/erts/emulator/beam/erl_init.c b/erts/emulator/beam/erl_init.c
index 12750b9aa6..547e4064a2 100644
--- a/erts/emulator/beam/erl_init.c
+++ b/erts/emulator/beam/erl_init.c
@@ -593,6 +593,7 @@ void erts_usage(void)
erts_fprintf(stderr, " no_time_warp|single_time_warp|multi_time_warp\n");
erts_fprintf(stderr, "-d don't write a crash dump for internally detected errors\n");
erts_fprintf(stderr, " (halt(String) will still produce a crash dump)\n");
+ erts_fprintf(stderr, "-dcg set the limit for the number of decentralized counter groups\n");
erts_fprintf(stderr, "-fn[u|a|l] Control how filenames are interpreted\n");
erts_fprintf(stderr, "-hms size set minimum heap size in words (default %d)\n",
H_DEFAULT_SIZE);
@@ -785,6 +786,8 @@ early_init(int *argc, char **argv) /*
int dirty_io_scheds;
int max_reader_groups;
int reader_groups;
+ int max_decentralized_counter_groups;
+ int decentralized_counter_groups;
char envbuf[21]; /* enough for any 64-bit integer */
size_t envbufsz;
@@ -804,7 +807,8 @@ early_init(int *argc, char **argv) /*
erts_initialized = 0;
- erts_pre_early_init_cpu_topology(&max_reader_groups,
+ erts_pre_early_init_cpu_topology(&max_decentralized_counter_groups,
+ &max_reader_groups,
&ncpu,
&ncpuonln,
&ncpuavail);
@@ -865,6 +869,24 @@ early_init(int *argc, char **argv) /*
}
if (argv[i][0] == '-') {
switch (argv[i][1]) {
+ case 'd': {
+ char *sub_param = argv[i]+2;
+ if (has_prefix("cg", sub_param)) {
+ char *arg = get_arg(sub_param+2, argv[i+1], &i);
+ if (sscanf(arg, "%d", &max_decentralized_counter_groups) != 1) {
+ erts_fprintf(stderr,
+ "bad decentralized counter groups limit: %s\n", arg);
+ erts_usage();
+ }
+ if (max_decentralized_counter_groups < 0) {
+ erts_fprintf(stderr,
+ "bad decentralized counter groups limit: %d\n",
+ max_decentralized_counter_groups);
+ erts_usage();
+ }
+ }
+ break;
+ }
case 'r': {
char *sub_param = argv[i]+2;
if (has_prefix("g", sub_param)) {
@@ -1186,8 +1208,10 @@ early_init(int *argc, char **argv) /*
erts_early_init_cpu_topology(no_schedulers,
&max_main_threads,
max_reader_groups,
- &reader_groups);
-
+ &reader_groups,
+ max_decentralized_counter_groups,
+ &decentralized_counter_groups);
+ erts_flxctr_setup(decentralized_counter_groups);
{
erts_thr_late_init_data_t elid = ERTS_THR_LATE_INIT_DATA_DEF_INITER;
elid.mem.std.alloc = ethr_std_alloc;
diff --git a/erts/emulator/beam/erl_message.c b/erts/emulator/beam/erl_message.c
index 2a0fb9e2aa..6645341512 100644
--- a/erts/emulator/beam/erl_message.c
+++ b/erts/emulator/beam/erl_message.c
@@ -1590,6 +1590,9 @@ void erts_factory_undo(ErtsHeapFactory* factory)
factory->message->hfrag.next = factory->heap_frags;
else
factory->message->data.heap_frag = factory->heap_frags;
+ /* Set the message to NIL in order for this message not to be
+ treated as a distributed message by the cleanup_messages logic */
+ factory->message->m[0] = NIL;
erts_cleanup_messages(factory->message);
break;
case FACTORY_TMP:
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index 49dea8919b..4eb6c3e214 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -805,8 +805,7 @@ static int
node_table_cmp(void *venp1, void *venp2)
{
return ((((ErlNode *) venp1)->sysname == ((ErlNode *) venp2)->sysname) &&
- ((((ErlNode *) venp1)->creation == ((ErlNode *) venp2)->creation) ||
- (((ErlNode *) venp1)->creation == 0 || ((ErlNode *) venp2)->creation == 0))
+ ((((ErlNode *) venp1)->creation == ((ErlNode *) venp2)->creation))
? 0
: 1);
}
@@ -977,7 +976,7 @@ static void print_node(void *venp, void *vpndp)
if(pndp->sysname == NIL) {
erts_print(pndp->to, pndp->to_arg, "Name: %T ", enp->sysname);
}
- erts_print(pndp->to, pndp->to_arg, " %u", enp->creation);
+ erts_print(pndp->to, pndp->to_arg, " %d", enp->creation);
#ifdef DEBUG
erts_print(pndp->to, pndp->to_arg, " (refc=%ld)",
erts_refc_read(&enp->refc, 0));
@@ -1020,7 +1019,7 @@ void erts_print_node_info(fmtfn_t to,
/* ----------------------------------------------------------------------- */
void
-erts_set_this_node(Eterm sysname, Uint32 creation)
+erts_set_this_node(Eterm sysname, Uint creation)
{
ERTS_LC_ASSERT(erts_thr_progress_is_blocking());
ASSERT(2 <= de_refc_read(erts_this_dist_entry, 2));
diff --git a/erts/emulator/beam/erl_node_tables.h b/erts/emulator/beam/erl_node_tables.h
index c434926142..aa8af12555 100644
--- a/erts/emulator/beam/erl_node_tables.h
+++ b/erts/emulator/beam/erl_node_tables.h
@@ -95,6 +95,7 @@ enum dist_entry_state {
struct ErtsDistOutputBuf_ {
#ifdef DEBUG
Uint dbg_pattern;
+ byte *ext_startp;
byte *alloc_endp;
#endif
ErtsDistOutputBuf *next;
@@ -258,7 +259,7 @@ void erts_set_dist_entry_pending(DistEntry *);
void erts_set_dist_entry_connected(DistEntry *, Eterm, Uint);
ErlNode *erts_find_or_insert_node(Eterm, Uint32, Eterm);
void erts_schedule_delete_node(ErlNode *);
-void erts_set_this_node(Eterm, Uint32);
+void erts_set_this_node(Eterm, Uint);
Uint erts_node_table_size(void);
void erts_init_node_tables(int);
void erts_node_table_info(fmtfn_t, void *);
diff --git a/erts/emulator/beam/erl_posix_str.c b/erts/emulator/beam/erl_posix_str.c
index 7b3e640d3f..5b515d6e78 100644
--- a/erts/emulator/beam/erl_posix_str.c
+++ b/erts/emulator/beam/erl_posix_str.c
@@ -171,6 +171,9 @@ erl_errno_id(error)
#if defined(EIDRM) && (!defined(EINPROGRESS) || (EIDRM != EINPROGRESS))
case EIDRM: return "eidrm";
#endif
+#ifdef EILSEQ
+ case EILSEQ: return "eilseq";
+#endif
#ifdef EINIT
case EINIT: return "einit";
#endif
diff --git a/erts/emulator/beam/erl_proc_sig_queue.c b/erts/emulator/beam/erl_proc_sig_queue.c
index 4e9f177e51..f58a606d57 100644
--- a/erts/emulator/beam/erl_proc_sig_queue.c
+++ b/erts/emulator/beam/erl_proc_sig_queue.c
@@ -1019,6 +1019,8 @@ send_gen_exit_signal(Process *c_p, Eterm from_tag,
ref_sz = size_object(ref);
hsz += ref_sz;
+ reason_sz = 0; /* Set to silence gcc warning */
+
/* The reason was part of the control message,
just use copy it into the xsigd */
if (is_value(reason)) {
diff --git a/erts/emulator/beam/erl_process.c b/erts/emulator/beam/erl_process.c
index 4780b7fb21..1f6adb98ef 100644
--- a/erts/emulator/beam/erl_process.c
+++ b/erts/emulator/beam/erl_process.c
@@ -12095,6 +12095,7 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
Eterm watched;
Uint watcher_sz, ref_sz;
ErtsHeapFactory factory;
+ Sint reds_consumed = 0;
ASSERT(erts_monitor_is_target(mon) && mon->type == ERTS_MON_TYPE_DIST_PROC);
@@ -12139,12 +12140,13 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
watched,
ref,
reason);
+ reds_consumed = reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR);
switch (code) {
case ERTS_DSIG_SEND_CONTINUE:
+ case ERTS_DSIG_SEND_YIELD:
erts_set_gc_state(c_p, 0);
ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx);
- /* fall-through */
- case ERTS_DSIG_SEND_YIELD:
+ reds_consumed = reds; /* force yield */
break;
case ERTS_DSIG_SEND_OK:
break;
@@ -12165,7 +12167,7 @@ erts_proc_exit_handle_dist_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
erts_monitor_release(mon);
else
erts_monitor_release_both(mdp);
- return reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR);
+ return reds_consumed;
}
int
@@ -12352,6 +12354,7 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds)
ErtsLink *dlnk;
ErtsLinkData *ldp = NULL;
ErtsHeapFactory factory;
+ Sint reds_consumed = 0;
ASSERT(lnk->type == ERTS_LNK_TYPE_DIST_PROC);
dlnk = erts_link_to_other(lnk, &ldp);
@@ -12388,12 +12391,13 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds)
item,
reason,
SEQ_TRACE_TOKEN(c_p));
+ reds_consumed = reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR);
switch (code) {
+ case ERTS_DSIG_SEND_YIELD:
case ERTS_DSIG_SEND_CONTINUE:
erts_set_gc_state(c_p, 0);
ctxt->dist_state = erts_dsend_export_trap_context(c_p, &ctx);
- /* fall-through */
- case ERTS_DSIG_SEND_YIELD:
+ reds_consumed = reds; /* force yield */
break;
case ERTS_DSIG_SEND_OK:
break;
@@ -12414,7 +12418,7 @@ erts_proc_exit_handle_dist_link(ErtsLink *lnk, void *vctxt, Sint reds)
erts_link_release_both(ldp);
else if (lnk)
erts_link_release(lnk);
- return reds - (ctx.reds / TERM_TO_BINARY_LOOP_FACTOR);
+ return reds_consumed;
}
int
diff --git a/erts/emulator/beam/erl_process.h b/erts/emulator/beam/erl_process.h
index 711b73417d..6118c671ee 100644
--- a/erts/emulator/beam/erl_process.h
+++ b/erts/emulator/beam/erl_process.h
@@ -641,6 +641,7 @@ struct ErtsSchedulerData_ {
ErtsSchedType type;
Uint no; /* Scheduler number for normal schedulers */
Uint dirty_no; /* Scheduler number for dirty schedulers */
+ int flxctr_slot_no; /* slot nr when a flxctr is used */
struct enif_environment_t *current_nif;
Process *dirty_shadow_process;
Port *current_port;
@@ -1847,6 +1848,7 @@ int erts_resume_processes(ErtsProcList *);
void erts_deep_process_dump(fmtfn_t, void *);
Eterm erts_get_reader_groups_map(Process *c_p);
+Eterm erts_get_decentralized_counter_groups_map(Process *c_p);
Eterm erts_debug_reader_groups_map(Process *c_p, int groups);
Uint erts_debug_nbalance(void);
diff --git a/erts/emulator/beam/erl_utils.h b/erts/emulator/beam/erl_utils.h
index 880febba8b..430ac305c5 100644
--- a/erts/emulator/beam/erl_utils.h
+++ b/erts/emulator/beam/erl_utils.h
@@ -69,7 +69,6 @@ int erts_fit_in_bits_int32(Sint32);
int erts_fit_in_bits_uint(Uint);
Sint erts_list_length(Eterm);
int erts_is_builtin(Eterm, Eterm, int);
-Uint32 block_hash(byte *, unsigned, Uint32);
Uint32 make_hash2(Eterm);
Uint32 make_hash(Eterm);
Uint32 make_internal_hash(Eterm, Uint32 salt);
diff --git a/erts/emulator/beam/external.c b/erts/emulator/beam/external.c
index 13a2e4258c..ec67ab2aed 100644
--- a/erts/emulator/beam/external.c
+++ b/erts/emulator/beam/external.c
@@ -51,17 +51,18 @@
#define MAX_STRING_LEN 0xffff
-/*
- * MAX value for the creation field in pid, port and reference
- * for the old PID_EXT, PORT_EXT, REFERENCE_EXT and NEW_REFERENCE_EXT.
- * Older nodes (OTP 19-21) will send us these so we must be able to decode them.
- *
- * From OTP 22 DFLAG_BIG_CREATION is mandatory so this node will always
- * encode with new big 32-bit creations using NEW_PID_EXT, NEW_PORT_EXT
- * and NEWER_REFERENCE_EXT.
+/* MAX value for the creation field in pid, port and reference
+ for the local node and for the current external format.
+
+ Larger creation values than this are allowed in external pid, port and refs
+ encoded with NEW_PID_EXT, NEW_PORT_EXT and NEWER_REFERENCE_EXT.
+ The point here is to prepare for future upgrade to 32-bit creation.
+ OTP-19 (erts-8.0) can handle big creation values from other (newer) nodes,
+ but do not use big creation values for the local node yet,
+ as we still may have to communicate with older nodes.
*/
-#define ERTS_MAX_TINY_CREATION (3)
-#define is_tiny_creation(Cre) ((unsigned)(Cre) <= ERTS_MAX_TINY_CREATION)
+#define ERTS_MAX_LOCAL_CREATION (3)
+#define is_valid_creation(Cre) ((unsigned)(Cre) <= ERTS_MAX_LOCAL_CREATION)
#undef ERTS_DEBUG_USE_DIST_SEP
#ifdef DEBUG
@@ -700,6 +701,7 @@ dist_ext_size(ErtsDistExternal *edep)
} else {
sz -= sizeof(ErtsAtomTranslationTable);
}
+ ASSERT(sz % 4 == 0);
return sz;
}
@@ -707,8 +709,9 @@ Uint
erts_dist_ext_size(ErtsDistExternal *edep)
{
Uint sz = dist_ext_size(edep);
+ sz += 4; /* may need to pad to 8-byte-align ErtsDistExternalData */
sz += edep->data[0].frag_id * sizeof(ErtsDistExternalData);
- return sz + ERTS_EXTRA_DATA_ALIGN_SZ(sz);
+ return sz;
}
Uint
@@ -750,6 +753,8 @@ erts_make_dist_ext_copy(ErtsDistExternal *edep, ErtsDistExternal *new_edep)
erts_ref_dist_entry(new_edep->dep);
ep += dist_ext_sz;
+ ep += (UWord)ep & 4; /* 8-byte alignment for ErtsDistExternalData */
+ ASSERT((UWord)ep % 8 == 0);
new_edep->data = (ErtsDistExternalData*)ep;
sys_memzero(new_edep->data, sizeof(ErtsDistExternalData) * edep->data->frag_id);
@@ -2433,8 +2438,7 @@ enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags)
Eterm sysname = ((is_internal_pid(pid) && (dflags & DFLAG_INTERNAL_TAGS))
? INTERNAL_LOCAL_SYSNAME : pid_node_name(pid));
Uint32 creation = pid_creation(pid);
-
- *ep++ = NEW_PID_EXT;
+ byte* tagp = ep++;
/* insert atom here containing host and sysname */
ep = enc_atom(acmp, sysname, ep, dflags);
@@ -2446,8 +2450,15 @@ enc_pid(ErtsAtomCacheMap *acmp, Eterm pid, byte* ep, Uint32 dflags)
ep += 4;
put_int32(os, ep);
ep += 4;
- put_int32(creation, ep);
- ep += 4;
+ if (creation <= ERTS_MAX_LOCAL_CREATION) {
+ *tagp = PID_EXT;
+ *ep++ = creation;
+ } else {
+ ASSERT(is_external_pid(pid));
+ *tagp = NEW_PID_EXT;
+ put_int32(creation, ep);
+ ep += 4;
+ }
return ep;
}
@@ -2567,7 +2578,7 @@ dec_pid(ErtsDistExternal *edep, ErtsHeapFactory* factory, byte* ep,
if (tag == PID_EXT) {
cre = get_int8(ep);
ep += 1;
- if (!is_tiny_creation(cre)) {
+ if (!is_valid_creation(cre)) {
return NULL;
}
} else {
@@ -2828,18 +2839,25 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_ref(obj))
? INTERNAL_LOCAL_SYSNAME : ref_node_name(obj));
Uint32 creation = ref_creation(obj);
+ byte* tagp = ep++;
ASSERT(dflags & DFLAG_EXTENDED_REFERENCES);
erts_magic_ref_save_bin(obj);
- *ep++ = NEWER_REFERENCE_EXT;
i = ref_no_numbers(obj);
put_int16(i, ep);
ep += 2;
ep = enc_atom(acmp, sysname, ep, dflags);
- put_int32(creation, ep);
- ep += 4;
+ if (creation <= ERTS_MAX_LOCAL_CREATION) {
+ *tagp = NEW_REFERENCE_EXT;
+ *ep++ = creation;
+ } else {
+ ASSERT(is_external_ref(obj));
+ *tagp = NEWER_REFERENCE_EXT;
+ put_int32(creation, ep);
+ ep += 4;
+ }
ref_num = ref_numbers(obj);
for (j = 0; j < i; j++) {
put_int32(ref_num[j], ep);
@@ -2852,14 +2870,21 @@ enc_term_int(TTBEncodeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj, byte* ep,
Eterm sysname = (((dflags & DFLAG_INTERNAL_TAGS) && is_internal_port(obj))
? INTERNAL_LOCAL_SYSNAME : port_node_name(obj));
Uint32 creation = port_creation(obj);
+ byte* tagp = ep++;
- *ep++ = NEW_PORT_EXT;
ep = enc_atom(acmp, sysname, ep, dflags);
j = port_number(obj);
put_int32(j, ep);
ep += 4;
- put_int32(creation, ep);
- ep += 4;
+ if (creation <= ERTS_MAX_LOCAL_CREATION) {
+ *tagp = PORT_EXT;
+ *ep++ = creation;
+ } else {
+ ASSERT(is_external_port(obj));
+ *tagp = NEW_PORT_EXT;
+ put_int32(creation, ep);
+ ep += 4;
+ }
break;
}
case LIST_DEF:
@@ -3554,7 +3579,7 @@ dec_term_atom_common:
if (tag == PORT_EXT) {
cre = get_int8(ep);
ep++;
- if (!is_tiny_creation(cre)) {
+ if (!is_valid_creation(cre)) {
goto error;
}
}
@@ -3601,7 +3626,7 @@ dec_term_atom_common:
cre = get_int8(ep);
ep += 1;
- if (!is_tiny_creation(cre)) {
+ if (!is_valid_creation(cre)) {
goto error;
}
goto ref_ext_common;
@@ -3615,7 +3640,7 @@ dec_term_atom_common:
cre = get_int8(ep);
ep += 1;
- if (!is_tiny_creation(cre)) {
+ if (!is_valid_creation(cre)) {
goto error;
}
r0 = get_int32(ep);
@@ -4321,21 +4346,30 @@ encode_size_struct_int(TTBSizeContext* ctx, ErtsAtomCacheMap *acmp, Eterm obj,
result += 1 + 4 + 1 + i; /* tag,size,sign,digits */
break;
case EXTERNAL_PID_DEF:
+ if (external_pid_creation(obj) > ERTS_MAX_LOCAL_CREATION)
+ result += 3;
+ /*fall through*/
case PID_DEF:
result += (1 + encode_size_struct2(acmp, pid_node_name(obj), dflags) +
- 4 + 4 + 4);
+ 4 + 4 + 1);
break;
case EXTERNAL_REF_DEF:
+ if (external_ref_creation(obj) > ERTS_MAX_LOCAL_CREATION)
+ result += 3;
+ /*fall through*/
case REF_DEF:
ASSERT(dflags & DFLAG_EXTENDED_REFERENCES);
i = ref_no_numbers(obj);
result += (1 + 2 + encode_size_struct2(acmp, ref_node_name(obj), dflags) +
- 4 + 4*i);
+ 1 + 4*i);
break;
case EXTERNAL_PORT_DEF:
+ if (external_port_creation(obj) > ERTS_MAX_LOCAL_CREATION)
+ result += 3;
+ /*fall through*/
case PORT_DEF:
result += (1 + encode_size_struct2(acmp, port_node_name(obj), dflags) +
- 4 + 4);
+ 4 + 1);
break;
case LIST_DEF: {
int is_str = is_external_string(obj, &m);
diff --git a/erts/emulator/beam/external.h b/erts/emulator/beam/external.h
index 4a30d65c25..b556c9076c 100644
--- a/erts/emulator/beam/external.h
+++ b/erts/emulator/beam/external.h
@@ -144,14 +144,6 @@ typedef struct erl_dist_external {
ErtsAtomTranslationTable attab;
} ErtsDistExternal;
-#define ERTS_DIST_EXT_SIZE(EDEP) \
- (sizeof(ErtsDistExternal) \
- - (((EDEP)->flags & ERTS_DIST_EXT_ATOM_TRANS_TAB) \
- ? (ASSERT(0 <= (EDEP)->attab.size \
- && (EDEP)->attab.size <= ERTS_ATOM_CACHE_SIZE), \
- sizeof(Eterm)*(ERTS_ATOM_CACHE_SIZE - (EDEP)->attab.size)) \
- : sizeof(ErtsAtomTranslationTable)))
-
typedef struct {
byte *extp;
int exttmp;
diff --git a/erts/emulator/beam/global.h b/erts/emulator/beam/global.h
index f9bbe4167f..4c8d3d3dbe 100644
--- a/erts/emulator/beam/global.h
+++ b/erts/emulator/beam/global.h
@@ -1216,10 +1216,11 @@ Uint64 erts_timestamp_millis(void);
Export* erts_find_function(Eterm, Eterm, unsigned int, ErtsCodeIndex);
-void *erts_calc_stacklimit(char *prev_c, UWord stacksize);
-int erts_check_below_limit(char *ptr, char *limit);
-int erts_check_above_limit(char *ptr, char *limit);
-void *erts_ptr_id(void *ptr);
+/* ERTS_NOINLINE prevents link-time optimization across modules */
+void *erts_calc_stacklimit(char *prev_c, UWord stacksize) ERTS_NOINLINE;
+int erts_check_below_limit(char *ptr, char *limit) ERTS_NOINLINE;
+int erts_check_above_limit(char *ptr, char *limit) ERTS_NOINLINE;
+void *erts_ptr_id(void *ptr) ERTS_NOINLINE;
Eterm store_external_or_ref_in_proc_(Process *, Eterm);
Eterm store_external_or_ref_(Uint **, ErlOffHeap*, Eterm);
diff --git a/erts/emulator/beam/instrs.tab b/erts/emulator/beam/instrs.tab
index 692408e212..462ee77e6f 100644
--- a/erts/emulator/beam/instrs.tab
+++ b/erts/emulator/beam/instrs.tab
@@ -1001,7 +1001,7 @@ catch_end(Y) {
}
/* only x(2) is included in the rootset here */
if (E - HTOP < 3) {
- SWAPOUT;
+ $GC_SWAPOUT();
PROCESS_MAIN_CHK_LOCKS(c_p);
FCALLS -= erts_garbage_collect_nobump(c_p, 3, reg+2, 1, FCALLS);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
diff --git a/erts/emulator/beam/macros.tab b/erts/emulator/beam/macros.tab
index 494fe8961e..1b5e5f66b0 100644
--- a/erts/emulator/beam/macros.tab
+++ b/erts/emulator/beam/macros.tab
@@ -63,10 +63,21 @@ JUMP(Fail) {
Goto(*I);
}
+GC_SWAPOUT() {
+ //
+ // Since a garbage collection is expensive anyway, we can afford
+ // to save the instruction counter so that the correct function will
+ // be pointed in the crash dump if the garbage collection fails
+ // because of insufficient memory.
+ //
+ SWAPOUT;
+ c_p->i = I;
+}
+
GC_TEST(Ns, Nh, Live) {
Uint need = $Nh + $Ns;
if (ERTS_UNLIKELY(E - HTOP < need)) {
- SWAPOUT;
+ $GC_SWAPOUT();
PROCESS_MAIN_CHK_LOCKS(c_p);
FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, $Live, FCALLS);
ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
@@ -79,7 +90,7 @@ GC_TEST(Ns, Nh, Live) {
GC_TEST_PRESERVE(NeedHeap, Live, PreserveTerm) {
Uint need = $NeedHeap;
if (ERTS_UNLIKELY(E - HTOP < need)) {
- SWAPOUT;
+ $GC_SWAPOUT();
reg[$Live] = $PreserveTerm;
PROCESS_MAIN_CHK_LOCKS(c_p);
FCALLS -= erts_garbage_collect_nobump(c_p, need, reg, $Live+1, FCALLS);
diff --git a/erts/emulator/beam/msg_instrs.tab b/erts/emulator/beam/msg_instrs.tab
index 6f8d1469ef..b08466c830 100644
--- a/erts/emulator/beam/msg_instrs.tab
+++ b/erts/emulator/beam/msg_instrs.tab
@@ -105,6 +105,7 @@ i_loop_rec(Dest) {
$SET_CP_I_ABS(I);
c_p->arity = 0;
c_p->current = NULL;
+ c_p->fcalls = FCALLS;
FCALLS -= erts_proc_sig_receive_helper(c_p, FCALLS, neg_o_reds,
&msgp, &get_out);
SWAPIN;
diff --git a/erts/emulator/beam/ops.tab b/erts/emulator/beam/ops.tab
index 7a125b0f67..10ca74cd60 100644
--- a/erts/emulator/beam/ops.tab
+++ b/erts/emulator/beam/ops.tab
@@ -1690,9 +1690,14 @@ i_plus S1=c S2=c Fail Dst => move S1 x | i_plus x S2 Fail Dst
i_plus xy xyc j? d
-i_minus x x j? d
-i_minus c x j? d
-i_minus s s j? d
+# A minus instruction with a constant right operand will be
+# converted to an i_increment instruction, except in guards or
+# when the negated value of the constant won't fit in a guard.
+# Therefore, it very rare.
+i_minus S1 S2=c Fail Dst => move S2 x | i_minus S1 x Fail Dst
+
+i_minus xy xy j? d
+i_minus c xy j? d
i_times j? s s d
diff --git a/erts/emulator/beam/sys.h b/erts/emulator/beam/sys.h
index a6312293cc..c261c8e117 100644
--- a/erts/emulator/beam/sys.h
+++ b/erts/emulator/beam/sys.h
@@ -63,6 +63,14 @@
# endif
#endif
+#ifndef ERTS_NOINLINE
+# if ERTS_AT_LEAST_GCC_VSN__(3,1,1)
+# define ERTS_NOINLINE __attribute__((__noinline__))
+# else
+# define ERTS_NOINLINE
+# endif
+#endif
+
#if defined(DEBUG) || defined(ERTS_ENABLE_LOCK_CHECK)
# undef ERTS_CAN_INLINE
# define ERTS_CAN_INLINE 0
diff --git a/erts/emulator/beam/utils.c b/erts/emulator/beam/utils.c
index 36cfe0548e..0bbae65e28 100644
--- a/erts/emulator/beam/utils.c
+++ b/erts/emulator/beam/utils.c
@@ -1069,11 +1069,11 @@ do { \
#define HCONST 0x9e3779b9UL /* the golden ratio; an arbitrary value */
-Uint32
-block_hash(byte *k, unsigned length, Uint32 initval)
+static Uint32
+block_hash(byte *k, Uint length, Uint32 initval)
{
Uint32 a,b,c;
- unsigned len;
+ Uint len;
/* Set up the internal state */
len = length;
@@ -1749,7 +1749,7 @@ make_internal_hash(Eterm term, Uint32 salt)
case SUB_BINARY_SUBTAG:
{
byte* bptr;
- unsigned sz = binary_size(term);
+ Uint sz = binary_size(term);
Uint32 con = HCONST_13 + hash;
Uint bitoffs;
Uint bitsize;
diff --git a/erts/emulator/internal_doc/CarrierMigration.md b/erts/emulator/internal_doc/CarrierMigration.md
index bb3d8aac28..40f6031ca8 100644
--- a/erts/emulator/internal_doc/CarrierMigration.md
+++ b/erts/emulator/internal_doc/CarrierMigration.md
@@ -1,6 +1,9 @@
Carrier Migration
=================
+Introduction
+------------
+
The ERTS memory allocators manage memory blocks in two types of raw
memory chunks. We call these chunks of raw memory
*carriers*. Single-block carriers which only contain one large block,
@@ -141,11 +144,11 @@ Since the carrier has been unlinked from the data structure of
available free blocks, no more allocations will be made in the
carrier.
-The allocator instance that created a carrier is called its **owner**.
+The allocator instance that created a carrier is called its *owner*.
Ownership never changes.
The allocator instance that has the responsibility to perform deallocations in a
-carrier is called its **employer**. The employer may also perform allocations if
+carrier is called its *employer*. The employer may also perform allocations if
the carrier is not in the pool. Employment may change when a carrier is fetched from
or inserted into the pool.
@@ -153,14 +156,14 @@ Deallocations in a carrier, while it remains in the pool, is always performed
the owner. That is, all pooled carriers are employed by their owners.
Each carrier has an atomic word containing a pointer to the employing allocator
-instance and three bit flags; IN_POOL, BUSY and HOMECOMING.
+instance and three bit flags; IN\_POOL, BUSY and HOMECOMING.
When fetching a carrier from the pool, employment may change and further
deallocations in the carrier will be redirected to the new
employer using the delayed dealloc functionality.
When a foreign allocator instance abandons a carrier back into the pool, it will
-also pass it back to its **owner** using the delayed dealloc queue. When doing
+also pass it back to its *owner* using the delayed dealloc queue. When doing
this it will set the HOMECOMING bit flag to mark it as "enqueued". The owner
will later clear the HOMECOMING bit when the carrier is dequeued. This mechanism
prevents a carrier from being enqueued again before it has been dequeued.
@@ -180,14 +183,14 @@ back to the owner for deallocation using the delayed dealloc functionality.
In short:
-* The allocator instance that created a carrier **owns** it.
-* An empty carrier is always deallocated by its **owner**.
-* **Ownership** never changes.
-* The allocator instance that uses a carrier **employs** it.
-* An **employer** can abandon a carrier into the pool.
+* The allocator instance that created a carrier *owns* it.
+* An empty carrier is always deallocated by its *owner*.
+* *Ownership* never changes.
+* The allocator instance that uses a carrier *employs* it.
+* An *employer* can abandon a carrier into the pool.
* Pooled carriers are not allocated from.
-* Pooled carriers are always **employed** by their **owner**.
-* **Employment** can only change from **owner** to a foreign allocator
+* Pooled carriers are always *employed* by their *owner*.
+* *Employment* can only change from *owner* to a foreign allocator
when a carrier is fetched from the pool.
@@ -229,7 +232,7 @@ carrier. When the cluster gets to the same size as the search limit,
all searches will essentially fail.
To counter the "bad cluster" problem and also ease the contention, the
-search will now always start by first looking at the allocators **own**
+search will now always start by first looking at the allocators *own*
carriers. That is, carriers that were initially created by the
allocator itself and later had been abandoned to the pool. If none of
our own abandoned carrier would do, then the search continues into the
diff --git a/erts/emulator/internal_doc/CodeLoading.md b/erts/emulator/internal_doc/CodeLoading.md
index 151b9cd57c..0b2e3070e7 100644
--- a/erts/emulator/internal_doc/CodeLoading.md
+++ b/erts/emulator/internal_doc/CodeLoading.md
@@ -45,7 +45,7 @@ free to schedule other work while the second loader is waiting. (See
`erts_release_code_write_permission`).
The ability to prepare several modules in parallel is not currently
-used as almost all code loading is serialized by the code_server
+used as almost all code loading is serialized by the code\_server
process. The BIF interface is however prepared for this.
erlang:prepare_loading(Module, Code) -> LoaderState
@@ -71,8 +71,8 @@ structures. These *code access structures* are
* Export table. One entry for every exported function.
* Module table. One entry for each loaded module.
-* "beam_catches". Identifies jump destinations for catch instructions.
-* "beam_ranges". Map code address to function and line in source file.
+* "beam\_catches". Identifies jump destinations for catch instructions.
+* "beam\_ranges". Map code address to function and line in source file.
The most frequently used of these structures is the export table that
is accessed in run time for every executed external function call to
diff --git a/erts/emulator/internal_doc/GarbageCollection.md b/erts/emulator/internal_doc/GarbageCollection.md
index 1d9e3f4160..a1627b3233 100644
--- a/erts/emulator/internal_doc/GarbageCollection.md
+++ b/erts/emulator/internal_doc/GarbageCollection.md
@@ -1,6 +1,6 @@
# Erlang Garbage Collector
-Erlang manages dynamic memory with a [tracing garbage collector](https://en.wikipedia.org/wiki/Tracing_garbage_collection). More precisely a per process generational semi-space copying collector using [Cheney's](#cheney) copy collection algorithm together with a global large object space.
+Erlang manages dynamic memory with a [tracing garbage collector](https://en.wikipedia.org/wiki/Tracing_garbage_collection). More precisely a per process generational semi-space copying collector using Cheney's copy collection algorithm together with a global large object space. (See C. J. Cheney in [References](#references).)
## Overview
@@ -12,12 +12,11 @@ Terms are created on the heap by evaluating expressions. There are two major typ
Let's look at an example that returns a tuple with the newly created data.
-```erlang
-data(Foo) ->
- Cons = [42|Foo],
- Literal = {text, "hello world!"},
- {tag, Cons, Literal}.
-```
+
+ data(Foo) ->
+ Cons = [42|Foo],
+ Literal = {text, "hello world!"},
+ {tag, Cons, Literal}.
In this example we first create a new cons cell with an integer and a tuple with some text. Then a tuple of size three wrapping the other values with an atom tag is created and returned.
@@ -25,7 +24,6 @@ On the heap tuples require a word size for each of its elements as well as for t
Compiling this code to beam assembly (`erlc -S`) shows exactly what is happening.
-```erlang
...
{test_heap,6,1}.
{put_list,{integer,42},{x,0},{x,1}}.
@@ -34,9 +32,8 @@ Compiling this code to beam assembly (`erlc -S`) shows exactly what is happening
{put,{x,1}}.
{put,{literal,{text,"hello world!"}}}.
return.
-```
-Looking at the assembler code we can see three things; The heap requirement in this function turns out to be only six words, as seen by the `{test_heap,6,1}` instruction. All the allocations are combined to a single instruction. The bulk of the data `{text, "hello world!"}` is a *literal*. Literals, sometimes referred to as constants, are not allocated in the function since they are a part of the module and allocated at load time.
+Looking at the assembler code we can see three things: The heap requirement in this function turns out to be only six words, as seen by the `{test_heap,6,1}` instruction. All the allocations are combined to a single instruction. The bulk of the data `{text, "hello world!"}` is a *literal*. Literals, sometimes referred to as constants, are not allocated in the function since they are a part of the module and allocated at load time.
If there is not enough space available on the heap to satisfy the `test_heap` instructions request for memory, then a garbage collection is initiated. It may happen immediately in the `test_heap` instruction, or it can be delayed until a later time depending on what state the process is in. If the garbage collection is delayed, any memory needed will be allocated in heap fragments. Heap fragments are extra memory blocks that are a part of the young heap, but are not allocated in the contigious area where terms normally reside. See [The young heap](#the-young-heap) for more details.
@@ -50,11 +47,9 @@ It follows all the pointers from the root-set to the heap and copies each term w
After the header word has been copied a [*move marker*](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.h#L45-L46) is destructively placed in it pointing to the term in the *to space*. Any other term that points to the already moved term will [see this move marker](https://github.com/erlang/otp/blob/OTP-18.0/erts/emulator/beam/erl_gc.c#L1125) and copy the referring pointer instead. For example, if the have the following Erlang code:
-```erlang
-foo(Arg) ->
- T = {test, Arg},
- {wrapper, T, T, T}.
-```
+ foo(Arg) ->
+ T = {test, Arg},
+ {wrapper, T, T, T}.
Only one copy of T exists on the heap and during the garbage collection only the first time T is encountered will it be copied.
@@ -86,15 +81,15 @@ In the next garbage collection, any pointers to the old heap will be ignored and
Generational garbage collection aims to increase performance at the expense of memory. This is achieved because only the young, smaller, heap is considered in most garbage collections.
-The generational [hypothesis](#ungar) predicts that most terms tend to die young, and for an immutable language such as Erlang, young terms die even faster than in other languages. So for most usage patterns the data in the new heap will die very soon after it is allocated. This is good because it limits the amount of data copied to the old heap and also because the garbage collection algorithm used is proportional to the amount of live data on the heap.
+The generational hypothesis predicts that most terms tend to die young (see D. Ungar in [References](#references)), and for an immutable language such as Erlang, young terms die even faster than in other languages. So for most usage patterns the data in the new heap will die very soon after it is allocated. This is good because it limits the amount of data copied to the old heap and also because the garbage collection algorithm used is proportional to the amount of live data on the heap.
One critical issue to note here is that any term on the young heap can reference terms on the old heap but *no* term on the old heap may refer to a term on the young heap. This is due to the nature of the copy algorithm. Anything referenced by an old heap term is not included in the reference tree, root-set and its followers, and hence is not copied. If it was, the data would be lost, fire and brimstone would rise to cover the earth. Fortunately, this comes naturally for Erlang because the terms are immutable and thus there can be no pointers modified on the old heap to point to the young heap.
-To reclaim data from the old heap, both young and old heaps are included during the collection and copied to a common *to space*. Both the *from space* of the young and old heap are then deallocated and the procedure will start over from the beginning. This type of garbage collection is called a full sweep and is triggered when the size of the area under the high-watermark is larger than the size of the free area of the old heap. It can also be triggered by doing a manual call to [erlang:garbage_collect()](http://erlang.org/doc/man/erlang.html#garbage_collect-0), or by running into the young garbage collection limit set by [spawn_opt(fun(),[{fullsweep_after, N}])](http://erlang.org/doc/man/erlang.html#spawn_opt-4) where N is the number of young garbage collections to do before forcing a garbage collection of both young and old heap.
+To reclaim data from the old heap, both young and old heaps are included during the collection and copied to a common *to space*. Both the *from space* of the young and old heap are then deallocated and the procedure will start over from the beginning. This type of garbage collection is called a full sweep and is triggered when the size of the area under the high-watermark is larger than the size of the free area of the old heap. It can also be triggered by doing a manual call to [erlang:garbage_collect()](http://erlang.org/doc/man/erlang.html#garbage_collect-0), or by running into the young garbage collection limit set by [spawn\_opt(fun(),[{fullsweep\_after, N}\])](http://erlang.org/doc/man/erlang.html#spawn_opt-4) where N is the number of young garbage collections to do before forcing a garbage collection of both young and old heap.
## The young heap
-The young heap, or the allocation heap, consists of the stack and heap as described in the Overview. However, it also includes any heap fragments that are attached to the heap. All of the heap fragments are considered to be above the high-watermark and part of the young generation. Heap fragments contain terms that either did not fit on the heap, or were created by another process and then attached to the heap. For instance if the bif binary_to_term created a term which does not fit on the current heap without doing a garbage collection, it will create a heap-fragment for the term and then schedule a garbage collection for later. Also if a message is sent to the process, the payload may be placed in a heap-fragment and that fragment is added to young heap when the message is matched in a receive clause.
+The young heap, or the allocation heap, consists of the stack and heap as described in the Overview. However, it also includes any heap fragments that are attached to the heap. All of the heap fragments are considered to be above the high-watermark and part of the young generation. Heap fragments contain terms that either did not fit on the heap, or were created by another process and then attached to the heap. For instance if the bif `binary_to_term/1` created a term which does not fit on the current heap without doing a garbage collection, it will create a heap-fragment for the term and then schedule a garbage collection for later. Also if a message is sent to the process, the payload may be placed in a heap-fragment and that fragment is added to young heap when the message is matched in a receive clause.
This procedure differs from how it worked prior to Erlang/OTP 19.0. Before 19.0, only a contiguous memory block where the young heap and stack resided was considered to be part of the young heap. Heap fragments and messages were immediately copied into the young heap before they could be inspected by the Erlang program. The behaviour introduced in 19.0 is superior in many ways - most significantly it reduces the number of necessary copy operations and the root set for garbage collection.
@@ -118,21 +113,19 @@ The old heap is always one step ahead in the heap growth stages than the young h
When garbage collecting a heap (young or old) all literals are left in place and not copied. To figure out if a term should be copied or not when doing a garbage collection the following pseudo code is used:
-```c
-if (erts_is_literal(ptr) || (on_old_heap(ptr) && !fullsweep)) {
- /* literal or non fullsweep - do not copy */
-} else {
- copy(ptr);
-}
-```
+ if (erts_is_literal(ptr) || (on_old_heap(ptr) && !fullsweep)) {
+ /* literal or non fullsweep - do not copy */
+ } else {
+ copy(ptr);
+ }
The [`erts_is_literal`](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/global.h#L1452-L1465) check works differently on different architectures and operating systems.
-On 64 bit systems that allow mapping of unreserved virtual memory areas (most operating systems except Windows), an area of size 1 GB (by default) is mapped and then all literals are placed within that area. Then all that has to be done to determine if something is a literal or not is [two quick pointer checks](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_alloc.h#L322-L324). This system relies on the fact that a memory page that has not been touched yet does not take any actual space. So even if 1 GB of virtual memory is mapped, only the memory which is actually needed for literals is allocated in ram. The size of the literal area is configurable through the +MIscs erts_alloc option.
+On 64 bit systems that allow mapping of unreserved virtual memory areas (most operating systems except Windows), an area of size 1 GB (by default) is mapped and then all literals are placed within that area. Then all that has to be done to determine if something is a literal or not is [two quick pointer checks](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_alloc.h#L322-L324). This system relies on the fact that a memory page that has not been touched yet does not take any actual space. So even if 1 GB of virtual memory is mapped, only the memory which is actually needed for literals is allocated in ram. The size of the literal area is configurable through the +MIscs erts\_alloc option.
On 32 bit systems, there is not enough virtual memory space to allocate 1 GB for just literals, so instead small 256 KB sized literal regions are created on demand and a card mark bit-array of the entire 32 bit memory space is then used to determine if a term is a literal or not. Since the total memory space is only 32 bits, the card mark bit-array is only 256 words large. On a 64 bit system the same bit-array would have to be 1 tera words large, so this technique is only viable on 32 bit systems. Doing [lookups in the array](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_alloc.h#L316-L319) is a little more expensive then just doing the pointer checks that can be done in 64 bit systems, but not extremely so.
-On 64 bit windows, on which erts_alloc cannot do unreserved virtual memory mappings, a [special tag](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_term.h#L59) within the Erlang term object is used to determine if something [is a literal or not](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_term.h#L248-L252). This is very cheap, however, the tag is only available on 64 bit machines, and it is possible to do a great deal of other nice optimizations with this tag in the future (like for instance a more compact list implementation) so it is not used on operating systems where it is not needed.
+On 64 bit windows, on which erts\_alloc cannot do unreserved virtual memory mappings, a [special tag](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_term.h#L59) within the Erlang term object is used to determine if something [is a literal or not](https://github.com/erlang/otp/blob/OTP-19.0/erts/emulator/beam/erl_term.h#L248-L252). This is very cheap, however, the tag is only available on 64 bit machines, and it is possible to do a great deal of other nice optimizations with this tag in the future (like for instance a more compact list implementation) so it is not used on operating systems where it is not needed.
This behaviour is different from how it worked prior to Erlang/OTP 19.0. Before 19.0 the literal check was done by checking if the pointer pointed to the young or old heap block. If it did not, then it was considered a literal. This lead to considerable overhead and strange memory usage scenarios, so it was removed in 19.0.
@@ -182,6 +175,8 @@ Using `on_heap` will force all messages to be part of on the young heap which wi
Which one of these strategies is best depends a lot on what the process is doing and how it interacts with other processes. So, as always, profile the application and see how it behaves with the different options.
- <a name="cheney">[1]</a>: C. J. Cheney. A nonrecursive list compacting algorithm. Commun. ACM, 13(11):677–678, Nov. 1970.
+## References
+
+C. J. Cheney. A nonrecursive list compacting algorithm. Commun. ACM, 13(11):677–678, Nov. 1970.
- <a name="ungar">[2]</a>: D. Ungar. Generation scavenging: A non-disruptive high performance storage reclamation algorithm. SIGSOFT Softw. Eng. Notes, 9(3):157–167, Apr. 1984.
+D. Ungar. Generation scavenging: A non-disruptive high performance storage reclamation algorithm. SIGSOFT Softw. Eng. Notes, 9(3):157–167, Apr. 1984.
diff --git a/erts/emulator/internal_doc/PTables.md b/erts/emulator/internal_doc/PTables.md
index 6fe0e7665d..ef61963a40 100644
--- a/erts/emulator/internal_doc/PTables.md
+++ b/erts/emulator/internal_doc/PTables.md
@@ -85,13 +85,13 @@ following:
3. Depending on use, issue appropriate memory barrier.
A common barrier used is a barrier with acquire semantics. On
- x86/x86_64 this maps to a compiler barrier preventing the compiler
+ x86/x86\_64 this maps to a compiler barrier preventing the compiler
to reorder instructions, but on other hardware often some kind of
light weight hardware memory barrier is also needed.
When comparing with a locked approach, at least one heavy weight
memory barrier will be issued when locking the lock on most, if
- not all, hardware architectures (including x86/x86_64), and often
+ not all, hardware architectures (including x86/x86\_64), and often
some kind of light weight memory barrier will be issued when
unlocking the lock.
diff --git a/erts/emulator/internal_doc/SuperCarrier.md b/erts/emulator/internal_doc/SuperCarrier.md
index acf722ea37..f52c6613d5 100644
--- a/erts/emulator/internal_doc/SuperCarrier.md
+++ b/erts/emulator/internal_doc/SuperCarrier.md
@@ -5,7 +5,7 @@ A super carrier is large memory area, allocated at VM start, which can
be used during runtime to allocate normal carriers from.
The super carrier feature was introduced in OTP R16B03. It is
-enabled with command line option +MMscs <size in Mb>
+enabled with command line option +MMscs &lt;size in Mb&gt;
and can be configured with other options.
Problem
@@ -65,7 +65,7 @@ carrier is full.
### Implementation ###
-The entire super carrier implementation is kept in erl_mmap.c. The
+The entire super carrier implementation is kept in erl\_mmap.c. The
name suggest that it can be viewed as our own mmap implementation.
A super carrier needs to satisfy two slightly different kinds of
@@ -98,8 +98,8 @@ other.
### Data structures ###
-The MBC area is called **sa** as in super aligned and the SBC area is
-called **sua** as in super un-aligned.
+The MBC area is called *sa* as in super aligned and the SBC area is
+called *sua* as in super un-aligned.
Note that the "super" in super alignment and the "super" in super
carrier has nothing to do with each other. We could have choosen
@@ -128,7 +128,7 @@ down or up.
We need to keep track of all the free segments in order to reuse them
for new carrier allocations. One initial idea was to use the same
mechanism that is used to keep track of free blocks within MBCs
-(alloc_util and the different strategies). However, that would not be
+(alloc\_util and the different strategies). However, that would not be
as straight forward as one can think and can also waste quite a lot of
memory as it uses prepended block headers. The granularity of the
super carrier is one memory page (usually 4kb). We want to allocate
diff --git a/erts/emulator/internal_doc/Tracing.md b/erts/emulator/internal_doc/Tracing.md
index 7f97f64765..196ae0dd4e 100644
--- a/erts/emulator/internal_doc/Tracing.md
+++ b/erts/emulator/internal_doc/Tracing.md
@@ -37,6 +37,7 @@ what different type of break actions that are enabled.
Same Same but Different
-----------------------
+
Even though `trace_pattern` use the same technique as the non-blocking
code loading with replicated generations of data structures and an
atomic switch, the implementations are quite separate from each
@@ -72,6 +73,7 @@ aligned write operation on all hardware architectures we use.
Adding a new Breakpoint
-----------------------
+
This is a simplified sequence describing what `trace_pattern` goes
through when adding a new breakpoint.
@@ -82,7 +84,7 @@ through when adding a new breakpoint.
instruction word in the breakpoint.
3. Write a pointer to the breakpoint at offset -4 from the first
- instruction "func_info" header.
+ instruction "func\_info" header.
4. Set the staging part of the breakpoint as enabled with specified
breakpoint data.
@@ -139,7 +141,7 @@ and removing breakpoints.
2. Allocate new breakpoint structures with a disabled active part and
the original beam instruction. Write a pointer to the breakpoint in
- "func_info" header at offset -4.
+ "func\_info" header at offset -4.
3. Update the staging part of all affected breakpoints. Disable
breakpoints that are to be removed.
diff --git a/erts/emulator/internal_doc/beam_makeops.md b/erts/emulator/internal_doc/beam_makeops.md
index 1da8d2ab05..2880099b70 100644
--- a/erts/emulator/internal_doc/beam_makeops.md
+++ b/erts/emulator/internal_doc/beam_makeops.md
@@ -403,7 +403,7 @@ A line with `//` is also a comment. It is recommended to only
use this style of comments in files that define implementations of
instructions.
-A long line can be broken into shorter lines by a placing a`\` before
+A long line can be broken into shorter lines by a placing a `\` before
the newline.
### Variable definitions ###
@@ -1159,7 +1159,6 @@ implementation of `gen_element()`:
return op;
}
-}
### Defining the implementation ###
@@ -1452,7 +1451,7 @@ optionally additional heap space.
##### The NEXT_INSTRUCTION pre-bound variable #####
-The NEXT_INSTRUCTION is a pre-bound variable that is available in
+The NEXT\_INSTRUCTION is a pre-bound variable that is available in
all instructions. It expands to the address of the next instruction.
Here is an example:
@@ -1545,7 +1544,7 @@ register, the pointer will no longer be valid. (Y registers are
stored on the stack.)
In those circumstances, `$REFRESH_GEN_DEST()` must be invoked
-to set up the pointer again. **beam\_makeops** will notice
+to set up the pointer again. **beam\_makeops** will notice
if there is a call to a function that does a garbage collection and
`$REFRESH_GEN_DEST()` is not called.
diff --git a/erts/emulator/sys/common/erl_check_io.c b/erts/emulator/sys/common/erl_check_io.c
index 80e8030d74..98be50815c 100644
--- a/erts/emulator/sys/common/erl_check_io.c
+++ b/erts/emulator/sys/common/erl_check_io.c
@@ -842,6 +842,8 @@ driver_select(ErlDrvPort ix, ErlDrvEvent e, int mode, int on)
ret = 0;
goto done_unknown;
}
+ /* For some reason (don't know why), we do not clean all
+ events when doing ERL_DRV_USE_NO_CALLBACK. */
else if ((mode&ERL_DRV_USE_NO_CALLBACK) == ERL_DRV_USE) {
mode |= (ERL_DRV_READ | ERL_DRV_WRITE);
}
@@ -2491,6 +2493,10 @@ drvmode2str(int mode) {
case ERL_DRV_WRITE|ERL_DRV_USE: return "WRITE|USE";
case ERL_DRV_READ|ERL_DRV_WRITE|ERL_DRV_USE: return "READ|WRITE|USE";
case ERL_DRV_USE: return "USE";
+ case ERL_DRV_READ|ERL_DRV_USE_NO_CALLBACK: return "READ|USE_NO_CB";
+ case ERL_DRV_WRITE|ERL_DRV_USE_NO_CALLBACK: return "WRITE|USE_NO_CB";
+ case ERL_DRV_READ|ERL_DRV_WRITE|ERL_DRV_USE_NO_CALLBACK: return "READ|WRITE|USE_NO_CB";
+ case ERL_DRV_USE_NO_CALLBACK: return "USE_NO_CB";
case ERL_DRV_READ: return "READ";
case ERL_DRV_WRITE: return "WRITE";
case ERL_DRV_READ|ERL_DRV_WRITE: return "READ|WRITE";
diff --git a/erts/emulator/sys/unix/sys_drivers.c b/erts/emulator/sys/unix/sys_drivers.c
index 042a091db1..664d677ebd 100644
--- a/erts/emulator/sys/unix/sys_drivers.c
+++ b/erts/emulator/sys/unix/sys_drivers.c
@@ -1006,10 +1006,8 @@ static void clear_fd_data(ErtsSysFdData *fdd)
static void nbio_stop_fd(ErlDrvPort prt, ErtsSysFdData *fdd, int use)
{
- driver_select(prt, abs(fdd->fd), use ? ERL_DRV_USE_NO_CALLBACK : 0|DO_READ|DO_WRITE, 0);
clear_fd_data(fdd);
SET_BLOCKING(abs(fdd->fd));
-
}
static void fd_stop(ErlDrvData ev) /* Does not close the fds */
@@ -1026,10 +1024,12 @@ static void fd_stop(ErlDrvData ev) /* Does not close the fds */
if (dd->ifd) {
sz += sizeof(ErtsSysFdData);
+ driver_select(prt, abs(dd->ifd->fd), ERL_DRV_USE_NO_CALLBACK|DO_READ|DO_WRITE, 0);
nbio_stop_fd(prt, dd->ifd, 1);
}
if (dd->ofd && dd->ofd != dd->ifd) {
sz += sizeof(ErtsSysFdData);
+ driver_select(prt, abs(dd->ofd->fd), ERL_DRV_USE_NO_CALLBACK|DO_WRITE, 0);
nbio_stop_fd(prt, dd->ofd, 1);
}
diff --git a/erts/emulator/test/Makefile b/erts/emulator/test/Makefile
index 8c2054cb51..019af2162f 100644
--- a/erts/emulator/test/Makefile
+++ b/erts/emulator/test/Makefile
@@ -124,6 +124,7 @@ MODULES= \
send_term_SUITE \
sensitive_SUITE \
signal_SUITE \
+ small_SUITE \
smoke_test_SUITE \
$(SOCKET_MODULES) \
statistics_SUITE \
@@ -206,14 +207,10 @@ ERL_COMPILE_FLAGS +=
# ----------------------------------------------------
make_emakefile: $(NO_OPT_ERL_FILES) $(NATIVE_ERL_FILES)
- # This special rule can be removed when communication with R7B nodes
- # is no longer supported.
- $(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) +compressed -o$(EBIN) \
- '*_SUITE_make' > $(EMAKEFILE)
$(ERL_TOP)/make/make_emakefile $(ERL_COMPILE_FLAGS) +compressed -o$(EBIN) \
$(MODULES) >> $(EMAKEFILE)
- $(ERL_TOP)/make/make_emakefile +no_copt +no_postopt $(ERL_COMPILE_FLAGS) \
- -o$(EBIN) $(NO_OPT_MODULES) >> $(EMAKEFILE)
+ $(ERL_TOP)/make/make_emakefile +no_copt +no_postopt +no_ssa_opt +no_bsm_opt \
+ $(ERL_COMPILE_FLAGS) -o$(EBIN) $(NO_OPT_MODULES) >> $(EMAKEFILE)
$(ERL_TOP)/make/make_emakefile +native $(ERL_COMPILE_FLAGS) \
-o$(EBIN) $(NATIVE_MODULES) >> $(EMAKEFILE)
diff --git a/erts/emulator/test/bs_construct_SUITE.erl b/erts/emulator/test/bs_construct_SUITE.erl
index ad05cb3689..8fab4f5bc4 100644
--- a/erts/emulator/test/bs_construct_SUITE.erl
+++ b/erts/emulator/test/bs_construct_SUITE.erl
@@ -16,8 +16,6 @@
%% limitations under the License.
%%
%% %CopyrightEnd%
-%%
-%% Purpose : Common utilities used by several optimization passes.
%%
-module(bs_construct_SUITE).
@@ -27,9 +25,9 @@
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, append_empty_is_same/1,
- huge_float_field/1, huge_binary/1, system_limit/1, badarg/1,
+ huge_float_field/1, system_limit/1, badarg/1,
copy_writable_binary/1, kostis/1, dynamic/1, bs_add/1,
- otp_7422/1, zero_width/1, bad_append/1, bs_add_overflow/1]).
+ otp_7422/1, zero_width/1, bad_append/1, bs_append_overflow/1]).
-include_lib("common_test/include/ct.hrl").
@@ -40,9 +38,9 @@ suite() ->
all() ->
[test1, test2, test3, test4, test5, testf, not_used,
in_guard, mem_leak, coerce_to_float, bjorn, append_empty_is_same,
- huge_float_field, huge_binary, system_limit, badarg,
+ huge_float_field, system_limit, badarg,
copy_writable_binary, kostis, dynamic, bs_add, otp_7422, zero_width,
- bad_append, bs_add_overflow].
+ bad_append, bs_append_overflow].
init_per_suite(Config) ->
Config.
@@ -543,56 +541,6 @@ huge_float_field(Config) when is_list(Config) ->
huge_float_check({'EXIT',{system_limit,_}}) -> ok;
huge_float_check({'EXIT',{badarg,_}}) -> ok.
-huge_binary(Config) when is_list(Config) ->
- ct:timetrap({seconds, 60}),
- 16777216 = size(<<0:(id(1 bsl 26)),(-1):(id(1 bsl 26))>>),
- garbage_collect(),
- FreeMem = free_mem(),
- io:format("Free memory (Mb): ~p\n", [FreeMem]),
- {Shift,Return} = case free_mem() of
- undefined ->
- %% This test has to be inlined inside the case to
- %% use a literal Shift
- garbage_collect(),
- id(<<0:((1 bsl 32)-1)>>),
- {32,ok};
- Mb when Mb > 600 ->
- garbage_collect(),
- id(<<0:((1 bsl 32)-1)>>),
- {32,ok};
- Mb when Mb > 300 ->
- garbage_collect(),
- id(<<0:((1 bsl 31)-1)>>),
- {31,"Limit huge binaries to 256 Mb"};
- Mb when Mb > 200 ->
- garbage_collect(),
- id(<<0:((1 bsl 30)-1)>>),
- {30,"Limit huge binary to 128 Mb"};
- _ ->
- garbage_collect(),
- id(<<0:((1 bsl 29)-1)>>),
- {29,"Limit huge binary to 64 Mb"}
- end,
- garbage_collect(),
- id(<<0:((1 bsl Shift)-1)>>),
- garbage_collect(),
- id(<<0:(id((1 bsl Shift)-1))>>),
- garbage_collect(),
- case Return of
- ok -> ok;
- Comment -> {comment, Comment}
- end.
-
-%% Return the amount of free memory in Mb.
-free_mem() ->
- {ok,Apps} = application:ensure_all_started(os_mon),
- Mem = memsup:get_system_memory_data(),
- [ok = application:stop(App)||App <- Apps],
- case proplists:get_value(free_memory,Mem) of
- undefined -> undefined;
- Val -> Val div (1024*1024)
- end.
-
system_limit(Config) when is_list(Config) ->
WordSize = erlang:system_info(wordsize),
BitsPerWord = WordSize * 8,
@@ -904,33 +852,37 @@ append_unit_8(Bin) ->
append_unit_16(Bin) ->
<<Bin/binary-unit:16,0:1>>.
-%% 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) ->
+%% Test that the bs_append instruction will correctly check for
+%% overflow by producing a binary whose total size would exceed the
+%% maximum allowed size for a binary on a 32-bit computer.
+
+bs_append_overflow(_Config) ->
Memsize = memsize(),
io:format("Memsize = ~w Bytes~n", [Memsize]),
case erlang:system_info(wordsize) of
8 ->
+ %% Not possible to test on a 64-bit computer.
{skip, "64-bit architecture"};
_ when Memsize < (2 bsl 30) ->
- {skip, "Less then 2 GB of memory"};
+ {skip, "Less than 2 GB of memory"};
4 ->
- {'EXIT', {system_limit, _}} = (catch bs_add_overflow_signed()),
- {'EXIT', {system_limit, _}} = (catch bs_add_overflow_unsigned()),
+ {'EXIT', {system_limit, _}} = (catch bs_append_overflow_signed()),
+ erlang:garbage_collect(),
+ {'EXIT', {system_limit, _}} = (catch bs_append_overflow_unsigned()),
+ erlang:garbage_collect(),
ok
end.
-bs_add_overflow_signed() ->
- %% Produce a large result of bs_add that, if cast to signed int, would
+bs_append_overflow_signed() ->
+ %% Produce a large binary that, if cast to signed int, would
%% overflow into a negative number that fits a smallnum.
Large = <<0:((1 bsl 30)-1)>>,
<<Large/bits, Large/bits, Large/bits, Large/bits,
Large/bits, Large/bits, Large/bits, Large/bits,
Large/bits>>.
-bs_add_overflow_unsigned() ->
- %% Produce a large result of bs_add that goes beyond the limit of an
- %% unsigned word. This used to succeed but produced an incorrect result
+bs_append_overflow_unsigned() ->
+ %% The following would succeed but would produce an incorrect result
%% where B =:= C!
A = <<0:((1 bsl 32)-8)>>,
B = <<2, 3>>,
diff --git a/erts/emulator/test/bs_match_int_SUITE.erl b/erts/emulator/test/bs_match_int_SUITE.erl
index e913dc98b0..454e55d017 100644
--- a/erts/emulator/test/bs_match_int_SUITE.erl
+++ b/erts/emulator/test/bs_match_int_SUITE.erl
@@ -234,34 +234,49 @@ mml_choose(<<_A:8>>) -> single_byte_binary;
mml_choose(<<_A:8,_T/binary>>) -> multi_byte_binary.
match_huge_int(Config) when is_list(Config) ->
- Sz = 1 bsl 27,
- Bin = <<0:Sz,13:8>>,
- skip_huge_int_1(Sz, Bin),
- 0 = match_huge_int_1(Sz, Bin),
-
- %% Test overflowing the size of an integer field.
- nomatch = overflow_huge_int_skip_32(Bin),
- case erlang:system_info(wordsize) of
- 4 ->
- nomatch = overflow_huge_int_32(Bin);
- 8 ->
- %% An attempt will be made to allocate heap space for
- %% the bignum (which will probably fail); only if the
- %% allocation succeeds will the matching fail because
- %% the binary is too small.
- ok
- end,
- nomatch = overflow_huge_int_skip_64(Bin),
- nomatch = overflow_huge_int_64(Bin),
-
- %% Test overflowing the size of an integer field using variables as sizes.
- Sizes = case erlang:system_info(wordsize) of
- 4 -> lists:seq(25, 32);
- 8 -> []
- end ++ lists:seq(50, 64),
- ok = overflow_huge_int_unit128(Bin, Sizes),
-
- ok.
+ case ?MODULE of
+ bs_match_int_no_opt_SUITE ->
+ %% This test case is written with the assumption that
+ %% bs_skip2 instructions are used when the value of the
+ %% extracted segment will not be used. In OTP 21 and earlier, that
+ %% assumption was always true, because the bs_skip optimization
+ %% was included in v3_codegen and could not be disabled.
+ %% In OTP 22, the bs_skip optimization is done by beam_ssa_opt
+ %% and is disabled.
+ %%
+ %% On memory-constrained computers, using bs_get_integer2
+ %% instructions may cause the runtime system to terminate
+ %% because of insufficient memory.
+ {skip, "unoptimized code would use too much memory"};
+ bs_match_int_SUITE ->
+ Sz = 1 bsl 27,
+ Bin = <<0:Sz,13:8>>,
+ skip_huge_int_1(Sz, Bin),
+ 0 = match_huge_int_1(Sz, Bin),
+
+ %% Test overflowing the size of an integer field.
+ nomatch = overflow_huge_int_skip_32(Bin),
+ case erlang:system_info(wordsize) of
+ 4 ->
+ nomatch = overflow_huge_int_32(Bin);
+ 8 ->
+ %% An attempt will be made to allocate heap space for
+ %% the bignum (which will probably fail); only if the
+ %% allocation succeeds will the matching fail because
+ %% the binary is too small.
+ ok
+ end,
+ nomatch = overflow_huge_int_skip_64(Bin),
+ nomatch = overflow_huge_int_64(Bin),
+
+ %% Test overflowing the size of an integer field using
+ %% variables as sizes.
+ Sizes = case erlang:system_info(wordsize) of
+ 4 -> lists:seq(25, 32);
+ 8 -> []
+ end ++ lists:seq(50, 64),
+ ok = overflow_huge_int_unit128(Bin, Sizes)
+ end.
overflow_huge_int_unit128(Bin, [Sz0|Sizes]) ->
Sz = id(1 bsl Sz0),
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index b6f05efac2..7885d35d9d 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -19,7 +19,6 @@
%%
-module(distribution_SUITE).
--compile(r16).
-define(VERSION_MAGIC, 131).
@@ -40,6 +39,7 @@
-export([all/0, suite/0, groups/0,
init_per_suite/1, end_per_suite/1,
+ init_per_group/2, end_per_group/2,
ping/1, bulk_send_small/1,
group_leader/1,
optimistic_dflags/1,
@@ -54,7 +54,6 @@
dist_parallel_send/1,
atom_roundtrip/1,
unicode_atom_roundtrip/1,
- atom_roundtrip_r16b/1,
contended_atom_cache_entry/1,
contended_unicode_atom_cache_entry/1,
bad_dist_structure/1,
@@ -77,7 +76,7 @@
optimistic_dflags_echo/0, optimistic_dflags_sender/1,
roundtrip/1, bounce/1, do_dist_auto_connect/1, inet_rpc_server/1,
dist_parallel_sender/3, dist_parallel_receiver/0,
- dist_evil_parallel_receiver/0]).
+ dist_evil_parallel_receiver/0, make_busy/2]).
%% epmd_module exports
-export([start_link/0, register_node/2, register_node/3, port_please/2, address_please/3]).
@@ -86,22 +85,6 @@ suite() ->
[{ct_hooks,[ts_install_cth]},
{timetrap, {minutes, 4}}].
-init_per_suite(Config) ->
- A0 = case application:start(sasl) of
- ok -> [sasl];
- _ -> []
- end,
- A = case application:start(os_mon) of
- ok -> [os_mon|A0];
- _ -> A0
- end,
- [{started_apps, A}|Config].
-
-end_per_suite(Config) ->
- As = proplists:get_value(started_apps, Config),
- lists:foreach(fun (A) -> application:stop(A) end, As),
- Config.
-
all() ->
[ping, {group, bulk_send}, {group, local_send},
group_leader,
@@ -111,7 +94,6 @@ all() ->
ref_port_roundtrip, nil_roundtrip, stop_dist,
{group, trap_bif}, {group, dist_auto_connect},
dist_parallel_send, atom_roundtrip, unicode_atom_roundtrip,
- atom_roundtrip_r16b,
contended_atom_cache_entry, contended_unicode_atom_cache_entry,
{group, message_latency},
{group, bad_dist}, {group, bad_dist_ext},
@@ -137,6 +119,28 @@ groups() ->
message_latency_large_exit2]}
].
+init_per_suite(Config) ->
+ {ok, Apps} = application:ensure_all_started(os_mon),
+ [{started_apps, Apps} | Config].
+
+end_per_suite(Config) ->
+ Apps = proplists:get_value(started_apps, Config),
+ [application:stop(App) || App <- lists:reverse(Apps)],
+ Config.
+
+init_per_group(message_latency, Config) ->
+ Free = free_memory(),
+ if Free < 2048 ->
+ {skip, "Not enough memory"};
+ true ->
+ Config
+ end;
+init_per_group(_, Config) ->
+ Config.
+
+end_per_group(_, Config) ->
+ Config.
+
%% Tests pinging a node in different ways.
ping(Config) when is_list(Config) ->
Times = 1024,
@@ -1152,23 +1156,6 @@ atom_roundtrip(Config) when is_list(Config) ->
stop_node(Node),
ok.
-atom_roundtrip_r16b(Config) when is_list(Config) ->
- case test_server:is_release_available("r16b") of
- true ->
- ct:timetrap({minutes, 6}),
- AtomData = unicode_atom_data(),
- verify_atom_data(AtomData),
- case start_node(Config, [], "r16b") of
- {ok, Node} ->
- do_atom_roundtrip(Node, AtomData),
- stop_node(Node);
- {error, timeout} ->
- {skip,"Unable to start OTP R16B release"}
- end;
- false ->
- {skip,"No OTP R16B available"}
- end.
-
unicode_atom_roundtrip(Config) when is_list(Config) ->
AtomData = unicode_atom_data(),
verify_atom_data(AtomData),
@@ -1474,11 +1461,14 @@ measure_latency_large_message(Nodename, DataFun) ->
Echo = spawn(N, fun F() -> receive {From, Msg} -> From ! Msg, F() end end),
- case erlang:system_info(build_type) of
- debug ->
+ BuildType = erlang:system_info(build_type),
+ WordSize = erlang:system_info(wordsize),
+
+ if
+ BuildType =/= opt; WordSize =:= 4 ->
%% Test 3.2 MB and 32 MB and test the latency difference of sent messages
Payloads = [{I, <<0:(I * 32 * 1024 * 8)>>} || I <- [1,10]];
- _ ->
+ true ->
%% Test 32 MB and 320 MB and test the latency difference of sent messages
Payloads = [{I, <<0:(I * 32 * 1024 * 1024 * 8)>>} || I <- [1,10]]
end,
@@ -1493,7 +1483,7 @@ measure_latency_large_message(Nodename, DataFun) ->
stop_node(N),
case {lists:max(Times), lists:min(Times)} of
- {Max, Min} when Max * 0.25 > Min ->
+ {Max, Min} when Max * 0.25 > Min, BuildType =:= opt ->
ct:fail({incorrect_latency, IndexTimes});
_ ->
ok
@@ -1518,13 +1508,19 @@ measure_latency(DataFun, Dropper, Echo, Payload) ->
ok
end || _ <- lists:seq(1,10)],
- {TS, _} =
+ {TS, Times} =
timer:tc(fun() ->
[begin
+ T0 = erlang:monotonic_time(),
Echo ! {self(), hello},
- receive hello -> ok end
+ receive hello -> ok end,
+ (erlang:monotonic_time() - T0) / 1000000
end || _ <- lists:seq(1,100)]
end),
+ Avg = lists:sum(Times) / length(Times),
+ StdDev = math:sqrt(lists:sum([math:pow(V - Avg,2) || V <- Times]) / length(Times)),
+ ct:pal("Times: Avg: ~p Max: ~p Min: ~p Var: ~p",
+ [Avg, lists:max(Times), lists:min(Times), StdDev]),
[begin
Sender ! die,
receive
@@ -3001,3 +2997,23 @@ uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 ->
Uint band 16#ff;
uint8(Uint) ->
exit({badarg, uint8, [Uint]}).
+
+free_memory() ->
+ %% Free memory in MB.
+ try
+ SMD = memsup:get_system_memory_data(),
+ {value, {free_memory, Free}} = lists:keysearch(free_memory, 1, SMD),
+ TotFree = (Free +
+ case lists:keysearch(cached_memory, 1, SMD) of
+ {value, {cached_memory, Cached}} -> Cached;
+ false -> 0
+ end +
+ case lists:keysearch(buffered_memory, 1, SMD) of
+ {value, {buffered_memory, Buffed}} -> Buffed;
+ false -> 0
+ end),
+ TotFree div (1024*1024)
+ catch
+ error : undef ->
+ ct:fail({"os_mon not built"})
+ end.
diff --git a/erts/emulator/test/driver_SUITE.erl b/erts/emulator/test/driver_SUITE.erl
index bb0f3498ab..cbed71cedd 100644
--- a/erts/emulator/test/driver_SUITE.erl
+++ b/erts/emulator/test/driver_SUITE.erl
@@ -998,7 +998,9 @@ chkio_test({erts_poll_info, Before},
During = get_check_io_total(erlang:system_info(check_io)),
erlang:display(During),
- 0 = element(1, erts_debug:get_internal_state(check_io_debug)),
+ [0 = element(1, erts_debug:get_internal_state(check_io_debug)) ||
+ %% The pollset is not stable when running the fallback testcase
+ Test /= ?CHKIO_USE_FALLBACK_POLLSET],
io:format("During test: ~p~n", [During]),
chk_chkio_port(Port),
case erlang:port_control(Port, ?CHKIO_STOP, "") of
diff --git a/erts/emulator/test/dump_SUITE.erl b/erts/emulator/test/dump_SUITE.erl
index 3b860ebdf6..9f8ac42fa9 100644
--- a/erts/emulator/test/dump_SUITE.erl
+++ b/erts/emulator/test/dump_SUITE.erl
@@ -137,26 +137,43 @@ exiting_dump(Config) when is_list(Config) ->
free_dump(Config) when is_list(Config) ->
Dump = filename:join(proplists:get_value(priv_dir, Config),"signal_abort.dump"),
- {ok, Node} = start_node(Config),
-
- Self = self(),
-
- Pid = spawn_link(Node,
- fun() ->
- Self ! ready,
- receive
- ok ->
- unlink(Self),
- exit(lists:duplicate(1000,1000))
- end
- end),
+ {ok, NodeA} = start_node(Config),
+ {ok, NodeB} = start_node(Config),
- true = rpc:call(Node, os, putenv, ["ERL_CRASH_DUMP",Dump]),
- [erlang:monitor(process, Pid) || _ <- lists:seq(1,10000)],
- receive ready -> unlink(Pid), Pid ! ok end,
+ Self = self(),
- rpc:call(Node, erlang, halt, ["dump"]),
+ PidA = spawn_link(
+ NodeA,
+ fun() ->
+ Self ! ready,
+ receive
+ ok ->
+ spawn(fun() ->
+ erlang:system_monitor(self(), [busy_dist_port]),
+ timer:sleep(5),
+ receive
+ M ->
+ io:format("~p",[M]),
+ erlang:halt("dump")
+ end
+ end),
+ exit(lists:duplicate(1000000,100))
+ end
+ end),
+
+ spawn_link(NodeB,
+ fun() ->
+ [erlang:monitor(process, PidA) || _ <- lists:seq(1,10000)],
+ Self ! done,
+ receive _ -> ok end
+ end),
+
+ receive done -> ok end,
+ true = rpc:call(NodeA, os, putenv, ["ERL_CRASH_DUMP",Dump]),
+ ct:pal("~p",[rpc:call(NodeA, distribution_SUITE, make_busy, [NodeB, 1000])]),
+
+ receive ready -> unlink(PidA), PidA ! ok end,
{ok, Bin} = get_dump_when_done(Dump),
diff --git a/erts/emulator/test/efile_SUITE.erl b/erts/emulator/test/efile_SUITE.erl
index 55c5343739..045b351e02 100644
--- a/erts/emulator/test/efile_SUITE.erl
+++ b/erts/emulator/test/efile_SUITE.erl
@@ -105,34 +105,27 @@ open_files(Name) ->
%% a /proc directory), let's read some zero sized files 500 times each, while
%% ensuring that response isn't empty << >>
proc_zero_sized_files(Config) when is_list(Config) ->
- {Type, Flavor} = os:type(),
- %% Some files which exist on Linux but might be missing on other systems
- Inputs = ["/proc/cpuinfo",
- "/proc/meminfo",
- "/proc/partitions",
- "/proc/swaps",
- "/proc/version",
- "/proc/uptime",
- %% curproc is present on freebsd
- "/proc/curproc/cmdline"],
- case filelib:is_dir("/proc") of
- false -> {skip, "/proc not found"}; % skip the test if no /proc
- _ when Type =:= unix andalso Flavor =:= sunos ->
- %% SunOS has a /proc, but no zero sized special files
- {skip, "sunos does not have any zero sized special files"};
- true ->
- %% Take away files which do not exist in proc
- Inputs1 = lists:filter(fun filelib:is_file/1, Inputs),
-
- %% Fail if none of mentioned files exist in /proc, did we just get
- %% a normal /proc directory without any special files?
- ?assertNotEqual([], Inputs1),
-
+ TestFiles0 = [%% Some files which exist on Linux but might be missing on
+ %% other systems
+ "/proc/cpuinfo",
+ "/proc/meminfo",
+ "/proc/partitions",
+ "/proc/swaps",
+ "/proc/version",
+ "/proc/uptime",
+ %% curproc is present on FreeBSD
+ "/proc/curproc/cmdline"],
+
+ TestFiles = [F || F <- TestFiles0, filelib:is_file(F)],
+
+ case TestFiles of
+ [_|_] ->
%% For 6 inputs and 500 attempts each this do run anywhere
%% between 500 and 3000 function calls.
- lists:foreach(
- fun(Filename) -> do_proc_zero_sized(Filename, 500) end,
- Inputs1)
+ [do_proc_zero_sized(F, 500) || F <- TestFiles],
+ ok;
+ [] ->
+ {skip, "Failed to find any known zero-sized files"}
end.
%% @doc Test one file N times to also trigger possible leaking fds and memory
diff --git a/erts/emulator/test/emulator_bench.spec b/erts/emulator/test/emulator_bench.spec
index 2a180b440c..03638bfa23 100644
--- a/erts/emulator/test/emulator_bench.spec
+++ b/erts/emulator/test/emulator_bench.spec
@@ -1,2 +1,3 @@
{groups,"../emulator_test",estone_SUITE,[estone_bench]}.
{groups,"../emulator_test",binary_SUITE,[iolist_size_benchmarks]}.
+{groups,"../emulator_test",erts_debug_SUITE,[interpreter_size_bench]}.
diff --git a/erts/emulator/test/erts_debug_SUITE.erl b/erts/emulator/test/erts_debug_SUITE.erl
index f39dbedd8f..6798e3bf25 100644
--- a/erts/emulator/test/erts_debug_SUITE.erl
+++ b/erts/emulator/test/erts_debug_SUITE.erl
@@ -20,10 +20,12 @@
-module(erts_debug_SUITE).
-include_lib("common_test/include/ct.hrl").
+-include_lib("common_test/include/ct_event.hrl").
--export([all/0, suite/0,
+-export([all/0, suite/0, groups/0,
test_size/1,flat_size_big/1,df/1,term_type/1,
- instructions/1, stack_check/1, alloc_blocks_size/1]).
+ instructions/1, stack_check/1, alloc_blocks_size/1,
+ interpreter_size_bench/1]).
-export([do_alloc_blocks_size/0]).
@@ -35,6 +37,15 @@ all() ->
[test_size, flat_size_big, df, instructions, term_type,
stack_check, alloc_blocks_size].
+groups() ->
+ [{interpreter_size_bench, [], [interpreter_size_bench]}].
+
+interpreter_size_bench(_Config) ->
+ Size = erts_debug:interpreter_size(),
+ ct_event:notify(#event{name=benchmark_data,
+ data=[{value,Size}]}),
+ {comment,integer_to_list(Size)++" bytes"}.
+
test_size(Config) when is_list(Config) ->
ConsCell1 = id([a|b]),
ConsCell2 = id(ConsCell1),
diff --git a/erts/emulator/test/erts_test_utils.erl b/erts/emulator/test/erts_test_utils.erl
index e4e00a0a16..9c9eaa70ed 100644
--- a/erts/emulator/test/erts_test_utils.erl
+++ b/erts/emulator/test/erts_test_utils.erl
@@ -19,7 +19,7 @@
%%
-module(erts_test_utils).
--compile(r16).
+-compile(r20).
%%
%% THIS MODULE IS ALSO USED BY *OTHER* APPLICATIONS TEST CODE
diff --git a/erts/emulator/test/fun_SUITE.erl b/erts/emulator/test/fun_SUITE.erl
index 7f6caa08f1..2cbde621ce 100644
--- a/erts/emulator/test/fun_SUITE.erl
+++ b/erts/emulator/test/fun_SUITE.erl
@@ -513,6 +513,8 @@ refc(Config) when is_list(Config) ->
Other -> ct:fail({unexpected,Other})
end,
process_flag(trap_exit, false),
+ %% Wait to make sure that the process has terminated completely.
+ receive after 1 -> ok end,
{refc,3} = erlang:fun_info(F1, refc),
%% Garbage collect. Only the F2 fun will be left.
diff --git a/erts/emulator/test/lcnt_SUITE.erl b/erts/emulator/test/lcnt_SUITE.erl
index 87b97037d6..2dbaec9942 100644
--- a/erts/emulator/test/lcnt_SUITE.erl
+++ b/erts/emulator/test/lcnt_SUITE.erl
@@ -187,5 +187,8 @@ remove_untoggleable_locks([]) ->
[];
remove_untoggleable_locks([{resource_monitors, _, _, _} | T]) ->
remove_untoggleable_locks(T);
+remove_untoggleable_locks([{'socket[gcnt]', _, _, _} | T]) ->
+ %% Global lock used by socket NIF
+ remove_untoggleable_locks(T);
remove_untoggleable_locks([H | T]) ->
[H | remove_untoggleable_locks(T)].
diff --git a/erts/emulator/test/map_SUITE.erl b/erts/emulator/test/map_SUITE.erl
index d0a6763fe5..9ea59e1084 100644
--- a/erts/emulator/test/map_SUITE.erl
+++ b/erts/emulator/test/map_SUITE.erl
@@ -17,7 +17,7 @@
%% %CopyrightEnd%
%%
-module(map_SUITE).
--export([all/0, suite/0]).
+-export([all/0, suite/0, init_per_suite/1, end_per_suite/1]).
-export([t_build_and_match_literals/1, t_build_and_match_literals_large/1,
t_update_literals/1, t_update_literals_large/1,
@@ -84,7 +84,10 @@
%% instruction-level tests
t_has_map_fields/1,
y_regs/1,
- badmap_17/1]).
+ badmap_17/1,
+
+ %%Bugs
+ t_large_unequal_bins_same_hash_bug/1]).
-include_lib("stdlib/include/ms_transform.hrl").
@@ -149,7 +152,26 @@ all() -> [t_build_and_match_literals, t_build_and_match_literals_large,
%% instruction-level tests
t_has_map_fields,
y_regs,
- badmap_17].
+ badmap_17,
+
+ %% Bugs
+ t_large_unequal_bins_same_hash_bug].
+
+init_per_suite(Config) ->
+ A0 = case application:start(sasl) of
+ ok -> [sasl];
+ _ -> []
+ end,
+ A = case application:start(os_mon) of
+ ok -> [os_mon|A0];
+ _ -> A0
+ end,
+ [{started_apps, A}|Config].
+
+end_per_suite(Config) ->
+ As = proplists:get_value(started_apps, Config),
+ lists:foreach(fun (A) -> application:stop(A) end, As),
+ Config.
%% tests
@@ -3374,3 +3396,67 @@ fannerl() ->
104,2,97,9,97,16,70,63,184,100,97,32,0,0,0,104,2,97,10,97,16,70,63,169,174,
254,64,0,0,0,104,2,97,11,97,16,70,191,119,121,234,0,0,0,0,104,2,97,12,97,
16,70,63,149,12,170,128,0,0,0,104,2,97,13,97,16,70,191,144,193,191,0,0,0,0>>.
+
+%% This test case checks that the bug with ticket number OTP-15707 is
+%% fixed. The bug could cause a crash or memory usage to grow until
+%% the machine ran out of memory.
+t_large_unequal_bins_same_hash_bug(Config) when is_list(Config) ->
+ run_when_enough_resources(
+ fun() ->
+ K1 = get_4GB_bin(1),
+ K2 = get_4GB_bin(2),
+ Map = make_map(500),
+ Map2 = maps:put(K1, 42, Map),
+ %% The map needed to contain at least 32 key-value pairs
+ %% at this point to get the crash or out of memory
+ %% problem on the next line
+ Map3 = maps:put(K2, 43, Map2),
+ %% The following line should avoid that the compiler
+ %% optimizes away the above
+ io:format("~p ~p~n", [erlang:phash2(Map3), maps:size(Map3)])
+ end).
+
+make_map(0) ->
+ #{};
+make_map(Size) ->
+ maps:put(Size, Size, make_map(Size-1)).
+
+get_4GB_bin(Value) ->
+ List = lists:duplicate(65536, Value),
+ Bin = erlang:iolist_to_binary(List),
+ IOList4GB = duplicate_iolist(Bin, 16),
+ Bin4GB = erlang:iolist_to_binary(IOList4GB),
+ 4294967296 = size(Bin4GB),
+ Bin4GB.
+
+duplicate_iolist(IOList, 0) ->
+ IOList;
+duplicate_iolist(IOList, NrOfTimes) ->
+ duplicate_iolist([IOList, IOList], NrOfTimes - 1).
+
+run_when_enough_resources(Fun) ->
+ case {total_memory(), erlang:system_info(wordsize)} of
+ {Mem, 8} when is_integer(Mem) andalso Mem >= 31 ->
+ Fun();
+ {Mem, WordSize} ->
+ {skipped,
+ io_lib:format("Not enough resources (System Memory >= ~p, Word Size = ~p)",
+ [Mem, WordSize])}
+ end.
+
+total_memory() ->
+ %% Total memory in GB.
+ try
+ MemoryData = memsup:get_system_memory_data(),
+ case lists:keysearch(total_memory, 1, MemoryData) of
+ {value, {total_memory, TM}} ->
+ TM div (1024*1024*1024);
+ false ->
+ {value, {system_total_memory, STM}} =
+ lists:keysearch(system_total_memory, 1, MemoryData),
+ STM div (1024*1024*1024)
+ end
+ catch
+ _ : _ ->
+ undefined
+ end.
diff --git a/erts/emulator/test/process_SUITE.erl b/erts/emulator/test/process_SUITE.erl
index c698220013..b530ced566 100644
--- a/erts/emulator/test/process_SUITE.erl
+++ b/erts/emulator/test/process_SUITE.erl
@@ -44,6 +44,7 @@
process_info_garbage_collection/1,
process_info_smoke_all/1,
process_info_status_handled_signal/1,
+ process_info_reductions/1,
bump_reductions/1, low_prio/1, binary_owner/1, yield/1, yield2/1,
otp_4725/1, bad_register/1, garbage_collect/1, otp_6237/1,
process_info_messages/1, process_flag_badarg/1, process_flag_heap_size/1,
@@ -84,6 +85,7 @@ all() ->
process_info_garbage_collection,
process_info_smoke_all,
process_info_status_handled_signal,
+ process_info_reductions,
bump_reductions, low_prio, yield, yield2, otp_4725,
bad_register, garbage_collect, process_info_messages,
process_flag_badarg, process_flag_heap_size,
@@ -1093,6 +1095,46 @@ process_info_status_handled_signal(Config) when is_list(Config) ->
false = erlang:is_process_alive(P),
ok.
+%% OTP-15709
+%% Provoke a bug where process_info(reductions) returned wrong result
+%% because REDS_IN (def_arg_reg[5]) is read when the process in not running.
+process_info_reductions(Config) when is_list(Config) ->
+ pi_reductions_tester(spawn_link(fun() -> pi_reductions_spinnloop() end)),
+ pi_reductions_tester(spawn_link(fun() -> pi_reductions_recvloop() end)),
+ ok.
+
+pi_reductions_tester(Pid) ->
+ {_, DiffList} =
+ lists:foldl(fun(_, {Prev, Acc}) ->
+ %% Add another item that force sending the request
+ %% as a signal, like 'current_function'.
+ PI = process_info(Pid, [reductions, current_function]),
+ [{reductions,Reds}, {current_function,_}] = PI,
+ Diff = Reds - Prev,
+ {Diff, true} = {Diff, (Diff >= 0)},
+ {Diff, true} = {Diff, (Diff =< 1000*1000)},
+ {Reds, [Diff | Acc]}
+ end,
+ {0, []},
+ lists:seq(1,10)),
+ unlink(Pid),
+ exit(Pid,kill),
+ io:format("Reduction diffs: ~p\n", [DiffList]),
+ ok.
+
+pi_reductions_spinnloop() ->
+ %% 6 args to make use of def_arg_reg[5] which is also used as REDS_IN
+ pi_reductions_spinnloop(1, atom, "hej", self(), make_ref(), 3.14).
+
+pi_reductions_spinnloop(A,B,C,D,E,F) ->
+ pi_reductions_spinnloop(B,C,D,E,F,A).
+
+pi_reductions_recvloop() ->
+ receive
+ "a free lunch" -> false
+ end.
+
+
%% Tests erlang:bump_reductions/1.
bump_reductions(Config) when is_list(Config) ->
erlang:garbage_collect(),
diff --git a/erts/emulator/test/small_SUITE.erl b/erts/emulator/test/small_SUITE.erl
new file mode 100644
index 0000000000..00a02e5560
--- /dev/null
+++ b/erts/emulator/test/small_SUITE.erl
@@ -0,0 +1,115 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2019. All Rights Reserved.
+%%
+%% Licensed under the Apache License, Version 2.0 (the "License");
+%% you may not use this file except in compliance with the License.
+%% You may obtain a copy of the License at
+%%
+%% http://www.apache.org/licenses/LICENSE-2.0
+%%
+%% Unless required by applicable law or agreed to in writing, software
+%% distributed under the License is distributed on an "AS IS" BASIS,
+%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%% See the License for the specific language governing permissions and
+%% limitations under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(small_SUITE).
+
+-export([all/0, suite/0]).
+-export([edge_cases/1]).
+
+-include_lib("common_test/include/ct.hrl").
+
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap, {minutes, 1}}].
+
+all() ->
+ [edge_cases].
+
+edge_cases(Config) when is_list(Config) ->
+ {MinSmall, MaxSmall} = Limits = determine_small_limits(0),
+ ct:pal("Limits = ~p", [Limits]),
+
+ true = (MaxSmall + 1) =:= MaxSmall + id(1),
+ true = (MinSmall - 1) =:= MinSmall - id(1),
+ true = (MaxSmall + 1) > id(MaxSmall),
+ true = (MinSmall - 1) < id(MinSmall),
+ -1 = MinSmall + id(MaxSmall),
+ -1 = MaxSmall + id(MinSmall),
+
+ false = is_small(MinSmall * -1),
+ false = is_small(MinSmall - id(1)),
+ false = is_small(MinSmall - 1),
+ false = is_small(MaxSmall + id(1)),
+
+ Lower = lists:seq(MinSmall, MinSmall + 128),
+ Upper = lists:seq(MaxSmall, MaxSmall - 128, -1),
+ Pow2 = seq_pow2(MinSmall, MaxSmall),
+ NearZero = lists:seq(-128, 128),
+
+ ok = test_combinations([Lower, Upper, Pow2, NearZero], MinSmall, MaxSmall),
+
+ ok.
+
+test_combinations([As | Rest]=TestVectors, MinS, MaxS) ->
+ [begin
+ _ = [arith_test(A, B, MinS, MaxS) || B <- Bs]
+ end || A <- As, Bs <- TestVectors],
+ test_combinations(Rest, MinS, MaxS);
+test_combinations([], _MinS, _MaxS) ->
+ ok.
+
+%% Builds a sequence of all powers of 2 between MinSmall and MaxSmall
+seq_pow2(MinSmall, MaxSmall) ->
+ sp2_1(MinSmall, MinSmall, MaxSmall).
+
+sp2_1(N, _MinS, MaxS) when N >= MaxS ->
+ [];
+sp2_1(-1, MinS, MaxS) ->
+ [-1 | sp2_1(1, MinS, MaxS)];
+sp2_1(N, MinS, MaxS) when N < 0 ->
+ [N | sp2_1(N bsr 1, MinS, MaxS)];
+sp2_1(N, MinS, MaxS) when N > 0 ->
+ [N | sp2_1(N bsl 1, MinS, MaxS)].
+
+arith_test(A, B, MinS, MaxS) ->
+ verify_kind(A + B, MinS, MaxS),
+ verify_kind(B + A, MinS, MaxS),
+ verify_kind(A - B, MinS, MaxS),
+ verify_kind(B - A, MinS, MaxS),
+ verify_kind(A * B, MinS, MaxS),
+ verify_kind(B * A, MinS, MaxS),
+
+ true = A + B =:= apply(erlang, id('+'), [A, B]),
+ true = A - B =:= apply(erlang, id('-'), [A, B]),
+ true = A * B =:= apply(erlang, id('*'), [A, B]),
+
+ true = A + B =:= B + id(A),
+ true = A - B =:= A + id(-B),
+ true = B - A =:= B + id(-A),
+ true = A * B =:= B * id(A),
+
+ true = B =:= 0 orelse ((A * B) div id(B) =:= A),
+ true = A =:= 0 orelse ((B * A) div id(A) =:= B),
+
+ ok.
+
+%% Verifies that N is a small when it should be
+verify_kind(N, MinS, MaxS) ->
+ true = is_small(N) =:= (N >= MinS andalso N =< MaxS).
+
+is_small(N) when is_integer(N) ->
+ 0 =:= erts_debug:flat_size(N).
+
+determine_small_limits(N) ->
+ case is_small(-1 bsl N) of
+ true -> determine_small_limits(N + 1);
+ false -> {-1 bsl (N - 1), (1 bsl (N - 1)) - 1}
+ end.
+
+id(I) -> I.
diff --git a/erts/emulator/test/statistics_SUITE.erl b/erts/emulator/test/statistics_SUITE.erl
index ae3099633a..d278ac86c7 100644
--- a/erts/emulator/test/statistics_SUITE.erl
+++ b/erts/emulator/test/statistics_SUITE.erl
@@ -93,25 +93,34 @@ wall_clock_zero_diff1(0) ->
%% statistics(wall_clock) are compatible, and are within a small number
%% of ms of the amount of real time we waited for.
wall_clock_update(Config) when is_list(Config) ->
- wall_clock_update1(6).
+ N = 10,
+ Inc = 200,
+ TotalTime = wall_clock_update1(N, Inc, 0),
+ Overhead = TotalTime - N * Inc,
+ IsDebug = test_server:is_debug(),
-wall_clock_update1(N) when N > 0 ->
- {T1_wc_time, _} = statistics(wall_clock),
- receive after 1000 -> ok end,
- {T2_wc_time, Wc_Diff} = statistics(wall_clock),
-
- Wc_Diff = T2_wc_time - T1_wc_time,
- io:format("Wall clock diff = ~p; should be = 1000..1040~n", [Wc_Diff]),
- case test_server:is_debug() of
- false ->
- true = Wc_Diff =< 1040;
+ %% Check that the average overhead is reasonable.
+ if
+ Overhead < N * 100 ->
+ ok;
+ IsDebug, Overhead < N * 1000 ->
+ ok;
true ->
- true = Wc_Diff =< 2000 %Be more tolerant in debug-compiled emulator.
- end,
- true = Wc_Diff >= 1000,
- wall_clock_update1(N-1);
-wall_clock_update1(0) ->
- ok.
+ io:format("There was an overhead of ~p ms during ~p rounds.",
+ [Overhead,N]),
+ ct:fail(too_much_overhead)
+ end.
+
+wall_clock_update1(N, Inc, Total) when N > 0 ->
+ {Time1, _} = statistics(wall_clock),
+ receive after Inc -> ok end,
+ {Time2, WcDiff} = statistics(wall_clock),
+ WcDiff = Time2 - Time1,
+ io:format("Wall clock diff = ~p (expected at least ~p)\n", [WcDiff,Inc]),
+ true = WcDiff >= Inc,
+ wall_clock_update1(N-1, Inc, Total + WcDiff);
+wall_clock_update1(0, _, Total) ->
+ Total.
%%% Test statistics(runtime).
diff --git a/erts/epmd/epmd.mk b/erts/epmd/epmd.mk
index f6889a7ff1..b1fd04dc04 100644
--- a/erts/epmd/epmd.mk
+++ b/erts/epmd/epmd.mk
@@ -67,5 +67,5 @@ EPMD_NODE_TYPE=110
# Distribution format 5 contains the new md5 based handshake.
EPMD_DIST_LOW=5
-EPMD_DIST_HIGH=6
+EPMD_DIST_HIGH=5
diff --git a/erts/epmd/src/epmd.h b/erts/epmd/src/epmd.h
index 7332294d3d..cffcd4ae7a 100644
--- a/erts/epmd/src/epmd.h
+++ b/erts/epmd/src/epmd.h
@@ -26,7 +26,6 @@
#define EPMD_ALIVE2_REQ 'x'
#define EPMD_PORT2_REQ 'z'
#define EPMD_ALIVE2_RESP 'y'
-#define EPMD_ALIVE2_X_RESP 'v'
#define EPMD_PORT2_RESP 'w'
#define EPMD_NAMES_REQ 'n'
diff --git a/erts/epmd/src/epmd_int.h b/erts/epmd/src/epmd_int.h
index a5156a142e..ed9bbdb8cd 100644
--- a/erts/epmd/src/epmd_int.h
+++ b/erts/epmd/src/epmd_int.h
@@ -277,12 +277,6 @@ static const struct in6_addr in6addr_loopback =
#define put_int16(i, s) {((unsigned char*)(s))[0] = ((i) >> 8) & 0xff; \
((unsigned char*)(s))[1] = (i) & 0xff;}
-#define put_int32(i, s) do {((char*)(s))[0] = (char)((i) >> 24) & 0xff; \
- ((char*)(s))[1] = (char)((i) >> 16) & 0xff; \
- ((char*)(s))[2] = (char)((i) >> 8) & 0xff; \
- ((char*)(s))[3] = (char)(i) & 0xff;} \
- while (0)
-
#if defined(__GNUC__)
# define EPMD_INLINE __inline__
#elif defined(__WIN32__)
@@ -313,10 +307,10 @@ struct enode {
int fd; /* The socket in use */
unsigned short port; /* Port number of Erlang node */
char symname[MAXSYMLEN+1]; /* Name of the Erlang node */
- unsigned int cr_counter; /* Used to generate 'creation' numbers */
+ short creation; /* Started as a random number 1..3 */
char nodetype; /* 77 = normal erlang node 72 = hidden (c-node */
char protocol; /* 0 = tcp/ipv4 */
- unsigned short highvsn; /* 5: creation=1..3, 6: creation=1..(2^32-1)*/
+ unsigned short highvsn; /* 0 = OTP-R3 erts-4.6.x, 1 = OTP-R4 erts-4.7.x*/
unsigned short lowvsn;
int extralen;
char extra[MAXSYMLEN+1];
diff --git a/erts/epmd/src/epmd_srv.c b/erts/epmd/src/epmd_srv.c
index 633ec71e5f..3c6f1fbdf4 100644
--- a/erts/epmd/src/epmd_srv.c
+++ b/erts/epmd/src/epmd_srv.c
@@ -665,21 +665,6 @@ static int do_accept(EpmdVars *g,int listensock)
return conn_open(g,msgsock);
}
-static void bump_creation(Node* node)
-{
- if (++node->cr_counter == 0)
- node->cr_counter = 1;
-}
-static unsigned int get_creation(Node* node)
-{
- if (node->highvsn >= 6) {
- return node->cr_counter; /* 1..(2^32-1)*/
- }
- else {
- return (node->cr_counter - 1) % 3 + 1; /* 1..3 */
- }
-}
-
/* buf is actually one byte larger than bsize,
giving place for null termination */
static void do_request(g, fd, s, buf, bsize)
@@ -721,10 +706,8 @@ static void do_request(g, fd, s, buf, bsize)
unsigned char protocol;
unsigned short highvsn;
unsigned short lowvsn;
- unsigned int creation;
int namelen;
int extralen;
- int replylen;
char *name;
char *extra;
eport = get_int16(&buf[1]);
@@ -754,22 +737,17 @@ static void do_request(g, fd, s, buf, bsize)
extra = &buf[11+namelen+2];
extra[extralen]='\000';
- node = node_reg2(g, namelen, name, fd, eport, nodetype, protocol,
- highvsn, lowvsn, extralen, extra);
- creation = node ? get_creation(node) : 99;
- wbuf[1] = node ? 0 : 1; /* ok | error */
- if (highvsn >= 6) {
- wbuf[0] = EPMD_ALIVE2_X_RESP;
- put_int32(creation, wbuf+2);
- replylen = 6;
- }
- else {
- wbuf[0] = EPMD_ALIVE2_RESP;
- put_int16(creation, wbuf+2);
- replylen = 4;
- }
+ wbuf[0] = EPMD_ALIVE2_RESP;
+ if ((node = node_reg2(g, namelen, name, fd, eport, nodetype, protocol,
+ highvsn, lowvsn, extralen, extra)) == NULL) {
+ wbuf[1] = 1; /* error */
+ put_int16(99, wbuf+2);
+ } else {
+ wbuf[1] = 0; /* ok */
+ put_int16(node->creation, wbuf+2);
+ }
- if (!reply(g, fd, wbuf, replylen))
+ if (!reply(g, fd, wbuf, 4))
{
node_unreg(g, name);
dbg_tty_printf(g,1,"** failed to send ALIVE2_RESP for \"%s\"",
@@ -1222,8 +1200,8 @@ static int node_unreg(EpmdVars *g,char *name)
for (; node; prev = &node->next, node = node->next)
if (is_same_str(node->symname, name))
{
- dbg_tty_printf(g,1,"unregistering '%s:%u', port %d",
- node->symname, get_creation(node), node->port);
+ dbg_tty_printf(g,1,"unregistering '%s:%d', port %d",
+ node->symname, node->creation, node->port);
*prev = node->next; /* Link out from "reg" list */
@@ -1257,8 +1235,8 @@ static int node_unreg_sock(EpmdVars *g,int fd)
for (; node; prev = &node->next, node = node->next)
if (node->fd == fd)
{
- dbg_tty_printf(g,1,"unregistering '%s:%u', port %d",
- node->symname, get_creation(node), node->port);
+ dbg_tty_printf(g,1,"unregistering '%s:%d', port %d",
+ node->symname, node->creation, node->port);
*prev = node->next; /* Link out from "reg" list */
@@ -1286,8 +1264,19 @@ static int node_unreg_sock(EpmdVars *g,int fd)
}
/*
- * Register a new node
+ * Finding a node slot and a (name,creation) name is a bit tricky.
+ * We try in order
+ *
+ * 1. If the name was used before and we can reuse that slot but use
+ * a new "creation" digit in the range 1..3.
+ *
+ * 2. We try to find a new unused slot.
+ *
+ * 3. We try to use an used slot this isn't used any longer.
+ * FIXME: The criteria for *what* slot to steal should be improved.
+ * Perhaps use the oldest or something.
*/
+
static Node *node_reg2(EpmdVars *g,
int namelen,
char* name,
@@ -1357,7 +1346,7 @@ static Node *node_reg2(EpmdVars *g,
}
/* Try to find the name in the used queue so that we
- can change "creation" number */
+ can change "creation" number 1..3 */
prev = NULL;
@@ -1386,8 +1375,9 @@ static Node *node_reg2(EpmdVars *g,
g->nodes.unreg_count--;
- /* When reusing we change the "creation" number */
- bump_creation(node);
+ /* When reusing we change the "creation" number 1..3 */
+
+ node->creation = node->creation % 3 + 1;
break;
}
@@ -1414,8 +1404,7 @@ static Node *node_reg2(EpmdVars *g,
exit(1);
}
- node->cr_counter = current_time(g); /* "random" */
- bump_creation(node);
+ node->creation = (current_time(g) % 3) + 1; /* "random" 1-3 */
}
}
@@ -1434,11 +1423,11 @@ static Node *node_reg2(EpmdVars *g,
select_fd_set(g, fd);
if (highvsn == 0) {
- dbg_tty_printf(g,1,"registering '%s:%u', port %d",
- node->symname, get_creation(node), node->port);
+ dbg_tty_printf(g,1,"registering '%s:%d', port %d",
+ node->symname, node->creation, node->port);
} else {
- dbg_tty_printf(g,1,"registering '%s:%u', port %d",
- node->symname, get_creation(node), node->port);
+ dbg_tty_printf(g,1,"registering '%s:%d', port %d",
+ node->symname, node->creation, node->port);
dbg_tty_printf(g,1,"type %d proto %d highvsn %d lowvsn %d",
nodetype, protocol, highvsn, lowvsn);
}
@@ -1572,8 +1561,8 @@ static void print_names(EpmdVars *g)
for (node = g->nodes.reg; node; node = node->next)
{
- fprintf(stderr,"***** active name \"%s#%u\" at port %d, fd = %d\r\n",
- node->symname, get_creation(node), node->port, node->fd);
+ fprintf(stderr,"***** active name \"%s#%d\" at port %d, fd = %d\r\n",
+ node->symname, node->creation, node->port, node->fd);
count ++;
}
@@ -1583,8 +1572,8 @@ static void print_names(EpmdVars *g)
for (node = g->nodes.unreg; node; node = node->next)
{
- fprintf(stderr,"***** old/unused name \"%s#%u\"\r\n",
- node->symname, get_creation(node));
+ fprintf(stderr,"***** old/unused name \"%s#%d\"\r\n",
+ node->symname, node->creation);
count ++;
}
diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src
index bcd64d242e..8cfc2d549e 100644
--- a/erts/etc/unix/cerl.src
+++ b/erts/etc/unix/cerl.src
@@ -320,7 +320,7 @@ if [ "x$GDB" = "x" ]; then
if [ "$1" = "-p" ]; then
echo 'set $etp_rr_run_until_beam = 1' >> $cmdfile
fi
- cat $ROOTDIR/erts/etc/unix/etp-commands.in >> $cmdfile
+ cat $ROOTDIR/erts/etc/unix/etp-commands >> $cmdfile
exec rr replay -x $cmdfile $*
elif [ $1 = ps ]; then
shift
diff --git a/erts/etc/unix/etp-commands.in b/erts/etc/unix/etp-commands.in
index dc28107ef5..f2b2ab1d9a 100644
--- a/erts/etc/unix/etp-commands.in
+++ b/erts/etc/unix/etp-commands.in
@@ -2090,7 +2090,7 @@ define etp-process-info-int
etp-pid2proc-1 $etp_proc->common.id
etp-process-info $proc
else
- if (*(((Uint32 *) &($etp_proc->state))) & 0x4) == 0
+ if (*(((Uint32 *) &($etp_proc->state))) & 0x800) == 0
if ($etp_proc->common.u.alive.reg)
printf " Registered name: "
etp-1 $etp_proc->common.u.alive.reg->name
@@ -2176,8 +2176,8 @@ end
define etp-processes-free-de-int
set $de_ix = 0
- while $de_ix < ($arg1)
- set $de = ($arg0)+$de_ix
+ set $de = ($arg0)
+ while $de
set $susp = $de->suspended
set $susp_curr = $susp
set $first_loop = 1
@@ -2191,7 +2191,7 @@ define etp-processes-free-de-int
set $first_loop = 0
set $susp_curr = $susp_curr->next
end
- set $de_ix++
+ set $de = $de->next
end
end
diff --git a/erts/etc/unix/to_erl.c b/erts/etc/unix/to_erl.c
index ed4fe12e8b..1448980f77 100644
--- a/erts/etc/unix/to_erl.c
+++ b/erts/etc/unix/to_erl.c
@@ -415,7 +415,7 @@ int main(int argc, char **argv)
if (len) {
#ifdef DEBUG
- (void)write(1, buf, len);
+ write_all(1, buf, len);
#endif
if (write_all(wfd, buf, len) != len) {
fprintf(stderr, "Error in writing to FIFO.\n");
diff --git a/erts/include/internal/ethr_internal.h b/erts/include/internal/ethr_internal.h
index ac27ff2ed0..17ec84c52b 100644
--- a/erts/include/internal/ethr_internal.h
+++ b/erts/include/internal/ethr_internal.h
@@ -90,7 +90,7 @@ int ethr_init_common__(ethr_init_data *id);
int ethr_late_init_common__(ethr_late_init_data *lid);
void ethr_run_exit_handlers__(void);
void ethr_ts_event_destructor__(void *vtsep);
-void ethr_set_stacklimit__(char *prev_c, size_t stacksize);
+void ethr_set_stacklimit__(char *prev_c, size_t stacksize) ETHR_NOINLINE;
#if defined(ETHR_X86_RUNTIME_CONF__)
void ethr_x86_cpuid__(int *eax, int *ebx, int *ecx, int *edx);
diff --git a/erts/include/internal/ethread_inline.h b/erts/include/internal/ethread_inline.h
index 8e6bcfc4a8..791d7fa0ff 100644
--- a/erts/include/internal/ethread_inline.h
+++ b/erts/include/internal/ethread_inline.h
@@ -62,12 +62,15 @@
# define ETHR_INLINE __inline__
# if ETHR_AT_LEAST_GCC_VSN__(3, 1, 1)
# define ETHR_FORCE_INLINE __inline__ __attribute__((__always_inline__))
+# define ETHR_NOINLINE __attribute__((__noinline__))
# else
# define ETHR_FORCE_INLINE __inline__
+# define ETHR_NOINLINE
# endif
#elif defined(__WIN32__)
# define ETHR_INLINE __forceinline
# define ETHR_FORCE_INLINE __forceinline
+# define ETHR_NOINLINE
#endif
#endif /* #ifndef ETHREAD_INLINE_H__ */
diff --git a/erts/test/erl_print_SUITE.erl b/erts/test/erl_print_SUITE.erl
index 0a5987df88..463d890688 100644
--- a/erts/test/erl_print_SUITE.erl
+++ b/erts/test/erl_print_SUITE.erl
@@ -324,9 +324,6 @@ run_case(Config, TestArgs, Fun) ->
-define(PORT_EXT, 102).
-define(PID_EXT, 103).
-define(NEW_REFERENCE_EXT, 114).
--define(NEW_PID_EXT, $X).
--define(NEW_PORT_EXT, $Y).
--define(NEWER_REFERENCE_EXT, $Z).
uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 ->
[(Uint bsr 24) band 16#ff,
@@ -354,13 +351,13 @@ mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) ->
mk_pid({atom_to_list(NodeName), Creation}, Number, Serial);
mk_pid({NodeName, Creation}, Number, Serial) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?NEW_PID_EXT,
+ ?PID_EXT,
?ATOM_EXT,
uint16_be(length(NodeName)),
NodeName,
uint32_be(Number),
uint32_be(Serial),
- uint32_be(Creation)])) of
+ uint8(Creation)])) of
Pid when is_pid(Pid) ->
Pid;
{'EXIT', {badarg, _}} ->
@@ -373,12 +370,12 @@ mk_port({NodeName, Creation}, Number) when is_atom(NodeName) ->
mk_port({atom_to_list(NodeName), Creation}, Number);
mk_port({NodeName, Creation}, Number) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?NEW_PORT_EXT,
+ ?PORT_EXT,
?ATOM_EXT,
uint16_be(length(NodeName)),
NodeName,
uint32_be(Number),
- uint32_be(Creation)])) of
+ uint8(Creation)])) of
Port when is_port(Port) ->
Port;
{'EXIT', {badarg, _}} ->
@@ -391,16 +388,33 @@ mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName),
is_integer(Creation),
is_list(Numbers) ->
mk_ref({atom_to_list(NodeName), Creation}, Numbers);
+mk_ref({NodeName, Creation}, [Number]) when is_list(NodeName),
+ is_integer(Creation),
+ is_integer(Number) ->
+ case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
+ ?REFERENCE_EXT,
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint32_be(Number),
+ uint8(Creation)])) of
+ Ref when is_reference(Ref) ->
+ Ref;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_ref, [{NodeName, Creation}, [Number]]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end;
mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
is_integer(Creation),
is_list(Numbers) ->
case catch binary_to_term(list_to_binary([?VERSION_MAGIC,
- ?NEWER_REFERENCE_EXT,
+ ?NEW_REFERENCE_EXT,
uint16_be(length(Numbers)),
?ATOM_EXT,
uint16_be(length(NodeName)),
NodeName,
- uint32_be(Creation),
+ uint8(Creation),
lists:map(fun (N) ->
uint32_be(N)
end,
@@ -415,10 +429,11 @@ mk_ref({NodeName, Creation}, Numbers) when is_list(NodeName),
my_cre() -> erlang:system_info(creation).
-oth_cre(N) when N >= 0, N < (1 bsl 32) ->
- (N rem ((1 bsl 32) - 1)) + 1;
-oth_cre(N) ->
- exit({invalid_creation, N}).
+oth_cre(0) -> 1;
+oth_cre(1) -> 2;
+oth_cre(2) -> 3;
+oth_cre(3) -> 1;
+oth_cre(N) -> exit({invalid_creation, N}).
str_1_bsl_10000() ->
"19950631168807583848837421626835850838234968318861924548520089498529438830221946631919961684036194597899331129423209124271556491349413781117593785932096323957855730046793794526765246551266059895520550086918193311542508608460618104685509074866089624888090489894838009253941633257850621568309473902556912388065225096643874441046759871626985453222868538161694315775629640762836880760732228535091641476183956381458969463899410840960536267821064621427333394036525565649530603142680234969400335934316651459297773279665775606172582031407994198179607378245683762280037302885487251900834464581454650557929601414833921615734588139257095379769119277800826957735674444123062018757836325502728323789270710373802866393031428133241401624195671690574061419654342324638801248856147305207431992259611796250130992860241708340807605932320161268492288496255841312844061536738951487114256315111089745514203313820202931640957596464756010405845841566072044962867016515061920631004186422275908670900574606417856951911456055068251250406007519842261898059237118054444788072906395242548339221982707404473162376760846613033778706039803413197133493654622700563169937455508241780972810983291314403571877524768509857276937926433221599399876886660808368837838027643282775172273657572744784112294389733810861607423253291974813120197604178281965697475898164531258434135959862784130128185406283476649088690521047580882615823961985770122407044330583075869039319604603404973156583208672105913300903752823415539745394397715257455290510212310947321610753474825740775273986348298498340756937955646638621874569499279016572103701364433135817214311791398222983845847334440270964182851005072927748364550578634501100852987812389473928699540834346158807043959118985815145779177143619698728131459483783202081474982171858011389071228250905826817436220577475921417653715687725614904582904992461028630081535583308130101987675856234343538955409175623400844887526162643568648833519463720377293240094456246923254350400678027273837755376406726898636241037491410966718557050759098100246789880178271925953381282421954028302759408448955014676668389697996886241636313376393903373455801407636741877711055384225739499110186468219696581651485130494222369947714763069155468217682876200362777257723781365331611196811280792669481887201298643660768551639860534602297871557517947385246369446923087894265948217008051120322365496288169035739121368338393591756418733850510970271613915439590991598154654417336311656936031122249937969999226781732358023111862644575299135758175008199839236284615249881088960232244362173771618086357015468484058622329792853875623486556440536962622018963571028812361567512543338303270029097668650568557157505516727518899194129711337690149916181315171544007728650573189557450920330185304847113818315407324053319038462084036421763703911550639789000742853672196280903477974533320468368795868580237952218629120080742819551317948157624448298518461509704888027274721574688131594750409732115080498190455803416826949787141316063210686391511681774304792596709376".
diff --git a/erts/test/z_SUITE.erl b/erts/test/z_SUITE.erl
index 6a34299dd2..536212af2e 100644
--- a/erts/test/z_SUITE.erl
+++ b/erts/test/z_SUITE.erl
@@ -88,10 +88,10 @@ find_cerl(DBTop) ->
[Cerl | _ ] ->
case filelib:is_regular(Cerl) of
true -> Cerl;
- _ -> false
+ _ -> find_cerl(false)
end;
_ ->
- false
+ find_cerl(false)
end.
is_dir(false) ->
diff --git a/erts/vsn.mk b/erts/vsn.mk
index 3942af7f78..fafcdf3b28 100644
--- a/erts/vsn.mk
+++ b/erts/vsn.mk
@@ -18,7 +18,7 @@
# %CopyrightEnd%
#
-VSN = 10.3.1
+VSN = 10.3.4
# Port number 4365 in 4.2
# Port number 4366 in 4.3