summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bootstrap/lib/stdlib/ebin/erl_internal.beambin6732 -> 6732 bytes
-rw-r--r--erts/doc/src/erlang.xml85
-rw-r--r--erts/emulator/beam/atom.names1
-rw-r--r--erts/emulator/beam/bif.tab2
-rw-r--r--erts/emulator/beam/dist.c498
-rw-r--r--erts/emulator/beam/erl_map.c134
-rw-r--r--erts/emulator/beam/erl_map.h4
-rw-r--r--erts/emulator/beam/erl_node_tables.c6
-rw-r--r--erts/emulator/test/distribution_SUITE.erl292
-rw-r--r--erts/preloaded/ebin/erlang.beambin100436 -> 100708 bytes
-rw-r--r--erts/preloaded/src/erlang.erl34
-rw-r--r--erts/preloaded/src/erts.app.src2
-rw-r--r--lib/kernel/doc/src/net_kernel.xml219
-rw-r--r--lib/kernel/src/kernel.app.src2
-rw-r--r--lib/kernel/src/net_kernel.erl77
-rw-r--r--lib/kernel/test/erl_distribution_SUITE.erl99
-rw-r--r--lib/stdlib/src/erl_internal.erl1
-rw-r--r--lib/stdlib/src/stdlib.app.src2
18 files changed, 1200 insertions, 258 deletions
diff --git a/bootstrap/lib/stdlib/ebin/erl_internal.beam b/bootstrap/lib/stdlib/ebin/erl_internal.beam
index 9015ad8936..7f2a331f00 100644
--- a/bootstrap/lib/stdlib/ebin/erl_internal.beam
+++ b/bootstrap/lib/stdlib/ebin/erl_internal.beam
Binary files differ
diff --git a/erts/doc/src/erlang.xml b/erts/doc/src/erlang.xml
index 613d382396..a94c10bf22 100644
--- a/erts/doc/src/erlang.xml
+++ b/erts/doc/src/erlang.xml
@@ -3806,6 +3806,91 @@ RealSystem = system + MissedSystem</code>
</func>
<func>
+ <name name="nodes" arity="2" since="OTP @OTP-17934@"/>
+ <fsummary>All nodes of a certain type in the system.</fsummary>
+ <desc>
+ <p>
+ Returns a list of <c><anno>NodeInfo</anno></c> tuples. The first
+ element is the node name. Nodes to be included in the list are
+ determined by the first argument <c><anno>Arg</anno></c> in the same
+ way as for
+ <seealso marker="#nodes/1"><c>nodes(<anno>Arg</anno>)</c></seealso>.
+ The second element of <c><anno>NodeInfo</anno></c> tuples is a map
+ containing further information about the node identified by the
+ first element. The information present in this map is determined by
+ the <c><anno>InfoOpts</anno></c> map passed as the second argument.
+ Currently the following associations are allowed in the
+ <c><anno>InfoOpts</anno></c> map:</p>
+ <taglist>
+ <tag><c>connection_id => boolean()</c></tag>
+ <item><p>
+ If the value of the association equals <c>true</c>, the <c>Info</c>
+ map in the returned result will contain the key <c>connection_id</c>
+ associated with the value <c><anno>ConnectionId</anno></c>. If
+ <c><anno>ConnectionId</anno></c> equals <c>undefined</c>, the node
+ is not connected to the node which the caller is executing on, or
+ is the node which the caller is executing on. If
+ <c><anno>ConnectionId</anno></c> is an integer, the node is
+ currently connected to the node which the caller is executing on.
+ </p>
+ <p>
+ <marker id="connection_id"/>
+ The integer connection identifier value together with a node name
+ identifies a specific connection instance to the node with that
+ node name. The connection identifier value is node local. That is,
+ on the other node the connection identifier will <i>not</i> be the
+ same value. If a connection is taken down and then taken up again,
+ the connection identifier value will change for the connection to
+ that node. The amount of values for connection identifiers are
+ limited, so it is possible to see the same value for different
+ instances, but quite unlikely. It is undefined how the value
+ change between two consecutive connection instances.
+ </p></item>
+ <tag><c>node_type => boolean()</c></tag>
+ <item><p>
+ If the value of the association equals <c>true</c>, the <c>Info</c>
+ map in the returned result will contain the key <c>node_type</c>
+ associated with the value <c><anno>NodeTypeInfo</anno></c>.
+ Currently the following node types exist:</p>
+ <taglist>
+ <tag><c>visible</c></tag>
+ <item><p>
+ The node is connected to the node of the calling process
+ through an ordinary visible connection. That is, the node
+ name would appear in the result returned by
+ <seealso marker="#nodes/0"><c>nodes/0</c></seealso>.
+ </p></item>
+ <tag><c>hidden</c></tag>
+ <item><p>
+ The node is connected to the node of the calling process
+ through a hidden connection. That is, the node
+ name would <i>not</i> appear in the result returned by
+ <seealso marker="#nodes/0"><c>nodes/0</c></seealso>.
+ </p></item>
+ <tag><c>this</c></tag>
+ <item><p>
+ This is the node of the calling process.
+ </p></item>
+ <tag><c>known</c></tag>
+ <item><p>
+ The node is not connected but known to the node of the
+ calling process.
+ </p></item>
+ </taglist>
+ </item>
+ </taglist>
+ <p>Example:</p>
+ <code type="erl">
+(a@localhost)1> nodes([this, connected], #{connection_id=>true, node_type=>true}).
+[{c@localhost,#{connection_id => 13892108,node_type => hidden}},
+ {b@localhost,#{connection_id => 3067553,node_type => visible}},
+ {a@localhost,#{connection_id => undefined,node_type => this}}]
+(a@localhost)2>
+ </code>
+ </desc>
+ </func>
+
+ <func>
<name name="now" arity="0" since=""/>
<fsummary>Elapsed time since 00:00 GMT.</fsummary>
<type name="timestamp"/>
diff --git a/erts/emulator/beam/atom.names b/erts/emulator/beam/atom.names
index 93960862b0..c2c00a6da1 100644
--- a/erts/emulator/beam/atom.names
+++ b/erts/emulator/beam/atom.names
@@ -178,6 +178,7 @@ atom convert_time_unit
atom connect
atom connected
atom connection_closed
+atom connection_id
atom const
atom context_switches
atom control
diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab
index 8d16ffd857..c330e329a2 100644
--- a/erts/emulator/beam/bif.tab
+++ b/erts/emulator/beam/bif.tab
@@ -110,7 +110,9 @@ bif erlang:monitor_node/2
bif erlang:monitor_node/3
ubif erlang:node/1
ubif erlang:node/0
+bif erlang:nodes/0
bif erlang:nodes/1
+bif erlang:nodes/2
bif erlang:now/0
bif erlang:monotonic_time/0
bif erlang:monotonic_time/1
diff --git a/erts/emulator/beam/dist.c b/erts/emulator/beam/dist.c
index d37211c742..8be2a77ae3 100644
--- a/erts/emulator/beam/dist.c
+++ b/erts/emulator/beam/dist.c
@@ -46,6 +46,7 @@
#include "erl_thr_progress.h"
#include "dtrace-wrapper.h"
#include "erl_proc_sig_queue.h"
+#include "erl_map.h"
#define DIST_CTL_DEFAULT_SIZE 64
@@ -180,7 +181,7 @@ static Export *dist_ctrl_put_data_trap;
static void erts_schedule_dist_command(Port *, DistEntry *);
static int dsig_send_exit(ErtsDSigSendContext *ctx, Eterm ctl, Eterm msg);
static int dsig_send_ctl(ErtsDSigSendContext *ctx, Eterm ctl);
-static void send_nodes_mon_msgs(Process *, Eterm, Eterm, Eterm, Eterm);
+static void send_nodes_mon_msgs(Process *, Eterm, Eterm, Uint32, Eterm, Eterm);
static void init_nodes_monitors(void);
static Sint abort_pending_connection(DistEntry* dep, Uint32 conn_id,
int *was_connected_p);
@@ -280,6 +281,7 @@ typedef enum {
typedef struct {
ErtsConMonLnkSeqCleanupState state;
DistEntry* dep;
+ Uint32 connection_id;
ErtsMonLnkDist *dist;
DistSeqNode *seq;
void *yield_state;
@@ -348,6 +350,7 @@ con_monitor_link_seq_cleanup(void *vcmlcp)
send_nodes_mon_msgs(NULL,
am_nodedown,
cmlcp->nodename,
+ cmlcp->connection_id,
cmlcp->visability,
cmlcp->reason);
erts_de_rwlock(cmlcp->dep);
@@ -413,10 +416,13 @@ schedule_con_monitor_link_seq_cleanup(DistEntry* dep,
cmlcp->yield_state = NULL;
cmlcp->dist = dist;
- if (!dist)
+ if (!dist) {
cmlcp->state = ERTS_CML_CLEANUP_STATE_NODE_MONITORS;
+ cmlcp->connection_id = 0;
+ }
else {
cmlcp->state = ERTS_CML_CLEANUP_STATE_LINKS;
+ cmlcp->connection_id = dist->connection_id;
erts_mtx_lock(&dist->mtx);
ASSERT(dist->alive);
dist->alive = 0;
@@ -514,7 +520,8 @@ set_node_not_alive(void *unused)
erts_thr_progress_block();
erts_set_this_node(am_Noname, 0);
erts_is_alive = 0;
- send_nodes_mon_msgs(NULL, am_nodedown, nodename, am_visible, nodedown.reason);
+ send_nodes_mon_msgs(NULL, am_nodedown, nodename, ~((Uint32) 0),
+ am_visible, nodedown.reason);
nodedown.reason = NIL;
bp = nodedown.bp;
nodedown.bp = NULL;
@@ -3842,7 +3849,8 @@ BIF_RETTYPE setnode_2(BIF_ALIST_2)
inc_no_nodes();
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);
+ send_nodes_mon_msgs(NULL, am_nodeup, BIF_ARG_1, ~((Uint32) 0),
+ am_visible, NIL);
erts_proc_lock(net_kernel, ERTS_PROC_LOCKS_ALL);
/* By setting F_DISTRIBUTION on net_kernel,
@@ -4221,6 +4229,7 @@ setup_connection_epiloge_rwunlock(Process *c_p, DistEntry *dep,
send_nodes_mon_msgs(c_p,
am_nodeup,
dep->sysname,
+ dep->connection_id,
flags & DFLAG_PUBLISHED ? am_visible : am_hidden,
NIL);
@@ -4497,38 +4506,54 @@ BIF_RETTYPE node_0(BIF_ALIST_0)
BIF_RET(erts_this_dist_entry->sysname);
}
-
/**********************************************************************/
/* nodes() -> [ Node ] */
-#if 0 /* Done in erlang.erl instead. */
+static BIF_RETTYPE nodes(Process *c_p, Eterm node_types, Eterm options);
+
BIF_RETTYPE nodes_0(BIF_ALIST_0)
{
- return nodes_1(BIF_P, am_visible);
+ return nodes(BIF_P, am_visible, THE_NON_VALUE);
}
-#endif
-
BIF_RETTYPE nodes_1(BIF_ALIST_1)
{
+ return nodes(BIF_P, BIF_ARG_1, THE_NON_VALUE);
+}
+
+BIF_RETTYPE nodes_2(BIF_ALIST_2)
+{
+ return nodes(BIF_P, BIF_ARG_1, BIF_ARG_2);
+}
+
+typedef struct {
+ Eterm name;
+ Eterm type;
+ Uint32 cid;
+} ErtsNodeInfo;
+
+static BIF_RETTYPE
+nodes(Process *c_p, Eterm node_types, Eterm options)
+{
+ BIF_RETTYPE ret_val;
+ ErtsNodeInfo *eni, *eni_start = NULL, *eni_end;
Eterm result;
- int length;
- Eterm* hp;
+ Uint length;
int not_connected = 0;
int visible = 0;
int hidden = 0;
int this = 0;
- DeclareTmpHeap(buf,2,BIF_P); /* For one cons-cell */
+ int node_type = 0;
+ int connection_id = 0;
+ int xinfo = 0;
+ Eterm tmp_heap[2]; /* For one cons-cell */
DistEntry *dep;
- Eterm arg_list = BIF_ARG_1;
-#ifdef DEBUG
- Eterm* endp;
-#endif
-
- UseTmpHeap(2,BIF_P);
+ Eterm arg_list;
- if (is_atom(BIF_ARG_1))
- arg_list = CONS(buf, BIF_ARG_1, NIL);
+ if (is_atom(node_types))
+ arg_list = CONS(&tmp_heap[0], node_types, NIL);
+ else
+ arg_list = node_types;
while (is_list(arg_list)) {
switch(CAR(list_val(arg_list))) {
@@ -4537,13 +4562,43 @@ BIF_RETTYPE nodes_1(BIF_ALIST_1)
case am_known: visible = hidden = not_connected = this = 1; break;
case am_this: this = 1; break;
case am_connected: visible = hidden = 1; break;
- default: goto error; break;
+ default: goto badarg; break;
}
arg_list = CDR(list_val(arg_list));
}
if (is_not_nil(arg_list)) {
- goto error;
+ goto badarg;
+ }
+
+ if (is_value(options)) {
+ if (is_not_map(options)) {
+ goto badarg;
+ }
+ else {
+ Sint no_opts = 0;
+ const Eterm *conn_idp = erts_maps_get(am_connection_id, options);
+ const Eterm *node_typep = erts_maps_get(am_node_type, options);
+ if (conn_idp) {
+ switch (*conn_idp) {
+ case am_true: connection_id = !0; break;
+ case am_false: connection_id = 0; break;
+ default: goto badarg;
+ }
+ no_opts++;
+ }
+ if (node_typep) {
+ switch (*node_typep) {
+ case am_true: node_type = !0; break;
+ case am_false: node_type = 0; break;
+ default: goto badarg;
+ }
+ no_opts++;
+ }
+ if (no_opts != erts_map_size(options))
+ goto badarg; /* got invalid options... */
+ xinfo = !0;
+ }
}
length = 0;
@@ -4568,50 +4623,130 @@ BIF_RETTYPE nodes_1(BIF_ALIST_1)
if (length == 0) {
erts_rwmtx_runlock(&erts_dist_table_rwmtx);
- goto done;
+ ERTS_BIF_PREP_RET(ret_val, NIL);
+ return ret_val;
}
- hp = HAlloc(BIF_P, 2*length);
+ eni_start = eni = erts_alloc(ERTS_ALC_T_TMP, sizeof(ErtsNodeInfo)*length);
-#ifdef DEBUG
- endp = hp + length*2;
-#endif
- if(not_connected) {
- for(dep = erts_not_connected_dist_entries; dep; dep = dep->next) {
- if (dep != erts_this_dist_entry) {
- result = CONS(hp, dep->sysname, result);
- hp += 2;
- }
+ if (this) {
+ eni->name = erts_this_dist_entry->sysname;
+ eni->type = am_this;
+ eni->cid = ~((Uint32) 0);
+ eni++;
+ }
+
+ if (visible) {
+ for (dep = erts_visible_dist_entries; dep; dep = dep->next) {
+ eni->name = dep->sysname;
+ eni->type = am_visible;
+ eni->cid = dep->connection_id;
+ ASSERT(eni->cid >= 0);
+ eni++;
}
- for(dep = erts_pending_dist_entries; dep; dep = dep->next) {
- result = CONS(hp, dep->sysname, result);
- hp += 2;
- }
}
- if(hidden)
- for(dep = erts_hidden_dist_entries; dep; dep = dep->next) {
- result = CONS(hp, dep->sysname, result);
- hp += 2;
- }
- if(visible)
- for(dep = erts_visible_dist_entries; dep; dep = dep->next) {
- result = CONS(hp, dep->sysname, result);
- hp += 2;
- }
- if(this) {
- result = CONS(hp, erts_this_dist_entry->sysname, result);
- hp += 2;
+
+ if (hidden) {
+ for (dep = erts_hidden_dist_entries; dep; dep = dep->next) {
+ eni->name = dep->sysname;
+ eni->type = am_hidden;
+ eni->cid = dep->connection_id;
+ eni++;
+ }
+ }
+
+ if (not_connected) {
+ for (dep = erts_not_connected_dist_entries; dep; dep = dep->next) {
+ if (dep != erts_this_dist_entry) {
+ eni->name = dep->sysname;
+ eni->type = am_known;
+ eni->cid = ~((Uint32) 0);
+ eni++;
+ }
+ }
+ for (dep = erts_pending_dist_entries; dep; dep = dep->next) {
+ eni->name = dep->sysname;
+ eni->type = am_known;
+ eni->cid = ~((Uint32) 0);
+ eni++;
+ }
}
- ASSERT(endp == hp);
+
erts_rwmtx_runlock(&erts_dist_table_rwmtx);
-done:
- UnUseTmpHeap(2,BIF_P);
- BIF_RET(result);
+ eni_end = eni;
+
+ result = NIL;
+ if (!xinfo) {
+ Eterm *hp = HAlloc(c_p, 2*length);
+ for (eni = eni_start; eni < eni_end; eni++) {
+ result = CONS(hp, eni->name, result);
+ hp += 2;
+ }
+ }
+ else {
+ Eterm ks[2], *hp, keys_tuple = THE_NON_VALUE;
+ Uint map_size = 0, el_xtra, xtra;
+ ErtsHeapFactory hfact;
+
+ erts_factory_proc_init(&hfact, c_p);
+
+ if (connection_id) {
+ ks[map_size++] = am_connection_id;
+ }
+ if (node_type) {
+ ks[map_size++] = am_node_type;
+ }
+
+ el_xtra = 3 + 2 + MAP_HEADER_FLATMAP_SZ + map_size;
+ xtra = length*el_xtra;
+
+ for (eni = eni_start; eni < eni_end; eni++) {
+ Eterm vs[2], info_map, tuple;
+ map_size = 0;
+ if (connection_id) {
+ Eterm cid;
+ if (eni->cid == ~((Uint32) 0))
+ cid = am_undefined;
+ else if (IS_USMALL(0, (Uint) eni->cid))
+ cid = make_small((Uint) eni->cid);
+ else {
+ hp = erts_produce_heap(&hfact, BIG_UINT_HEAP_SIZE, xtra);
+ cid = uint_to_big((Uint) eni->cid, hp);
+ }
+ vs[map_size++] = cid;
+ }
+ if (node_type) {
+ vs[map_size++] = eni->type;
+ }
+
+ info_map = erts_map_from_sorted_ks_and_vs(&hfact, ks, vs,
+ map_size, &keys_tuple);
+
+ hp = erts_produce_heap(&hfact, 3+2, xtra);
+
+ tuple = TUPLE2(hp, eni->name, info_map);
+ hp += 3;
+ result = CONS(hp, tuple, result);
+ xtra -= el_xtra;
+ }
+
+ erts_factory_close(&hfact);
+ }
+
+ erts_free(ERTS_ALC_T_TMP, (void *) eni_start);
+
+ if (length > 10) {
+ Uint reds = length / 10;
+ BUMP_REDS(c_p, reds);
+ }
+
+ ERTS_BIF_PREP_RET(ret_val, result);
+ return ret_val;
-error:
- UnUseTmpHeap(2,BIF_P);
- BIF_ERROR(BIF_P,BADARG);
+badarg:
+ ERTS_BIF_PREP_ERROR(ret_val, c_p, BADARG);
+ return ret_val;
}
/**********************************************************************/
@@ -4842,6 +4977,8 @@ BIF_RETTYPE net_kernel_dflag_unicode_io_1(BIF_ALIST_1)
#define ERTS_NODES_MON_OPT_TYPE_VISIBLE (((Uint16) 1) << 0)
#define ERTS_NODES_MON_OPT_TYPE_HIDDEN (((Uint16) 1) << 1)
#define ERTS_NODES_MON_OPT_DOWN_REASON (((Uint16) 1) << 2)
+#define ERTS_NODES_MON_OPT_INFO_MAP (((Uint16) 1) << 3)
+#define ERTS_NODES_MON_OPT_CONN_ID (((Uint16) 1) << 4)
#define ERTS_NODES_MON_OPT_TYPES \
(ERTS_NODES_MON_OPT_TYPE_VISIBLE|ERTS_NODES_MON_OPT_TYPE_HIDDEN)
@@ -4871,10 +5008,10 @@ init_nodes_monitors(void)
}
Eterm
-erts_monitor_nodes(Process *c_p, Eterm on, Eterm olist)
+erts_monitor_nodes(Process *c_p, Eterm on, Eterm options)
{
- Eterm key, old_value, opts_list = olist;
- Uint opts = (Uint) 0;
+ Eterm key, old_value;
+ Uint opts = (Uint) ERTS_NODES_MON_OPT_INFO_MAP;
ASSERT(c_p);
ERTS_LC_ASSERT(erts_proc_lc_my_proc_locks(c_p) == ERTS_PROC_LOCK_MAIN);
@@ -4882,55 +5019,63 @@ erts_monitor_nodes(Process *c_p, Eterm on, Eterm olist)
if (on != am_true && on != am_false)
return THE_NON_VALUE;
- if (is_not_nil(opts_list)) {
- int all = 0, visible = 0, hidden = 0;
-
- while (is_list(opts_list)) {
- Eterm *cp = list_val(opts_list);
- Eterm opt = CAR(cp);
- opts_list = CDR(cp);
- if (opt == am_nodedown_reason)
+ if (is_nil(options)) {
+ opts &= ~ERTS_NODES_MON_OPT_INFO_MAP;
+ }
+ else if (is_not_map(options)) {
+ return THE_NON_VALUE;
+ }
+ else {
+ Sint no_opts = 0;
+ const Eterm *l = erts_maps_get(am_list, options);
+ const Eterm *cid = erts_maps_get(am_connection_id, options);
+ const Eterm *nt = erts_maps_get(am_node_type, options);
+ const Eterm *nr = erts_maps_get(am_nodedown_reason, options);
+ if (l) {
+ if (*l == am_true) {
+ opts &= ~ERTS_NODES_MON_OPT_INFO_MAP;
+ }
+ else {
+ return THE_NON_VALUE;
+ }
+ no_opts++;
+ }
+ if (cid) {
+ if (*cid == am_true) {
+ opts |= ERTS_NODES_MON_OPT_CONN_ID;
+ }
+ else if (*cid != am_false) {
+ return THE_NON_VALUE;
+ }
+ no_opts++;
+ }
+ if (nt) {
+ switch (*nt) {
+ case am_visible:
+ opts |= ERTS_NODES_MON_OPT_TYPE_VISIBLE;
+ break;
+ case am_hidden:
+ opts |= ERTS_NODES_MON_OPT_TYPE_HIDDEN;
+ break;
+ case am_all:
+ opts |= ERTS_NODES_MON_OPT_TYPES;
+ break;
+ default:
+ return THE_NON_VALUE;
+ }
+ no_opts++;
+ }
+ if (nr) {
+ if (*nr == am_true) {
opts |= ERTS_NODES_MON_OPT_DOWN_REASON;
- else if (is_tuple(opt)) {
- Eterm* tp = tuple_val(opt);
- if (arityval(tp[0]) != 2)
- return THE_NON_VALUE;
- switch (tp[1]) {
- case am_node_type:
- switch (tp[2]) {
- case am_visible:
- if (hidden || all)
- return THE_NON_VALUE;
- opts |= ERTS_NODES_MON_OPT_TYPE_VISIBLE;
- visible = 1;
- break;
- case am_hidden:
- if (visible || all)
- return THE_NON_VALUE;
- opts |= ERTS_NODES_MON_OPT_TYPE_HIDDEN;
- hidden = 1;
- break;
- case am_all:
- if (visible || hidden)
- return THE_NON_VALUE;
- opts |= ERTS_NODES_MON_OPT_TYPES;
- all = 1;
- break;
- default:
- return THE_NON_VALUE;
- }
- break;
- default:
- return THE_NON_VALUE;
- }
- }
- else {
- return THE_NON_VALUE;
- }
- }
-
- if (is_not_nil(opts_list))
- return THE_NON_VALUE;
+ }
+ else if (*nr != am_false) {
+ return THE_NON_VALUE;
+ }
+ no_opts++;
+ }
+ if (no_opts != erts_map_size(options))
+ return THE_NON_VALUE; /* got invalid options... */
}
key = make_small(opts);
@@ -5023,8 +5168,24 @@ save_nodes_monitor(ErtsMonitor *mon, void *vctxt, Sint reds)
return 1;
}
+#define ERTS_MON_NODES_MAX_INFO_LIST_SZ__(MAX_ELEMS) \
+ ((MAX_ELEMS)*(3 /* key/value 2-tuple */ + 2/* cons cell */) \
+ + BIG_UINT_HEAP_SIZE /* connection id value */ \
+ + 4 /* top 3-tuple */)
+#define ERTS_MON_NODES_MAX_INFO_MAP_SZ__(MAX_ELEMS) \
+ ((MAX_ELEMS)*2 /* keys and values */ \
+ + 1 /* key tuple header */ + MAP_HEADER_FLATMAP_SZ /* 3 */ \
+ + BIG_UINT_HEAP_SIZE /* connection id value */ \
+ + 4 /* top 3-tuple */)
+#define ERTS_MON_NODES_MAX_INFO_SZ__(MAX_ELEMS) \
+ ((ERTS_MON_NODES_MAX_INFO_MAP_SZ__((MAX_ELEMS)) \
+ > ERTS_MON_NODES_MAX_INFO_LIST_SZ__((MAX_ELEMS))) \
+ ? ERTS_MON_NODES_MAX_INFO_MAP_SZ__((MAX_ELEMS)) \
+ : ERTS_MON_NODES_MAX_INFO_LIST_SZ__((MAX_ELEMS)))
+
static void
-send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reason)
+send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node,
+ Uint32 connection_id, Eterm type, Eterm reason)
{
Uint opts;
Uint i, no, reason_size;
@@ -5070,7 +5231,8 @@ send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reas
erts_mtx_unlock(&nodes_monitors_mtx);
for (i = 0; i < no; i++) {
- Eterm tmp_heap[3+2+3+2+4 /* max need */];
+ ErtsHeapFactory hfact;
+ Eterm tmp_heap[ERTS_MON_NODES_MAX_INFO_SZ__(3/* max info elements */)];
Eterm *hp, msg;
Uint hsz;
@@ -5096,42 +5258,114 @@ send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reas
}
}
+ /*
+ * tmp_heap[] is sized so there will be room for everything
+ * we need assuming no info, a two-tuple info list, or an info
+ * flat map is generated. In case there would be a greater heap
+ * need this will be taken care of by the heap factory...
+ */
+ erts_factory_tmp_init(&hfact,
+ &tmp_heap[0],
+ sizeof(tmp_heap)/sizeof(Uint),
+ ERTS_ALC_T_TMP);
hsz = 0;
- hp = &tmp_heap[0];
if (!opts) {
+ hp = erts_produce_heap(&hfact, 3, 0);
msg = TUPLE2(hp, what, node);
- hp += 3;
}
- else {
+ else { /* Info list or map... */
Eterm tup;
- Eterm info = NIL;
+ Eterm info;
- if (opts & (ERTS_NODES_MON_OPT_TYPE_VISIBLE
- | ERTS_NODES_MON_OPT_TYPE_HIDDEN)) {
+ if (opts & ERTS_NODES_MON_OPT_INFO_MAP) { /* Info map */
+ Uint map_size = 0;
+ Eterm ks[3], vs[3];
- tup = TUPLE2(hp, am_node_type, type);
- hp += 3;
- info = CONS(hp, tup, info);
- hp += 2;
- }
+ if (opts & ERTS_NODES_MON_OPT_CONN_ID) {
+ Eterm cid;
+ if (connection_id == ~((Uint32) 0)) {
+ cid = am_undefined;
+ }
+ else if (IS_USMALL(0, (Uint) connection_id)) {
+ cid = make_small(connection_id);
+ }
+ else {
+ hp = erts_produce_heap(&hfact, BIG_UINT_HEAP_SIZE, 0);
+ cid = uint_to_big(connection_id, hp);
+ }
+ ks[map_size] = am_connection_id;
+ vs[map_size] = cid;
+ map_size++;
+ }
+ if (opts & (ERTS_NODES_MON_OPT_TYPE_VISIBLE
+ | ERTS_NODES_MON_OPT_TYPE_HIDDEN)) {
+ ks[map_size] = am_node_type;
+ vs[map_size] = type;
+ map_size++;
+ }
+ if (what == am_nodedown
+ && (opts & ERTS_NODES_MON_OPT_DOWN_REASON)) {
+ hsz += reason_size;
+ ks[map_size] = am_nodedown_reason;
+ vs[map_size] = reason;
+ map_size++;
+ }
- if (what == am_nodedown
- && (opts & ERTS_NODES_MON_OPT_DOWN_REASON)) {
- hsz += reason_size;
- tup = TUPLE2(hp, am_nodedown_reason, reason);
- hp += 3;
- info = CONS(hp, tup, info);
- hp += 2;
+ info = erts_map_from_sorted_ks_and_vs(&hfact, ks, vs,
+ map_size, NULL);
+ ASSERT(is_value(info));
}
+ else { /* Info list */
+
+ info = NIL;
+ if (opts & (ERTS_NODES_MON_OPT_TYPE_VISIBLE
+ | ERTS_NODES_MON_OPT_TYPE_HIDDEN)) {
+ hp = erts_produce_heap(&hfact, 3 + 2, 0);
+ tup = TUPLE2(hp, am_node_type, type);
+ hp += 3;
+ info = CONS(hp, tup, info);
+ }
+ if (what == am_nodedown
+ && (opts & ERTS_NODES_MON_OPT_DOWN_REASON)) {
+ hp = erts_produce_heap(&hfact, 3 + 2, 0);
+ hsz += reason_size;
+ tup = TUPLE2(hp, am_nodedown_reason, reason);
+ hp += 3;
+ info = CONS(hp, tup, info);
+ }
+
+ if (opts & ERTS_NODES_MON_OPT_CONN_ID) {
+ Eterm cid;
+ if (connection_id == ~((Uint32) 0)) {
+ cid = am_undefined;
+ }
+ else if (IS_USMALL(0, (Uint) connection_id)) {
+ cid = make_small(connection_id);
+ }
+ else {
+ hp = erts_produce_heap(&hfact, BIG_UINT_HEAP_SIZE, 0);
+ cid = uint_to_big(connection_id, hp);
+ }
+ hp = erts_produce_heap(&hfact, 3 + 2, 0);
+ tup = TUPLE2(hp, am_connection_id, cid);
+ hp += 3;
+ info = CONS(hp, tup, info);
+ }
+ }
+
+ hp = erts_produce_heap(&hfact, 4, 0);
msg = TUPLE3(hp, what, node, info);
- hp += 4;
}
- ASSERT(hp - &tmp_heap[0] <= sizeof(tmp_heap)/sizeof(tmp_heap[0]));
- hsz += hp - &tmp_heap[0];
+ hsz += hfact.hp - hfact.hp_start;
+ if (hfact.heap_frags) {
+ ErlHeapFragment *bp;
+ for (bp = hfact.heap_frags; bp; bp = bp->next)
+ hsz += bp->used_size;
+ }
erts_proc_sig_send_persistent_monitor_msg(ERTS_MON_TYPE_NODES,
nmdp[i].options,
@@ -5139,6 +5373,8 @@ send_nodes_mon_msgs(Process *c_p, Eterm what, Eterm node, Eterm type, Eterm reas
nmdp[i].pid,
msg,
hsz);
+
+ erts_factory_close(&hfact);
}
if (nmdp != &def_buf[0])
diff --git a/erts/emulator/beam/erl_map.c b/erts/emulator/beam/erl_map.c
index d3e355183d..417f8b2a8b 100644
--- a/erts/emulator/beam/erl_map.c
+++ b/erts/emulator/beam/erl_map.c
@@ -121,28 +121,35 @@ void erts_init_map(void) {
*/
BIF_RETTYPE map_size_1(BIF_ALIST_1) {
- if (is_flatmap(BIF_ARG_1)) {
- flatmap_t *mp = (flatmap_t*)flatmap_val(BIF_ARG_1);
- BIF_RET(make_small(flatmap_get_size(mp)));
- } else if (is_hashmap(BIF_ARG_1)) {
- Eterm *head;
- Uint size;
+ Sint size = erts_map_size(BIF_ARG_1);
+ if (size < 0) {
+ BIF_P->fvalue = BIF_ARG_1;
+ BIF_ERROR(BIF_P, BADMAP);
+ }
- head = hashmap_val(BIF_ARG_1);
- size = head[1];
+ /*
+ * As long as a small has 28 bits (on a 32-bit machine) for
+ * the integer itself, it is impossible to build a map whose
+ * size would not fit in a small. Add an assertion in case we
+ * ever decreases the number of bits in a small.
+ */
+ ASSERT(IS_USMALL(0, size));
+ BIF_RET(make_small(size));
+}
- /*
- * As long as a small has 28 bits (on a 32-bit machine) for
- * the integer itself, it is impossible to build a map whose
- * size would not fit in a small. Add an assertion in case we
- * ever decreases the number of bits in a small.
- */
- ASSERT(IS_USMALL(0, size));
- BIF_RET(make_small(size));
+Sint
+erts_map_size(Eterm map)
+{
+ if (is_flatmap(map)) {
+ flatmap_t *mp = (flatmap_t*)flatmap_val(map);
+ return (Sint) flatmap_get_size(mp);
+ }
+ else if (is_hashmap(map)) {
+ Eterm *head = hashmap_val(map);
+ return (Sint) head[1];
}
- BIF_P->fvalue = BIF_ARG_1;
- BIF_ERROR(BIF_P, BADMAP);
+ return -1;
}
/* maps:find/2
@@ -478,38 +485,87 @@ Eterm erts_hashmap_from_array(ErtsHeapFactory* factory, Eterm *leafs, Uint n,
return res;
}
-Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks0, Eterm *vs0, Uint n)
+static ERTS_INLINE Eterm
+from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks, Eterm *vs,
+ Uint n, Eterm *key_tuple, flatmap_t **fmpp)
{
if (n <= MAP_SMALL_MAP_LIMIT) {
- Eterm *ks, *vs, *hp;
- flatmap_t *mp;
+ Eterm *hp;
+ flatmap_t *fmp;
Eterm keys;
- hp = erts_produce_heap(factory, 3 + 1 + (2 * n), 0);
- keys = make_tuple(hp);
- *hp++ = make_arityval(n);
- ks = hp;
- hp += n;
- mp = (flatmap_t*)hp;
- hp += MAP_HEADER_FLATMAP_SZ;
- vs = hp;
+ if (key_tuple && is_value(*key_tuple)) {
+ keys = *key_tuple;
+ hp = erts_produce_heap(factory, MAP_HEADER_FLATMAP_SZ + n, 0);
+ ASSERT(is_tuple_arity(keys, n));
+ ASSERT(sys_memcmp((void *) (tuple_val(keys) + 1),
+ (void *) ks,
+ n * sizeof(Eterm)) == 0);
+ }
+ else {
+ hp = erts_produce_heap(factory, 1 + MAP_HEADER_FLATMAP_SZ + 2*n, 0);
+ keys = make_tuple(hp);
+ if (key_tuple) {
+ *key_tuple = keys;
+ }
+ *hp++ = make_arityval(n);
+ sys_memcpy((void *) hp,
+ (void *) ks,
+ n * sizeof(Eterm));
+ hp += n;
+ }
- mp->thing_word = MAP_HEADER_FLATMAP;
- mp->size = n;
- mp->keys = keys;
+ fmp = (flatmap_t*)hp;
+ hp += MAP_HEADER_FLATMAP_SZ;
+
+ fmp->thing_word = MAP_HEADER_FLATMAP;
+ fmp->size = n;
+ fmp->keys = keys;
- sys_memcpy(ks, ks0, n * sizeof(Eterm));
- sys_memcpy(vs, vs0, n * sizeof(Eterm));
+ sys_memcpy((void *) hp, (void *) vs, n * sizeof(Eterm));
- if (!erts_validate_and_sort_flatmap(mp)) {
+ if (fmpp) {
+ *fmpp = fmp;
return THE_NON_VALUE;
}
-
- return make_flatmap(mp);
+ return make_flatmap(fmp);
} else {
- return erts_hashmap_from_ks_and_vs(factory, ks0, vs0, n);
+ if (fmpp) {
+ *fmpp = NULL;
+ }
+ return erts_hashmap_from_ks_and_vs(factory, ks, vs, n);
}
- return THE_NON_VALUE;
+}
+
+Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks, Eterm *vs, Uint n)
+{
+ Eterm res;
+ flatmap_t *fmp;
+
+ res = from_ks_and_vs(factory, ks, vs, n, NULL, &fmp);
+ if (fmp) {
+ if (erts_validate_and_sort_flatmap(fmp)) {
+ res = make_flatmap(fmp);
+ }
+ else {
+ res = THE_NON_VALUE;
+ }
+ }
+ return res;
+}
+
+Eterm erts_map_from_sorted_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks, Eterm *vs,
+ Uint n, Eterm *key_tuple)
+{
+#ifdef DEBUG
+ Uint i;
+ /* verify that key array contains unique and sorted keys... */
+ for (i = 1; i < n; i++) {
+ ASSERT(CMP_TERM(ks[i-1], ks[i]) < 0);
+ }
+#endif
+
+ return from_ks_and_vs(factory, ks, vs, n, key_tuple, NULL);
}
diff --git a/erts/emulator/beam/erl_map.h b/erts/emulator/beam/erl_map.h
index 718d400e22..6236ac8e2f 100644
--- a/erts/emulator/beam/erl_map.h
+++ b/erts/emulator/beam/erl_map.h
@@ -101,6 +101,8 @@ Eterm erts_hashmap_from_array(ErtsHeapFactory*, Eterm *leafs, Uint n, int rejec
erts_hashmap_from_ks_and_vs_extra((F), (KS), (VS), (N), THE_NON_VALUE, THE_NON_VALUE);
Eterm erts_map_from_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks, Eterm *vs, Uint n);
+Eterm erts_map_from_sorted_ks_and_vs(ErtsHeapFactory *factory, Eterm *ks0, Eterm *vs0,
+ Uint n, Eterm *key_tuple);
Eterm erts_hashmap_from_ks_and_vs_extra(ErtsHeapFactory *factory,
Eterm *ks, Eterm *vs, Uint n,
Eterm k, Eterm v);
@@ -109,6 +111,8 @@ const Eterm *erts_maps_get(Eterm key, Eterm map);
const Eterm *erts_hashmap_get(Uint32 hx, Eterm key, Eterm map);
+Sint erts_map_size(Eterm map);
+
/* hamt nodes v2.0
*
* node :: leaf | array | bitmap
diff --git a/erts/emulator/beam/erl_node_tables.c b/erts/emulator/beam/erl_node_tables.c
index eb2049f565..760d6d3cd7 100644
--- a/erts/emulator/beam/erl_node_tables.c
+++ b/erts/emulator/beam/erl_node_tables.c
@@ -152,9 +152,13 @@ dist_table_alloc(void *dep_tmpl)
Eterm sysname;
Binary *bin;
DistEntry *dep;
+ Uint32 init_connection_id;
erts_rwmtx_opt_t rwmtx_opt = ERTS_RWMTX_OPT_DEFAULT_INITER;
rwmtx_opt.type = ERTS_RWMTX_TYPE_FREQUENT_READ;
+ init_connection_id = (Uint32) erts_get_monotonic_time(NULL);
+ init_connection_id &= ERTS_DIST_CON_ID_MASK;
+
sysname = ((DistEntry *) dep_tmpl)->sysname;
bin = erts_create_magic_binary_x(sizeof(DistEntry),
@@ -174,7 +178,7 @@ dist_table_alloc(void *dep_tmpl)
dep->sysname = sysname;
dep->cid = NIL;
erts_atomic_init_nob(&dep->input_handler, (erts_aint_t) NIL);
- dep->connection_id = 0;
+ dep->connection_id = init_connection_id;
dep->state = ERTS_DE_STATE_IDLE;
dep->pending_nodedown = 0;
dep->suspended_nodeup = NULL;
diff --git a/erts/emulator/test/distribution_SUITE.erl b/erts/emulator/test/distribution_SUITE.erl
index d115626597..0fd8d29f4c 100644
--- a/erts/emulator/test/distribution_SUITE.erl
+++ b/erts/emulator/test/distribution_SUITE.erl
@@ -41,7 +41,7 @@
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,
+ group_leader/1, nodes2/1,
optimistic_dflags/1,
bulk_send_big/1, bulk_send_bigbig/1,
local_send_small/1, local_send_big/1,
@@ -87,7 +87,7 @@ suite() ->
all() ->
[ping, {group, bulk_send}, {group, local_send},
- group_leader,
+ group_leader, nodes2,
optimistic_dflags,
link_to_busy, exit_to_busy, lost_exit, link_to_dead,
link_to_dead_new_node,
@@ -205,6 +205,294 @@ group_leader_1(Node2) ->
?Line {ExtPid, group_leader, GL2} = receive_one(),
ok.
+nodes2(Config) when is_list(Config) ->
+
+ This = node(),
+
+ ok = net_kernel:monitor_nodes(true, #{node_type => all,
+ connection_id => true}),
+
+ AlreadyConnected = maps:from_list(lists:map(fun (N) ->
+ {N, true}
+ end, nodes(connected))),
+ AlreadyVisible = maps:from_list(lists:map(fun (N) ->
+ {N, true}
+ end, nodes(visible))),
+ AlreadyHidden = maps:from_list(lists:map(fun (N) ->
+ {N, true}
+ end, nodes(visible))),
+ AlreadyKnown = maps:from_list(lists:map(fun (N) ->
+ {N, true}
+ end, nodes(known))),
+
+ {ok, V1} = start_node(visible1),
+ {ok, H1} = start_node(hidden1, "-hidden"),
+ {ok, V2} = start_node(visible2),
+ {ok, H2} = start_node(hidden2, "-hidden"),
+
+ TestNodes = maps:from_list(lists:map(fun (N) ->
+ {N, true}
+ end, [This, V1, H1, V2, H2])),
+
+ V1CId = receive {nodeup, V1, #{connection_id := C1, node_type := visible}} -> C1 end,
+ V2CId = receive {nodeup, V2, #{connection_id := C2, node_type := visible}} -> C2 end,
+ H1CId = receive {nodeup, H1, #{connection_id := C3, node_type := hidden}} -> C3 end,
+ H2CId = receive {nodeup, H2, #{connection_id := C4, node_type := hidden}} -> C4 end,
+
+ lists:foreach(fun ({N, I}) when N == V1 ->
+ 2 = maps:size(I),
+ #{connection_id := V1CId, node_type := visible} = I;
+ ({N, I}) when N == V2 ->
+ 2 = maps:size(I),
+ #{connection_id := V2CId, node_type := visible} = I;
+ ({N, I}) when N == H1 ->
+ 2 = maps:size(I),
+ #{connection_id := H1CId, node_type := hidden} = I;
+ ({N, I}) when N == H2 ->
+ 2 = maps:size(I),
+ #{connection_id := H2CId, node_type := hidden} = I;
+ ({N, I}) ->
+ 2 = maps:size(I),
+ #{connection_id := _, node_type := _} = I,
+ false = maps:is_key(N, TestNodes),
+ true = maps:is_key(N, AlreadyConnected)
+ end, erlang:nodes(connected, #{connection_id => true,
+ node_type => true})),
+ lists:foreach(fun ({N, I}) when N == V1 ->
+ 2 = maps:size(I),
+ #{connection_id := V1CId, node_type := visible} = I;
+ ({N, I}) when N == V2 ->
+ 2 = maps:size(I),
+ #{connection_id := V2CId, node_type := visible} = I;
+ ({N, I}) when N == H1 ->
+ 2 = maps:size(I),
+ #{connection_id := H1CId, node_type := hidden} = I;
+ ({N, I}) when N == H2 ->
+ 2 = maps:size(I),
+ #{connection_id := H2CId, node_type := hidden} = I;
+ ({N, I}) when N == This ->
+ 2 = maps:size(I),
+ #{connection_id := undefined, node_type := this} = I;
+ ({N, I}) ->
+ 2 = maps:size(I),
+ #{connection_id := _, node_type := _} = I,
+ false = maps:is_key(N, TestNodes),
+ true = maps:is_key(N, AlreadyConnected)
+ end, erlang:nodes([this, connected], #{connection_id => true,
+ node_type => true})),
+ lists:foreach(fun ({N, I}) when N == V1 ->
+ 1 = maps:size(I),
+ #{connection_id := V1CId} = I;
+ ({N, I}) when N == V2 ->
+ 1 = maps:size(I),
+ #{connection_id := V2CId} = I;
+ ({N, I}) when N == H1 ->
+ 1 = maps:size(I),
+ #{connection_id := H1CId} = I;
+ ({N, I}) when N == H2 ->
+ 1 = maps:size(I),
+ #{connection_id := H2CId} = I;
+ ({N, I}) ->
+ 1 = maps:size(I),
+ #{connection_id := _} = I,
+ false = maps:is_key(N, TestNodes),
+ true = maps:is_key(N, AlreadyConnected)
+ end, erlang:nodes(connected, #{connection_id => true})),
+ lists:foreach(fun ({N, I}) when N == V1 ->
+ 1 = maps:size(I),
+ #{node_type := visible} = I;
+ ({N, I}) when N == V2 ->
+ 1 = maps:size(I),
+ #{node_type := visible} = I;
+ ({N, I}) when N == H1 ->
+ 1 = maps:size(I),
+ #{node_type := hidden} = I;
+ ({N, I}) when N == H2 ->
+ 1 = maps:size(I),
+ #{node_type := hidden} = I;
+ ({N, I}) ->
+ 1 = maps:size(I),
+ #{node_type := _} = I,
+ false = maps:is_key(N, TestNodes),
+ true = maps:is_key(N, AlreadyConnected)
+ end, erlang:nodes(connected, #{node_type => true})),
+ lists:foreach(fun ({N, I}) when N == V1 ->
+ 2 = maps:size(I),
+ #{connection_id := V1CId, node_type := visible} = I;
+ ({N, I}) when N == V2 ->
+ 2 = maps:size(I),
+ #{connection_id := V2CId, node_type := visible} = I;
+ ({N, I}) ->
+ 2 = maps:size(I),
+ #{connection_id := _, node_type := _} = I,
+ false = maps:is_key(N, TestNodes),
+ true = maps:is_key(N, AlreadyVisible)
+ end, erlang:nodes(visible, #{connection_id => true,
+ node_type => true})),
+ lists:foreach(fun ({N, I}) when N == V1 ->
+ 2 = maps:size(I),
+ #{connection_id := V1CId, node_type := visible} = I;
+ ({N, I}) when N == V2 ->
+ 2 = maps:size(I),
+ #{connection_id := V2CId, node_type := visible} = I;
+ ({N, I}) when N == This ->
+ 2 = maps:size(I),
+ #{connection_id := undefined, node_type := this} = I;
+ ({N, I}) ->
+ 2 = maps:size(I),
+ #{connection_id := _, node_type := _} = I,
+ false = maps:is_key(N, TestNodes),
+ true = maps:is_key(N, AlreadyVisible)
+ end, erlang:nodes([this, visible], #{connection_id => true,
+ node_type => true})),
+ lists:foreach(fun ({N, I}) when N == H1 ->
+ 2 = maps:size(I),
+ #{connection_id := H1CId, node_type := hidden} = I;
+ ({N, I}) when N == H2 ->
+ 2 = maps:size(I),
+ #{connection_id := H2CId, node_type := hidden} = I;
+ ({N, I}) ->
+ 2 = maps:size(I),
+ #{connection_id := _, node_type := _} = I,
+ false = maps:is_key(N, TestNodes),
+ true = maps:is_key(N, AlreadyHidden)
+ end, erlang:nodes(hidden, #{connection_id => true,
+ node_type => true})),
+ [{This, #{connection_id := undefined,
+ node_type := this}}] = erlang:nodes(this, #{connection_id => true,
+ node_type => true}),
+ [{This, #{connection_id := undefined}}] = erlang:nodes(this, #{connection_id => true}),
+ [{This, #{node_type := this}}] = erlang:nodes(this, #{node_type => true}),
+
+ %% Ensure dist these dist entries are not GC:d yet...
+ NKV2 = rpc:call(V2, erlang, whereis, [net_kernel]),
+ true = is_pid(NKV2),
+ NKH2 = rpc:call(H2, erlang, whereis, [net_kernel]),
+ true = is_pid(NKH2),
+
+ stop_node(V2),
+ stop_node(H2),
+
+ receive {nodedown, V2, #{connection_id := V2CId, node_type := visible}} -> ok end,
+ receive {nodedown, H2, #{connection_id := H2CId, node_type := hidden}} -> ok end,
+
+ lists:foreach(fun ({N, I}) when N == V1 ->
+ 2 = maps:size(I),
+ #{connection_id := V1CId, node_type := visible} = I;
+ ({N, I}) when N == V2 ->
+ 2 = maps:size(I),
+ #{connection_id := undefined, node_type := known} = I;
+ ({N, I}) when N == H1 ->
+ 2 = maps:size(I),
+ #{connection_id := H1CId, node_type := hidden} = I;
+ ({N, I}) when N == H2 ->
+ 2 = maps:size(I),
+ #{connection_id := undefined, node_type := known} = I;
+ ({N, I}) when N == This ->
+ 2 = maps:size(I),
+ #{connection_id := undefined, node_type := this} = I;
+ ({N, I}) ->
+ 2 = maps:size(I),
+ #{connection_id := _, node_type := _} = I,
+ false = maps:is_key(N, TestNodes),
+ true = maps:is_key(N, AlreadyKnown)
+ end, erlang:nodes(known, #{connection_id => true,
+ node_type => true})),
+ lists:foreach(fun ({N, I}) when N == V1 ->
+ 1 = maps:size(I),
+ #{node_type := visible} = I;
+ ({N, I}) when N == V2 ->
+ 1 = maps:size(I),
+ #{node_type := known} = I;
+ ({N, I}) when N == H1 ->
+ 1 = maps:size(I),
+ #{node_type := hidden} = I;
+ ({N, I}) when N == H2 ->
+ 1 = maps:size(I),
+ #{node_type := known} = I;
+ ({N, I}) when N == This ->
+ 1 = maps:size(I),
+ #{node_type := this} = I;
+ ({N, I}) ->
+ 1 = maps:size(I),
+ #{node_type := _} = I,
+ false = maps:is_key(N, TestNodes),
+ true = maps:is_key(N, AlreadyKnown)
+ end, erlang:nodes(known, #{node_type => true})),
+ lists:foreach(fun ({N, I}) when N == V1 ->
+ 1 = maps:size(I),
+ #{connection_id := V1CId} = I;
+ ({N, I}) when N == V2 ->
+ 1 = maps:size(I),
+ #{connection_id := undefined} = I;
+ ({N, I}) when N == H1 ->
+ 1 = maps:size(I),
+ #{connection_id := H1CId} = I;
+ ({N, I}) when N == H2 ->
+ 1 = maps:size(I),
+ #{connection_id := undefined} = I;
+ ({N, I}) when N == This ->
+ 1 = maps:size(I),
+ #{connection_id := undefined} = I;
+ ({N, I}) ->
+ 1 = maps:size(I),
+ #{connection_id := _} = I,
+ false = maps:is_key(N, TestNodes),
+ true = maps:is_key(N, AlreadyKnown)
+ end, erlang:nodes(known, #{connection_id => true})),
+ lists:foreach(fun ({N, I}) when N == V1 ->
+ 0 = maps:size(I),
+ #{} = I;
+ ({N, I}) when N == V2 ->
+ 0 = maps:size(I),
+ #{} = I;
+ ({N, I}) when N == H1 ->
+ 0 = maps:size(I),
+ #{} = I;
+ ({N, I}) when N == H2 ->
+ 0 = maps:size(I),
+ #{} = I;
+ ({N, I}) when N == This ->
+ 0 = maps:size(I),
+ #{} = I;
+ ({N, I}) ->
+ 0 = maps:size(I),
+ false = maps:is_key(N, TestNodes),
+ true = maps:is_key(N, AlreadyKnown)
+ end, erlang:nodes(known, #{})),
+
+ stop_node(V1),
+ stop_node(H1),
+
+ id(NKV2),
+ id(NKH2),
+
+ try erlang:nodes("visible", #{connection_id => true})
+ catch error:badarg -> ok
+ end,
+ try erlang:nodes([another], #{connection_id => true})
+ catch error:badarg -> ok
+ end,
+ try erlang:nodes(visible, #{cid => true})
+ catch error:badarg -> ok
+ end,
+ try erlang:nodes(visible, #{connection_id => yes})
+ catch error:badarg -> ok
+ end,
+ try erlang:nodes(visible, #{node_type => yes})
+ catch error:badarg -> ok
+ end,
+ try erlang:nodes(visible, [{connection_id, true}])
+ catch error:badarg -> ok
+ end,
+ try erlang:nodes(visible, [{node_type, true}])
+ catch error:badarg -> ok
+ end,
+ ok.
+
+id(X) ->
+ X.
+
%% Test optimistic distribution flags toward pending connections (DFLAG_DIST_HOPEFULLY)
optimistic_dflags(Config) when is_list(Config) ->
?Line Sender = start_relay_node(optimistic_dflags_sender, []),
diff --git a/erts/preloaded/ebin/erlang.beam b/erts/preloaded/ebin/erlang.beam
index 050db2b8b2..9d4bc2a6c6 100644
--- a/erts/preloaded/ebin/erlang.beam
+++ b/erts/preloaded/ebin/erlang.beam
Binary files differ
diff --git a/erts/preloaded/src/erlang.erl b/erts/preloaded/src/erlang.erl
index f78a79203c..5020cc6ff8 100644
--- a/erts/preloaded/src/erlang.erl
+++ b/erts/preloaded/src/erlang.erl
@@ -34,7 +34,7 @@
-export([dmonitor_node/3]).
-export([delay_trap/2]).
-export([set_cookie/2, get_cookie/0]).
--export([nodes/0]).
+-export([nodes/0, nodes/1, nodes/2]).
-export([integer_to_list/2]).
-export([integer_to_binary/2]).
@@ -175,7 +175,7 @@
is_list/1, is_map/1, is_number/1, is_pid/1, is_port/1, is_record/2,
is_record/3, is_reference/1, is_tuple/1, load_module/2,
load_nif/2, localtime_to_universaltime/2, make_fun/3,
- make_tuple/2, make_tuple/3, nodes/1, open_port/2,
+ make_tuple/2, make_tuple/3, open_port/2,
port_call/2, port_call/3, port_info/1, port_info/2, process_flag/2,
process_info/2, send/2, send/3, seq_trace_info/1,
setelement/3, spawn_opt/1,
@@ -2176,13 +2176,6 @@ make_tuple(_Arity,_InitialValue) ->
make_tuple(_Arity,_DefaultValue,_InitList) ->
erlang:nif_error(undefined).
--spec nodes(Arg) -> Nodes when
- Arg :: NodeType | [NodeType],
- NodeType :: visible | hidden | connected | this | known,
- Nodes :: [node()].
-nodes(_Arg) ->
- erlang:nif_error(undefined).
-
-spec open_port(PortName, PortSettings) -> port() when
PortName :: {spawn, Command :: string() | binary()} |
{spawn_driver, Command :: string() | binary()} |
@@ -3065,7 +3058,28 @@ yield() ->
-spec nodes() -> Nodes when
Nodes :: [node()].
nodes() ->
- erlang:nodes(visible).
+ erlang:nif_error(undefined).
+
+-spec nodes(Arg) -> Nodes when
+ Arg :: NodeType | [NodeType],
+ NodeType :: visible | hidden | connected | this | known,
+ Nodes :: [node()].
+nodes(_Arg) ->
+ erlang:nif_error(undefined).
+
+-spec nodes(Arg, InfoOpts) -> [NodeInfo] when
+ NodeType :: visible | hidden | connected | this | known,
+ Arg :: NodeType | [NodeType],
+ InfoOpts :: #{connection_id => boolean(),
+ node_type => boolean()},
+ NodeTypeInfo :: visible | hidden | this | known,
+ ConnectionId :: undefined | integer(),
+ Info :: #{connection_id => ConnectionId,
+ node_type => NodeTypeInfo},
+ NodeInfo :: {node(), Info}.
+
+nodes(_Args, _Opts) ->
+ erlang:nif_error(undefined).
-spec disconnect_node(Node) -> boolean() | ignored when
Node :: node().
diff --git a/erts/preloaded/src/erts.app.src b/erts/preloaded/src/erts.app.src
index 4f11613d0e..6cb889573a 100644
--- a/erts/preloaded/src/erts.app.src
+++ b/erts/preloaded/src/erts.app.src
@@ -42,7 +42,7 @@
{registered, []},
{applications, []},
{env, []},
- {runtime_dependencies, ["stdlib-3.5", "kernel-@OTP-17843@", "sasl-3.3"]}
+ {runtime_dependencies, ["stdlib-@OTP-17934@", "kernel-@OTP-17843:OTP-17934@", "sasl-3.3"]}
]}.
%% vim: ft=erlang
diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml
index 419d3cad84..096af5cb55 100644
--- a/lib/kernel/doc/src/net_kernel.xml
+++ b/lib/kernel/doc/src/net_kernel.xml
@@ -161,82 +161,121 @@ $ <input>erl -sname foobar</input></pre>
are stopped. Two
option lists are considered the same if they contain the same
set of options.</p>
- <p>As from Kernel version 2.11.4, and ERTS version
- 5.5.4, the following is guaranteed:</p>
- <list type="bulleted">
- <item><p><c>nodeup</c> messages are delivered before delivery
- of any message from the remote node passed through the
- newly established connection.</p></item>
- <item><p><c>nodedown</c> messages are not delivered until all
- messages from the remote node that have been passed
- through the connection have been delivered.</p></item>
- </list>
- <p>Notice that this is <em>not</em> guaranteed for Kernel
- versions before 2.11.4.</p>
- <p>As from Kernel version 2.11.4, subscriptions can also be
- made before the <c>net_kernel</c> server is started, that is,
- <c>net_kernel:monitor_nodes/[1,2]</c> does not return
- <c>ignored</c>.</p>
- <p>As from Kernel version 2.13, and ERTS version
- 5.7, the following is guaranteed:</p>
- <list type="bulleted">
- <item><p><c>nodeup</c> messages are delivered after the
+ <p>Delivery guarantees of <c>nodeup</c>/<c>nodedown</c> messages:</p>
+ <list>
+ <item><p>
+ <c>nodeup</c> messages are delivered before delivery
+ of any signals from the remote node through the newly
+ established connection.
+ </p></item>
+ <item>
+ <p><c>nodedown</c> messages are delivered after all
+ the signals from the remote node over the connection
+ have been delivered.
+ </p></item>
+ <item><p>
+ <c>nodeup</c> messages are delivered after the
corresponding node appears in results from
- <c>erlang:nodes/X</c>.</p></item>
- <item><p><c>nodedown</c> messages are delivered after the
+ <c>erlang:nodes()</c>.
+ </p></item>
+ <item>
+ <p><c>nodedown</c> messages are delivered after the
corresponding node has disappeared in results from
- <c>erlang:nodes/X</c>.</p></item>
+ <c>erlang:nodes()</c>.
+ </p></item>
+ <item><p>
+ As of OTP @OTP-16362@, a <c>nodedown</c> message for a
+ connection being taken down will be delivered before a
+ <c>nodeup</c> message due to a new connection to the
+ same node. Prior to OTP @OTP-16362@, this was not
+ guaranteed to be the case.
+ </p></item>
</list>
- <p>Notice that this is <em>not</em> guaranteed for Kernel
- versions before 2.13.</p>
<p>The format of the node status change messages depends on
<c><anno>Options</anno></c>. If <c><anno>Options</anno></c> is
- <c>[]</c>, which is the default, the format is as follows:</p>
- <code type="none">
+ the empty list or if <c>net_kernel:monitor_nodes/1</c> is called,
+ the format is as follows:</p>
+ <code type="erl">
{nodeup, Node} | {nodedown, Node}
Node = node()</code>
- <p>If <c><anno>Options</anno></c> is not <c>[]</c>, the format is
- as follows:</p>
- <code type="none">
-{nodeup, Node, InfoList} | {nodedown, Node, InfoList}
+ <p>
+ When <c><anno>Options</anno></c> is the empty map or empty
+ list, the caller will only subscribe for status change messages
+ for visible nodes. That is, only nodes that appear in the
+ result of
+ <seealso marker="erts:erlang#nodes/0"><c>erlang:nodes/0</c></seealso>.
+ </p>
+ <p>
+ If <c><anno>Options</anno></c> equals anything other than the
+ empty list, the format of the status change messages is as follows:
+ </p>
+ <code type="erl">
+{nodeup, Node, Info} | {nodedown, Node, Info}
Node = node()
- InfoList = [{Tag, Val}]</code>
- <p><c>InfoList</c> is a list of tuples. Its contents depends on
- <c><anno>Options</anno></c>, see below.</p>
- <p>Also, when <c>OptionList == []</c>, only visible nodes, that
- is, nodes that appear in the result of
- <seealso marker="erts:erlang#nodes/0"><c>erlang:nodes/0</c></seealso>,
- are monitored.</p>
- <p><c><anno>Option</anno></c> can be any of the following:</p>
+ Info = #{Tag => Val} | [{Tag, Val}]</code>
+ <p>
+ <c>Info</c> is either a map or a list of 2-tuples. Its content
+ depends on <c><anno>Options</anno></c>. If <c><anno>Options</anno></c>
+ is a map, <c>Info</c> will also be a map. If <c><anno>Options</anno></c>
+ is a list, <c>Info</c> will also be a list.
+ </p>
+ <p>
+ When <c><anno>Options</anno></c> is a map, currently
+ the following associations are allowed:
+ </p>
<taglist>
- <tag><c>{node_type, NodeType}</c></tag>
+ <tag><c>connection_id => boolean()</c></tag>
+ <item>
+ <p>
+ If the value of the association equals <c>true</c>, a
+ <c>connection_id => ConnectionId</c> association will be
+ included in the <c>Info</c> map where <c>ConnectionId</c>
+ is the connection identifier of the connection coming up
+ or going down. For more info about this connection
+ identifier see the documentation of
+ <seealso marker="erts:erlang#connection_id">erlang:nodes/2</seealso>.
+ </p>
+ </item>
+ <tag><c>node_type => <anno>NodeType</anno></c></tag>
<item>
<p>Valid values for <c>NodeType</c>:</p>
<taglist>
<tag><c>visible</c></tag>
<item><p>Subscribe to node status change messages for visible
- nodes only. The tuple <c>{node_type, visible}</c> is
- included in <c>InfoList</c>.</p></item>
+ nodes only. The association <c>node_type => visible</c> will
+ be included in the <c>Info</c> map.</p></item>
<tag><c>hidden</c></tag>
<item><p>Subscribe to node status change messages for hidden
- nodes only. The tuple <c>{node_type, hidden}</c> is
- included in <c>InfoList</c>.</p></item>
+ nodes only. The association <c>node_type => hidden</c> will
+ be included in the <c>Info</c> map.</p></item>
<tag><c>all</c></tag>
<item><p>Subscribe to node status change messages for both
- visible and hidden nodes. The tuple
- <c>{node_type, visible | hidden}</c> is included in
- <c>InfoList</c>.</p></item>
+ visible and hidden nodes. The association
+ <c>node_type => visible | hidden</c> will be included in
+ the <c>Info</c> map.</p></item>
</taglist>
+ <p>
+ If no <c>node_type => <anno>NodeType</anno></c> association
+ is included in the <c><anno>Options</anno></c> map, the
+ caller will subscribe for status change messages for visible
+ nodes only, but <i>no</i> <c>node_type => visible</c>
+ association will be included in the <c>Info</c> map.
+ </p>
</item>
- <tag><c>nodedown_reason</c></tag>
+ <tag><c>nodedown_reason => boolean()</c></tag>
<item>
- <p>The tuple <c>{nodedown_reason, Reason}</c> is included in
- <c>InfoList</c> in <c>nodedown</c> messages.</p>
+ <p>
+ If the value of the association equals <c>true</c>, a
+ <c>nodedown_reason => Reason</c> association will be
+ included in the <c>Info</c> map for <c>nodedown</c>
+ messages.
+ </p>
+ <marker id="nodedown_reasons"/>
<p>
<c>Reason</c> can, depending on which
- distribution module or process that is used be any term,
+ distribution module or process that is used, be any term,
but for the standard TCP distribution module it is
- any of the following:
+ one of the following:
</p>
<taglist>
<tag><c>connection_setup_failed</c></tag>
@@ -263,6 +302,82 @@ $ <input>erl -sname foobar</input></pre>
</taglist>
</item>
</taglist>
+ <p>
+ When <c><anno>Options</anno></c> is a list, currently
+ <c><anno>ListOption</anno></c> can be one of the following:
+ </p>
+ <taglist>
+ <tag><c>connection_id</c></tag>
+ <item>
+ <p>
+ A <c>{connection_id, ConnectionId}</c> tuple will be
+ included in <c>Info</c> where <c>ConnectionId</c> is the
+ connection identifier of the connection coming up or
+ going down. For more info about this connection identifier
+ see the documentation of
+ <seealso marker="erts:erlang#connection_id">erlang:nodes/2</seealso>.
+ </p>
+ </item>
+ <tag><c>{node_type, <anno>NodeType</anno>}</c></tag>
+ <item>
+ <p>Valid values for <c><anno>NodeType</anno></c>:</p>
+ <taglist>
+ <tag><c>visible</c></tag>
+ <item><p>Subscribe to node status change messages for visible
+ nodes only. The tuple <c>{node_type, visible}</c> will be
+ included in the <c>Info</c> list.</p></item>
+ <tag><c>hidden</c></tag>
+ <item><p>Subscribe to node status change messages for hidden
+ nodes only. The tuple <c>{node_type, hidden}</c> will be
+ included in the <c>Info</c> list.</p></item>
+ <tag><c>all</c></tag>
+ <item><p>Subscribe to node status change messages for both
+ visible and hidden nodes. The tuple
+ <c>{node_type, visible | hidden}</c> will be included in
+ the <c>Info</c> list.</p></item>
+ </taglist>
+ <p>
+ If no <c>{node_type, <anno>NodeType</anno>}</c> option
+ has been given. The caller will subscribe for status
+ change messages for visible nodes only, but <i>no</i>
+ <c>{node_type, visible}</c> tuple will be included in the
+ <c>Info</c> list.
+ </p>
+ </item>
+ <tag><c>nodedown_reason</c></tag>
+ <item>
+ <p>
+ The tuple <c>{nodedown_reason, Reason}</c> will be included
+ in the <c>Info</c> list for <c>nodedown</c> messages.
+ </p>
+ <p>
+ See the documentation of the
+ <seealso marker="#nodedown_reasons"><c>nodedown_reason
+ => boolean()</c></seealso> association above for information
+ about possible <c>Reason</c> values.
+ </p>
+ </item>
+ </taglist>
+ <p>Example:</p>
+ <code type="erl">
+(a@localhost)1> net_kernel:monitor_nodes(true, #{connection_id=>true, node_type=>all, nodedown_reason=>true}).
+ok
+(a@localhost)2> flush().
+Shell got {nodeup,b@localhost,
+ #{connection_id => 3067552,node_type => visible}}
+Shell got {nodeup,c@localhost,
+ #{connection_id => 13892107,node_type => hidden}}
+Shell got {nodedown,b@localhost,
+ #{connection_id => 3067552,node_type => visible,
+ nodedown_reason => connection_closed}}
+Shell got {nodedown,c@localhost,
+ #{connection_id => 13892107,node_type => hidden,
+ nodedown_reason => net_tick_timeout}}
+Shell got {nodeup,b@localhost,
+ #{connection_id => 3067553,node_type => visible}}
+ok
+(a@localhost)3>
+ </code>
</desc>
</func>
diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src
index 234d71f745..1e49d081fc 100644
--- a/lib/kernel/src/kernel.app.src
+++ b/lib/kernel/src/kernel.app.src
@@ -150,6 +150,6 @@
{prevent_overlapping_partitions, false}
]},
{mod, {kernel, []}},
- {runtime_dependencies, ["erts-@OTP-17843@", "stdlib-3.5", "sasl-3.0"]}
+ {runtime_dependencies, ["erts-@OTP-17843:OTP-17934@", "stdlib-3.5", "sasl-3.0"]}
]
}.
diff --git a/lib/kernel/src/net_kernel.erl b/lib/kernel/src/net_kernel.erl
index b857000a80..e4e2ca6e49 100644
--- a/lib/kernel/src/net_kernel.erl
+++ b/lib/kernel/src/net_kernel.erl
@@ -256,15 +256,41 @@ monitor_nodes(Flag) ->
-spec monitor_nodes(Flag, Options) -> ok | Error when
Flag :: boolean(),
- Options :: [Option],
- Option :: {node_type, NodeType}
- | nodedown_reason,
+ Options :: OptionsList | OptionsMap,
+ OptionsList :: [ListOption],
+ ListOption :: connection_id
+ | {node_type, NodeType}
+ | nodedown_reason,
+ OptionsMap :: #{connection_id => boolean(),
+ node_type => NodeType,
+ nodedown_reason => boolean()},
NodeType :: visible | hidden | all,
Error :: error | {error, term()}.
monitor_nodes(Flag, Opts) ->
- case catch process_flag({monitor_nodes, Opts}, Flag) of
- N when is_integer(N) -> ok;
- _ -> mk_monitor_nodes_error(Flag, Opts)
+ try
+ MapOpts = if is_map(Opts) ->
+ error = maps:find(list, Opts),
+ Opts;
+ is_list(Opts) ->
+ lists:foldl(fun (nodedown_reason, Acc) ->
+ Acc#{nodedown_reason => true};
+ (connection_id, Acc) ->
+ Acc#{connection_id => true};
+ ({node_type, Val}, Acc) ->
+ case maps:find(node_type, Acc) of
+ error -> ok;
+ {ok, Val} -> ok
+ end,
+ Acc#{node_type => Val}
+ end,
+ #{list => true},
+ Opts)
+ end,
+ true = is_integer(process_flag({monitor_nodes, MapOpts}, Flag)),
+ ok
+ catch
+ _:_ ->
+ mk_monitor_nodes_error(Flag, Opts)
end.
%% ...
@@ -1194,8 +1220,45 @@ check_options(Opts) when is_list(Opts) ->
_ ->
{error, {unknown_options, RestOpts2}}
end;
+check_options(Opts) when is_map(Opts) ->
+ BadMap0 = case maps:find(connection_id, Opts) of
+ error ->
+ Opts;
+ {ok, CIdBool} when is_boolean(CIdBool) ->
+ maps:remove(connection_id, Opts);
+ {ok, BadCIdVal} ->
+ throw({error,
+ {bad_option_value,
+ #{connection_id => BadCIdVal}}})
+ end,
+ BadMap1 = case maps:find(nodedown_reason, BadMap0) of
+ error ->
+ BadMap0;
+ {ok, NRBool} when is_boolean(NRBool) ->
+ maps:remove(nodedown_reason, BadMap0);
+ {ok, BadNRVal} ->
+ throw({error,
+ {bad_option_value,
+ #{nodedown_reason => BadNRVal}}})
+ end,
+ BadMap2 = case maps:find(node_type, BadMap1) of
+ error ->
+ BadMap1;
+ {ok, NTVal} when NTVal == visible; NTVal == hidden; NTVal == all ->
+ maps:remove(node_type, BadMap1);
+ {ok, BadNTVal} ->
+ throw({error,
+ {bad_option_value,
+ #{node_type => BadNTVal}}})
+ end,
+ if map_size(BadMap2) == 0 ->
+ {error, internal_error};
+ true ->
+ throw({error, {unknown_options, BadMap2}})
+ end;
check_options(Opts) ->
- {error, {options_not_a_list, Opts}}.
+ {error, {invalid_options, Opts}}.
+
mk_monitor_nodes_error(Flag, _Opts) when Flag =/= true, Flag =/= false ->
error;
diff --git a/lib/kernel/test/erl_distribution_SUITE.erl b/lib/kernel/test/erl_distribution_SUITE.erl
index e4fe27c619..7d7108e44b 100644
--- a/lib/kernel/test/erl_distribution_SUITE.erl
+++ b/lib/kernel/test/erl_distribution_SUITE.erl
@@ -1108,7 +1108,9 @@ monitor_nodes_misc(DCfg, _Config) ->
MonNodeState = monitor_node_state(),
ok = net_kernel:monitor_nodes(true),
ok = net_kernel:monitor_nodes(true, [{node_type, all}, nodedown_reason]),
- ok = net_kernel:monitor_nodes(true, [nodedown_reason, {node_type, all}]),
+ ok = net_kernel:monitor_nodes(true, [nodedown_reason, {node_type, all}, connection_id]),
+ ok = net_kernel:monitor_nodes(true, #{node_type => all, nodedown_reason => true}),
+ ok = net_kernel:monitor_nodes(true, #{node_type => all, nodedown_reason => true, connection_id => true}),
Names = get_numbered_nodenames(3, node),
[NN1, NN2, NN3] = Names,
@@ -1117,27 +1119,90 @@ monitor_nodes_misc(DCfg, _Config) ->
receive {nodeup, N1} -> ok end,
+ receive {nodeup, N1, #{node_type := visible}} -> ok end,
+ receive {nodeup, N2, #{node_type := hidden}} -> ok end,
receive {nodeup, N1, [{node_type, visible}]} -> ok end,
- receive {nodeup, N1, [{node_type, visible}]} -> ok end,
- receive {nodeup, N2, [{node_type, hidden}]} -> ok end,
receive {nodeup, N2, [{node_type, hidden}]} -> ok end,
+ NodesInfo = erlang:nodes(connected, #{connection_id => true}),
+
+ {N1, #{connection_id := N1CId}} = lists:keyfind(N1, 1, NodesInfo),
+ {N2, #{connection_id := N2CId}} = lists:keyfind(N2, 1, NodesInfo),
+
+ ct:pal("N1: ~p ~p~n", [N1, N1CId]),
+ ct:pal("N2: ~p ~p~n", [N2, N2CId]),
+
+ receive {nodeup, N1, #{node_type := visible, connection_id := N1CId}} -> ok end,
+ receive {nodeup, N2, #{node_type := hidden, connection_id := N2CId}} -> ok end,
+
+ N1UpInfoSorted = lists:sort([{node_type, visible},{connection_id, N1CId}]),
+ N2UpInfoSorted = lists:sort([{node_type, hidden},{connection_id, N2CId}]),
+
+ receive {nodeup, N1, UpN1Info} -> N1UpInfoSorted = lists:sort(UpN1Info) end,
+ receive {nodeup, N2, UpN2Info} -> N2UpInfoSorted = lists:sort(UpN2Info) end,
+
stop_node(N1),
stop_node(N2),
- VisbleDownInfo = lists:sort([{node_type, visible},
- {nodedown_reason, connection_closed}]),
- HiddenDownInfo = lists:sort([{node_type, hidden},
- {nodedown_reason, connection_closed}]),
-
receive {nodedown, N1} -> ok end,
- receive {nodedown, N1, Info1A} -> VisbleDownInfo = lists:sort(Info1A) end,
- receive {nodedown, N1, Info1B} -> VisbleDownInfo = lists:sort(Info1B) end,
- receive {nodedown, N2, Info2A} -> HiddenDownInfo = lists:sort(Info2A) end,
- receive {nodedown, N2, Info2B} -> HiddenDownInfo = lists:sort(Info2B) end,
+ receive {nodedown, N1, #{node_type := visible,
+ nodedown_reason := connection_closed}} -> ok end,
+ receive {nodedown, N1, #{node_type := visible,
+ nodedown_reason := connection_closed,
+ connection_id := N1CId}} -> ok end,
+ receive {nodedown, N2, #{node_type := hidden,
+ nodedown_reason := connection_closed}} -> ok end,
+ receive {nodedown, N2, #{node_type := hidden,
+ nodedown_reason := connection_closed,
+ connection_id := N2CId}} -> ok end,
+
+ N1ADownInfoSorted = lists:sort([{node_type, visible},
+ {nodedown_reason, connection_closed}]),
+ N1BDownInfoSorted = lists:sort([{node_type, visible},
+ {nodedown_reason, connection_closed},
+ {connection_id, N1CId}]),
+ N2ADownInfoSorted = lists:sort([{node_type, hidden},
+ {nodedown_reason, connection_closed}]),
+ N2BDownInfoSorted = lists:sort([{node_type, hidden},
+ {nodedown_reason, connection_closed},
+ {connection_id, N2CId}]),
+
+ receive
+ {nodedown, N1, N1Info1} ->
+ case lists:sort(N1Info1) of
+ N1ADownInfoSorted ->
+ receive
+ {nodedown, N1, N1Info2} ->
+ N1BDownInfoSorted = lists:sort(N1Info2)
+ end;
+ N1BDownInfoSorted ->
+ receive
+ {nodedown, N1, N1Info2} ->
+ N1ADownInfoSorted = lists:sort(N1Info2)
+ end
+ end
+ end,
+ receive
+ {nodedown, N2, N2Info1} ->
+ case lists:sort(N2Info1) of
+ N2ADownInfoSorted ->
+ receive
+ {nodedown, N2, N2Info2} ->
+ N2BDownInfoSorted = lists:sort(N2Info2)
+ end;
+ N2BDownInfoSorted ->
+ receive
+ {nodedown, N2, N2Info2} ->
+ N2ADownInfoSorted = lists:sort(N2Info2)
+ end
+ end
+ end,
ok = net_kernel:monitor_nodes(false, [{node_type, all}, nodedown_reason]),
+ ok = net_kernel:monitor_nodes(false, [nodedown_reason, {node_type, all}, connection_id]),
+ ok = net_kernel:monitor_nodes(false, #{node_type => all, nodedown_reason => true}),
+ ok = net_kernel:monitor_nodes(false, #{node_type => all, nodedown_reason => true, connection_id => true}),
{ok, N3} = start_node(DCfg, NN3),
receive {nodeup, N3} -> ok end,
@@ -1273,7 +1338,11 @@ monitor_nodes_errors(Config) when is_list(Config) ->
[gurka]}} = net_kernel:monitor_nodes(true,
[gurka]),
{error,
- {options_not_a_list,
+ {unknown_options,
+ #{gurka := true}}} = net_kernel:monitor_nodes(true,
+ #{gurka => true}),
+ {error,
+ {invalid_options,
gurka}} = net_kernel:monitor_nodes(true,
gurka),
{error,
@@ -1295,6 +1364,10 @@ monitor_nodes_errors(Config) when is_list(Config) ->
{node_type,
blaha}}}
= net_kernel:monitor_nodes(true, [{node_type, blaha}]),
+ {error,
+ {bad_option_value,
+ #{node_type := blaha}}}
+ = net_kernel:monitor_nodes(true, #{node_type => blaha}),
MonNodeState = monitor_node_state(),
ok.
diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl
index 939abaff00..bf170fd7b4 100644
--- a/lib/stdlib/src/erl_internal.erl
+++ b/lib/stdlib/src/erl_internal.erl
@@ -351,6 +351,7 @@ bif(node, 0) -> true;
bif(node, 1) -> true;
bif(nodes, 0) -> true;
bif(nodes, 1) -> true;
+bif(nodes, 2) -> true;
bif(now, 0) -> true;
bif(open_port, 2) -> true;
bif(pid_to_list, 1) -> true;
diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src
index f1a1afaf72..a48dbf0fc5 100644
--- a/lib/stdlib/src/stdlib.app.src
+++ b/lib/stdlib/src/stdlib.app.src
@@ -108,6 +108,6 @@
dets]},
{applications, [kernel]},
{env, []},
- {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-10.7.1","crypto-3.3",
+ {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-@OTP-17934@","crypto-3.3",
"compiler-5.0"]}
]}.