aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/test/node_container_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'erts/emulator/test/node_container_SUITE.erl')
-rw-r--r--erts/emulator/test/node_container_SUITE.erl1288
1 files changed, 1288 insertions, 0 deletions
diff --git a/erts/emulator/test/node_container_SUITE.erl b/erts/emulator/test/node_container_SUITE.erl
new file mode 100644
index 0000000000..f3d9eb783b
--- /dev/null
+++ b/erts/emulator/test/node_container_SUITE.erl
@@ -0,0 +1,1288 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%%----------------------------------------------------------------------
+%%% File : node_container_SUITE.erl
+%%% Author : Rickard <[email protected]>
+%%% Purpose :
+%%% Created : 24 Jul 2002 by Rickard <[email protected]>
+%%%----------------------------------------------------------------------
+
+-module(node_container_SUITE).
+-author('[email protected]').
+
+%-define(line_trace, 1).
+
+-include("test_server.hrl").
+
+%-compile(export_all).
+-export([all/1, init_per_testcase/2, fin_per_testcase/2, end_per_suite/1,
+ node_container_refc_check/1]).
+
+-export([term_to_binary_to_term_eq/1,
+ round_trip_eq/1,
+ cmp/1,
+ ref_eq/1,
+ node_table_gc/1,
+ dist_link_refc/1,
+ dist_monitor_refc/1,
+ node_controller_refc/1,
+ ets_refc/1,
+ match_spec_refc/1,
+ timer_refc/1,
+ otp_4715/1,
+ pid_wrap/1,
+ port_wrap/1,
+ bad_nc/1,
+ unique_pid/1,
+ iter_max_procs/1]).
+
+-define(DEFAULT_TIMEOUT, ?t:minutes(10)).
+
+all(doc) -> [];
+all(suite) ->
+ [term_to_binary_to_term_eq,
+ round_trip_eq,
+ cmp,
+ ref_eq,
+ node_table_gc,
+ dist_link_refc,
+ dist_monitor_refc,
+ node_controller_refc,
+ ets_refc,
+ match_spec_refc,
+ timer_refc,
+ otp_4715,
+ pid_wrap,
+ port_wrap,
+ bad_nc,
+ unique_pid,
+ iter_max_procs].
+
+available_internal_state(Bool) when Bool == true; Bool == false ->
+ case {Bool,
+ (catch erts_debug:get_internal_state(available_internal_state))} of
+ {true, true} ->
+ true;
+ {false, true} ->
+ erts_debug:set_internal_state(available_internal_state, false),
+ true;
+ {true, _} ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ false;
+ {false, _} ->
+ false
+ end.
+
+init_per_testcase(_Case, Config) when is_list(Config) ->
+ Dog = ?t:timetrap(?DEFAULT_TIMEOUT),
+ available_internal_state(true),
+ [{watchdog, Dog}|Config].
+
+fin_per_testcase(_Case, Config) when is_list(Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+end_per_suite(_Config) ->
+ available_internal_state(false).
+
+%%%
+%%% The test cases -------------------------------------------------------------
+%%%
+
+-define(MAX_PIDS_PORTS, ((1 bsl 28) - 1)).
+
+%%
+%% Test case: term_to_binary_to_term_eq
+%%
+term_to_binary_to_term_eq(doc) ->
+ ["Tests that node container terms that are converted to external format "
+ "and back stay equal to themselves."];
+term_to_binary_to_term_eq(suite) -> [];
+term_to_binary_to_term_eq(Config) when is_list(Config) ->
+ ?line ThisNode = {node(), erlang:system_info(creation)},
+ % Get local node containers
+ ?line LPid = self(),
+ ?line LXPid = mk_pid(ThisNode, 32767, 8191),
+ ?line LPort = hd(erlang:ports()),
+ ?line LXPort = mk_port(ThisNode, 268435455),
+ ?line LLRef = make_ref(),
+ ?line LHLRef = mk_ref(ThisNode, [47, 11]),
+ ?line LSRef = mk_ref(ThisNode, [4711]),
+ % Test local nc:s
+ ?line LPid = binary_to_term(term_to_binary(LPid)),
+ ?line LXPid = binary_to_term(term_to_binary(LXPid)),
+ ?line LPort = binary_to_term(term_to_binary(LPort)),
+ ?line LXPort = binary_to_term(term_to_binary(LXPort)),
+ ?line LLRef = binary_to_term(term_to_binary(LLRef)),
+ ?line LHLRef = binary_to_term(term_to_binary(LHLRef)),
+ ?line LSRef = binary_to_term(term_to_binary(LSRef)),
+ % Get remote node containers
+ ?line RNode = {get_nodename(), 3},
+ ?line RPid = mk_pid(RNode, 4711, 1),
+ ?line RXPid = mk_pid(RNode, 32767, 8191),
+ ?line RPort = mk_port(RNode, 4711),
+ ?line RXPort = mk_port(RNode, 268435455),
+ ?line RLRef = mk_ref(RNode, [4711, 4711, 4711]),
+ ?line RHLRef = mk_ref(RNode, [4711, 4711]),
+ ?line RSRef = mk_ref(RNode, [4711]),
+ % Test remote nc:s
+ ?line RPid = binary_to_term(term_to_binary(RPid)),
+ ?line RXPid = binary_to_term(term_to_binary(RXPid)),
+ ?line RPort = binary_to_term(term_to_binary(RPort)),
+ ?line RXPort = binary_to_term(term_to_binary(RXPort)),
+ ?line RLRef = binary_to_term(term_to_binary(RLRef)),
+ ?line RHLRef = binary_to_term(term_to_binary(RHLRef)),
+ ?line RSRef = binary_to_term(term_to_binary(RSRef)),
+ ?line nc_refc_check(node()),
+ ?line ok.
+
+
+%%
+%% Test case: round_trip_eq
+%%
+round_trip_eq(doc) ->
+ ["Tests that node containers that are sent beteen nodes stay equal to "
+ "themselves."];
+round_trip_eq(suite) -> [];
+round_trip_eq(Config) when is_list(Config) ->
+ ?line ThisNode = {node(), erlang:system_info(creation)},
+ ?line NodeFirstName = get_nodefirstname(),
+ ?line ?line {ok, Node} = start_node(NodeFirstName),
+ ?line Self = self(),
+ ?line RPid = spawn_link(Node,
+ fun () ->
+ receive
+ {Self, Data} ->
+ Self ! {self(), Data}
+ end
+ end),
+ ?line SentPid = self(),
+ ?line SentXPid = mk_pid(ThisNode, 17471, 8190),
+ ?line SentPort = hd(erlang:ports()),
+ ?line SentXPort = mk_port(ThisNode, 268435451),
+ ?line SentLRef = make_ref(),
+ ?line SentHLRef = mk_ref(ThisNode, [4711, 17]),
+ ?line SentSRef = mk_ref(ThisNode, [4711]),
+ ?line RPid ! {Self, {SentPid,
+ SentXPid,
+ SentPort,
+ SentXPort,
+ SentLRef,
+ SentHLRef,
+ SentSRef}},
+ receive
+ {RPid, {RecPid,
+ RecXPid,
+ RecPort,
+ RecXPort,
+ RecLRef,
+ RecHLRef,
+ RecSRef}} ->
+ ?line stop_node(Node),
+ ?line SentPid = RecPid,
+ ?line SentXPid = RecXPid,
+ ?line SentPort = RecPort,
+ ?line SentXPort = RecXPort,
+ ?line SentLRef = RecLRef,
+ ?line SentHLRef = RecHLRef,
+ ?line SentSRef = RecSRef,
+ ?line nc_refc_check(node()),
+ ?line ok
+ end.
+
+
+
+%%
+%% Test case: cmp
+%%
+cmp(doc) ->
+ ["Tests that Erlang term comparison works as it should on node "
+ "containers."];
+cmp(suite) -> [];
+cmp(Config) when is_list(Config) ->
+
+ %% Inter type comparison ---------------------------------------------------
+
+ %% The Erlang term order:
+ %% number < atom < ref < fun < port < pid < tuple < nil < cons < binary
+ RNode = {get_nodename(), 2},
+
+ IRef = make_ref(),
+ ERef = mk_ref({get_nodename(), 2}, [1,2,3]),
+
+ IPid = self(),
+ EPid = mk_pid(RNode, 1, 2),
+
+ IPort = hd(erlang:ports()),
+ EPort = mk_port(RNode, 1),
+
+ %% Test pids ----------------------------------------------------
+ ?line true = 1 < IPid,
+ ?line true = 1.3 < IPid,
+ ?line true = (1 bsl 64) < IPid,
+ ?line true = an_atom < IPid,
+ ?line true = IRef < IPid,
+ ?line true = ERef < IPid,
+ ?line true = fun () -> a_fun end < IPid,
+ ?line true = IPort < IPid,
+ ?line true = EPort < IPid,
+ ?line true = IPid < {a, tuple},
+ ?line true = IPid < [],
+ ?line true = IPid < [a|cons],
+ ?line true = IPid < <<"a binary">>,
+
+ ?line true = 1 < EPid,
+ ?line true = 1.3 < EPid,
+ ?line true = (1 bsl 64) < EPid,
+ ?line true = an_atom < EPid,
+ ?line true = IRef < EPid,
+ ?line true = ERef < EPid,
+ ?line true = fun () -> a_fun end < EPid,
+ ?line true = IPort < EPid,
+ ?line true = EPort < EPid,
+ ?line true = EPid < {a, tuple},
+ ?line true = EPid < [],
+ ?line true = EPid < [a|cons],
+ ?line true = EPid < <<"a binary">>,
+
+ %% Test ports --------------------------------------------------
+ ?line true = 1 < IPort,
+ ?line true = 1.3 < IPort,
+ ?line true = (1 bsl 64) < IPort,
+ ?line true = an_atom < IPort,
+ ?line true = IRef < IPort,
+ ?line true = ERef < IPort,
+ ?line true = fun () -> a_fun end < IPort,
+ ?line true = IPort < IPid,
+ ?line true = IPort < EPid,
+ ?line true = IPort < {a, tuple},
+ ?line true = IPort < [],
+ ?line true = IPort < [a|cons],
+ ?line true = IPort < <<"a binary">>,
+
+ ?line true = 1 < EPort,
+ ?line true = 1.3 < EPort,
+ ?line true = (1 bsl 64) < EPort,
+ ?line true = an_atom < EPort,
+ ?line true = IRef < EPort,
+ ?line true = ERef < EPort,
+ ?line true = fun () -> a_fun end < EPort,
+ ?line true = EPort < IPid,
+ ?line true = EPort < EPid,
+ ?line true = EPort < {a, tuple},
+ ?line true = EPort < [],
+ ?line true = EPort < [a|cons],
+ ?line true = EPort < <<"a binary">>,
+
+ %% Test refs ----------------------------------------------------
+ ?line true = 1 < IRef,
+ ?line true = 1.3 < IRef,
+ ?line true = (1 bsl 64) < IRef,
+ ?line true = an_atom < IRef,
+ ?line true = IRef < fun () -> a_fun end,
+ ?line true = IRef < IPort,
+ ?line true = IRef < EPort,
+ ?line true = IRef < IPid,
+ ?line true = IRef < EPid,
+ ?line true = IRef < {a, tuple},
+ ?line true = IRef < [],
+ ?line true = IRef < [a|cons],
+ ?line true = IRef < <<"a binary">>,
+
+ ?line true = 1 < ERef,
+ ?line true = 1.3 < ERef,
+ ?line true = (1 bsl 64) < ERef,
+ ?line true = an_atom < ERef,
+ ?line true = ERef < fun () -> a_fun end,
+ ?line true = ERef < IPort,
+ ?line true = ERef < EPort,
+ ?line true = ERef < IPid,
+ ?line true = ERef < EPid,
+ ?line true = ERef < {a, tuple},
+ ?line true = ERef < [],
+ ?line true = ERef < [a|cons],
+ ?line true = ERef < <<"a binary">>,
+
+
+ %% Intra type comparison ---------------------------------------------------
+
+
+ %% Test pids ----------------------------------------------------
+ %%
+ %% Significance (most -> least):
+ %% serial, number, nodename, creation
+ %%
+
+ ?line Pid = mk_pid({b@b, 2}, 4711, 1),
+
+ ?line true = mk_pid({a@b, 1}, 4710, 2) > Pid,
+ ?line true = mk_pid({a@b, 1}, 4712, 1) > Pid,
+ ?line true = mk_pid({c@b, 1}, 4711, 1) > Pid,
+ ?line true = mk_pid({b@b, 3}, 4711, 1) > Pid,
+ ?line true = mk_pid({b@b, 2}, 4711, 1) =:= Pid,
+
+ %% Test ports ---------------------------------------------------
+ %%
+ %% Significance (most -> least):
+ %% nodename, creation, number
+ %%
+ %% OBS: Comparison between ports has changed in R9. This
+ %% since it wasn't stable in R8 (and eariler releases).
+ %% Significance used to be: dist_slot, number,
+ %% creation.
+
+ ?line Port = mk_port({b@b, 2}, 4711),
+
+ ?line true = mk_port({c@b, 1}, 4710) > Port,
+ ?line true = mk_port({b@b, 3}, 4710) > Port,
+ ?line true = mk_port({b@b, 2}, 4712) > Port,
+ ?line true = mk_port({b@b, 2}, 4711) =:= Port,
+
+ %% Test refs ----------------------------------------------------
+ %% Significance (most -> least):
+ %% nodename, creation, (number high, number mid), number low,
+ %%
+ %% OBS: Comparison between refs has changed in R9. This
+ %% since it wasn't stable in R8 (and eariler releases).
+ %% Significance used to be: dist_slot, number,
+ %% creation.
+ %%
+
+ ?line Ref = mk_ref({b@b, 2}, [4711, 4711, 4711]),
+
+ ?line true = mk_ref({c@b, 1}, [4710, 4710, 4710]) > Ref,
+ ?line true = mk_ref({b@b, 3}, [4710, 4710, 4710]) > Ref,
+ ?line true = mk_ref({b@b, 2}, [4710, 4710, 4712]) > Ref,
+ ?line true = mk_ref({b@b, 2}, [4710, 4712, 4711]) > Ref,
+ ?line true = mk_ref({b@b, 2}, [4712, 4711, 4711]) > Ref,
+ ?line true = mk_ref({b@b, 2}, [4711, 4711, 4711]) =:= Ref,
+
+ ok.
+
+%%
+%% Test case: ref_eq
+%%
+ref_eq(doc) -> ["Test that one word refs \"works\"."];
+ref_eq(suite) -> [];
+ref_eq(Config) when is_list(Config) ->
+ ?line ThisNode = {node(), erlang:system_info(creation)},
+ ?line AnotherNode = {get_nodename(),2},
+ ?line LLongRef = mk_ref(ThisNode, [4711, 0, 0]),
+ ?line LHalfLongRef = mk_ref(ThisNode, [4711, 0]),
+ ?line LShortRef = mk_ref(ThisNode, [4711]),
+ ?line true = LLongRef =:= LShortRef,
+ ?line true = LLongRef =:= LHalfLongRef,
+ ?line true = LLongRef =:= LLongRef,
+ ?line true = LHalfLongRef =:= LShortRef,
+ ?line true = LHalfLongRef =:= LHalfLongRef,
+ ?line true = LShortRef =:= LShortRef,
+ ?line false = LShortRef == mk_ref(ThisNode, [4711, 0, 1]), % Not any more
+ ?line RLongRef = mk_ref(AnotherNode, [4711, 0, 0]),
+ ?line RHalfLongRef = mk_ref(AnotherNode, [4711, 0]),
+ ?line RShortRef = mk_ref(AnotherNode, [4711]),
+ ?line true = RLongRef =:= RShortRef,
+ ?line true = RLongRef =:= RHalfLongRef,
+ ?line true = RLongRef =:= RLongRef,
+ ?line true = RHalfLongRef =:= RShortRef,
+ ?line true = RHalfLongRef =:= RHalfLongRef,
+ ?line true = RShortRef =:= RShortRef,
+ ?line false = RShortRef == mk_ref(AnotherNode, [4711, 0, 1]), % Not any more
+ ?line nc_refc_check(node()),
+ ?line ok.
+
+%%
+%% Test case: node_table_gc
+%%
+node_table_gc(doc) ->
+ ["Tests that node tables are garbage collected."];
+node_table_gc(suite) -> [];
+node_table_gc(Config) when is_list(Config) ->
+ ?line PreKnown = nodes(known),
+ ?line ?t:format("PreKnown = ~p~n", [PreKnown]),
+ ?line make_node_garbage(0, 200000, 1000, []),
+ ?line PostKnown = nodes(known),
+ ?line PostAreas = erlang:system_info(allocated_areas),
+ ?line ?t:format("PostKnown = ~p~n", [PostKnown]),
+ ?line ?t:format("PostAreas = ~p~n", [PostAreas]),
+ ?line true = length(PostKnown) =< length(PreKnown),
+ ?line nc_refc_check(node()),
+ ?line ok.
+
+make_node_garbage(N, L, I, Ps) when N < L ->
+ ?line Self = self(),
+ ?line P = spawn_link(fun () ->
+ % Generate two node entries and one dist
+ % entry per node name
+ ?line PL1 = make_faked_pid_list(N,
+ I div 2,
+ 1),
+ ?line put(a, PL1),
+ ?line PL2 = make_faked_pid_list(N,
+ I div 2,
+ 2),
+ ?line put(b, PL2),
+ ?line Self ! {self(), length(nodes(known))}
+ end),
+ ?line receive
+ {P, KnownLength} ->
+ ?line true = KnownLength >= I div 2
+ end,
+ ?line make_node_garbage(N+(I div 2)*2, L, I, [P|Ps]);
+make_node_garbage(_, _, _, Ps) ->
+ %% Cleanup garbage...
+ ProcIsCleanedUp
+ = fun (Proc) ->
+ undefined == erts_debug:get_internal_state({process_status,
+ Proc})
+ end,
+ lists:foreach(fun (P) -> wait_until(fun () -> ProcIsCleanedUp(P) end) end,
+ Ps),
+ ?line case erlang:system_info(heap_type) of
+ shared -> ?line garbage_collect();
+ _ -> ?line ok
+ end,
+ ?line ok.
+
+
+make_faked_pid_list(Start, No, Creation) ->
+ make_faked_pid_list(Start, No, Creation, []).
+
+make_faked_pid_list(_Start, 0, _Creation, Acc) ->
+ Acc;
+make_faked_pid_list(Start, No, Creation, Acc) ->
+ make_faked_pid_list(Start+1,
+ No-1,
+ Creation,
+ [mk_pid({"faked_node-"
+ ++ integer_to_list(Start rem 50000)
+ ++ "@"
+ ++ atom_to_list(?MODULE),
+ Creation},
+ 4711,
+ 3) | Acc]).
+
+%%
+%% Test case: dist_link_refc
+%%
+dist_link_refc(doc) ->
+ ["Tests that external reference counts are incremented and decremented "
+ "as they should for distributed links"];
+dist_link_refc(suite) -> [];
+dist_link_refc(Config) when is_list(Config) ->
+ ?line NodeFirstName = get_nodefirstname(),
+ ?line ?line {ok, Node} = start_node(NodeFirstName),
+ ?line RP = spawn_execer(Node),
+ ?line LP = spawn_link_execer(node()),
+ ?line true = sync_exec(RP, fun () -> link(LP) end),
+ ?line wait_until(fun () ->
+ ?line {links, Links} = process_info(LP, links),
+ ?line lists:member(RP, Links)
+ end),
+ ?line NodeCre = sync_exec(RP, fun() -> erlang:system_info(creation) end),
+ ?line 1 = reference_type_count(
+ link,
+ refering_entity_id({process, LP},
+ get_node_references({Node, NodeCre}))),
+ ?line exec(RP, fun() -> exit(normal) end),
+ ?line wait_until(fun () ->
+ ?line {links, Links} = process_info(LP, links),
+ ?line not lists:member(RP, Links)
+ end),
+ ?line 0 = reference_type_count(
+ link,
+ refering_entity_id({process, LP},
+ get_node_references({Node, NodeCre}))),
+ ?line exit(LP, normal),
+ ?line stop_node(Node),
+ ?line nc_refc_check(node()),
+ ?line ok.
+
+
+%%
+%% Test case: dist_monitor_refc
+%%
+dist_monitor_refc(doc) ->
+ ["Tests that external reference counts are incremented and decremented "
+ "as they should for distributed monitors"];
+dist_monitor_refc(suite) -> [];
+dist_monitor_refc(Config) when is_list(Config) ->
+ ?line NodeFirstName = get_nodefirstname(),
+ ?line {ok, Node} = start_node(NodeFirstName),
+ ?line RP = spawn_execer(Node),
+ ?line LP = spawn_link_execer(node()),
+ ?line RMon = sync_exec(RP, fun () -> erlang:monitor(process, LP) end),
+ ?line true = is_reference(RMon),
+ ?line LMon = sync_exec(LP, fun () -> erlang:monitor(process, RP) end),
+ ?line true = is_reference(LMon),
+ ?line NodeCre = sync_exec(RP, fun() -> erlang:system_info(creation) end),
+ ?line wait_until(fun () ->
+ ?line {monitored_by, MonBy}
+ = process_info(LP, monitored_by),
+ ?line {monitors, Mon}
+ = process_info(LP, monitors),
+ ?line (lists:member(RP, MonBy)
+ and lists:member({process,RP}, Mon))
+ end),
+ ?line 3 = reference_type_count(
+ monitor,
+ refering_entity_id({process, LP},
+ get_node_references({Node, NodeCre}))),
+ ?line exec(RP, fun () -> exit(normal) end),
+ ?line wait_until(fun () ->
+ ?line {monitored_by, MonBy}
+ = process_info(LP, monitored_by),
+ ?line {monitors, Mon}
+ = process_info(LP, monitors),
+ ?line ((not lists:member(RP, MonBy))
+ and (not lists:member({process,RP}, Mon)))
+ end),
+ ?line ok = sync_exec(LP,
+ fun () ->
+ receive
+ {'DOWN', LMon, process, _, _} ->
+ ok
+ end
+ end),
+ ?line 0 = reference_type_count(
+ link,
+ refering_entity_id({process, LP},
+ get_node_references({Node, NodeCre}))),
+ ?line exit(LP, normal),
+ ?line stop_node(Node),
+ ?line nc_refc_check(node()),
+ ?line ok.
+
+
+%%
+%% Test case: node_controller_refc
+%%
+node_controller_refc(doc) ->
+ ["Tests that external reference counts are incremented and decremented "
+ "as they should for entities controlling a connections."];
+node_controller_refc(suite) -> [];
+node_controller_refc(Config) when is_list(Config) ->
+ ?line NodeFirstName = get_nodefirstname(),
+ ?line ?line {ok, Node} = start_node(NodeFirstName),
+ ?line true = lists:member(Node, nodes()),
+ ?line 1 = reference_type_count(control, get_dist_references(Node)),
+ ?line P = spawn_link_execer(node()),
+ ?line Node
+ = sync_exec(P,
+ fun () ->
+ put(remote_net_kernel,
+ rpc:call(Node,erlang,whereis,[net_kernel])),
+ node(get(remote_net_kernel))
+ end),
+ ?line Creation = rpc:call(Node, erlang, system_info, [creation]),
+ ?line monitor_node(Node,true),
+ ?line stop_node(Node),
+ ?line receive {nodedown, Node} -> ok end,
+ ?line DistRefs = get_dist_references(Node),
+ ?line true = reference_type_count(node, DistRefs) > 0,
+ ?line 0 = reference_type_count(control, DistRefs),
+ % Get rid of all references to Node
+ ?line exec(P, fun () -> exit(normal) end),
+ ?line wait_until(fun () -> not is_process_alive(P) end),
+ ?line case erlang:system_info(heap_type) of
+ shared ->
+ ?line garbage_collect();
+ hybrid ->
+ ?line lists:foreach(fun (Proc) -> garbage_collect(Proc) end,
+ processes()),
+ ?line erlang:garbage_collect_message_area();
+ _ ->
+ ?line lists:foreach(fun (Proc) -> garbage_collect(Proc) end,
+ processes())
+ end,
+ ?line false = get_node_references({Node,Creation}),
+ ?line false = get_dist_references(Node),
+ ?line false = lists:member(Node, nodes(known)),
+ ?line nc_refc_check(node()),
+ ?line ok.
+
+%%
+%% Test case: ets_refc
+%%
+ets_refc(doc) ->
+ ["Tests that external reference counts are incremented and decremented "
+ "as they should for data stored in ets tables."];
+ets_refc(suite) -> [];
+ets_refc(Config) when is_list(Config) ->
+ ?line RNode = {get_nodename(), 1},
+ ?line RPid = mk_pid(RNode, 4711, 2),
+ ?line RPort = mk_port(RNode, 4711),
+ ?line RRef = mk_ref(RNode, [4711, 47, 11]),
+ ?line Tab = ets:new(ets_refc, []),
+ ?line 0 = reference_type_count(ets, get_node_references(RNode)),
+ ?line true = ets:insert(Tab, [{a, self()},
+ {b, RPid},
+ {c, hd(erlang:ports())},
+ {d, RPort},
+ {e, make_ref()}]),
+ ?line 2 = reference_type_count(ets, get_node_references(RNode)),
+ ?line true = ets:insert(Tab, {f, RRef}),
+ ?line 3 = reference_type_count(ets, get_node_references(RNode)),
+ ?line true = ets:delete(Tab, d),
+ ?line 2 = reference_type_count(ets, get_node_references(RNode)),
+ ?line true = ets:delete_all_objects(Tab),
+ ?line 0 = reference_type_count(ets, get_node_references(RNode)),
+ ?line true = ets:insert(Tab, [{b, RPid}, {e, make_ref()}]),
+ ?line 1 = reference_type_count(ets, get_node_references(RNode)),
+ ?line true = ets:delete(Tab),
+ ?line 0 = reference_type_count(ets, get_node_references(RNode)),
+ ?line nc_refc_check(node()),
+ ?line ok.
+
+%%
+%% Test case: match_spec_refc
+%%
+match_spec_refc(doc) ->
+ ["Tests that external reference counts are incremented and decremented "
+ "as they should for data stored in match specifications."];
+match_spec_refc(suite) -> [];
+match_spec_refc(Config) when is_list(Config) ->
+ ?line RNode = {get_nodename(), 1},
+ ?line RPid = mk_pid(RNode, 4711, 2),
+ ?line RPort = mk_port(RNode, 4711),
+ ?line RRef = mk_ref(RNode, [4711, 47, 11]),
+ ?line ok = do_match_spec_test(RNode, RPid, RPort, RRef),
+ ?line garbage_collect(),
+ ?line NodeRefs = get_node_references(RNode),
+ ?line 0 = reference_type_count(binary, NodeRefs),
+ ?line 0 = reference_type_count(ets, NodeRefs),
+ ?line nc_refc_check(node()),
+ ?line ok.
+
+do_match_spec_test(RNode, RPid, RPort, RRef) ->
+ ?line Tab = ets:new(match_spec_refc, []),
+ ?line true = ets:insert(Tab, [{a, RPid, RPort, RRef},
+ {b, self(), RPort, RRef},
+ {c, RPid, RPort, make_ref()},
+ {d, RPid, RPort, RRef}]),
+ ?line {M1, C1} = ets:select(Tab, [{{'$1',RPid,RPort,RRef},[],['$1']}], 1),
+ ?line NodeRefs = get_node_references(RNode),
+ ?line 3 = reference_type_count(binary, NodeRefs),
+ ?line 10 = reference_type_count(ets, NodeRefs),
+ ?line {M2, C2} = ets:select(C1),
+ ?line '$end_of_table' = ets:select(C2),
+ ?line ets:delete(Tab),
+ ?line [a,d] = lists:sort(M1++M2),
+ ?line ok.
+
+
+%%
+%% Test case: ets_refc
+%%
+timer_refc(doc) ->
+ ["Tests that external reference counts are incremented and decremented "
+ "as they should for data stored in bif timers."];
+timer_refc(suite) -> [];
+timer_refc(Config) when is_list(Config) ->
+ ?line RNode = {get_nodename(), 1},
+ ?line RPid = mk_pid(RNode, 4711, 2),
+ ?line RPort = mk_port(RNode, 4711),
+ ?line RRef = mk_ref(RNode, [4711, 47, 11]),
+ ?line 0 = reference_type_count(timer, get_node_references(RNode)),
+ ?line Pid = spawn(fun () -> receive after infinity -> ok end end),
+ ?line erlang:start_timer(10000, Pid, {RPid, RPort, RRef}),
+ ?line 3 = reference_type_count(timer, get_node_references(RNode)),
+ ?line exit(Pid, kill),
+ ?line Mon = erlang:monitor(process, Pid),
+ ?line receive {'DOWN', Mon, process, Pid, _} -> ok end,
+ ?line 0 = reference_type_count(timer, get_node_references(RNode)),
+ ?line erlang:send_after(500, Pid, {timer, RPid, RPort, RRef}),
+ ?line 0 = reference_type_count(timer, get_node_references(RNode)),
+ ?line erlang:send_after(500, self(), {timer, RPid, RPort, RRef}),
+ ?line erlang:send_after(400, bananfluga, {timer, RPid, RPort, RRef}),
+ ?line 6 = reference_type_count(timer, get_node_references(RNode)),
+ ?line receive {timer, RPid, RPort, RRef} -> ok end,
+ ?line 0 = reference_type_count(timer, get_node_references(RNode)),
+ ?line nc_refc_check(node()),
+ ?line ok.
+
+otp_4715(doc) -> [];
+otp_4715(suite) -> [];
+otp_4715(Config) when is_list(Config) ->
+ case ?t:is_release_available("r9b") of
+ true -> otp_4715_1(Config);
+ false -> {skip,"No R9B found"}
+ end.
+
+otp_4715_1(Config) ->
+ case erlang:system_info(compat_rel) of
+ 9 ->
+ ?line run_otp_4715(Config);
+ _ ->
+ ?line Pa = filename:dirname(code:which(?MODULE)),
+ ?line ?t:run_on_shielded_node(fun () ->
+ run_otp_4715(Config)
+ end,
+ "+R9 -pa " ++ Pa)
+ end.
+
+run_otp_4715(Config) when is_list(Config) ->
+ ?line erts_debug:set_internal_state(available_internal_state, true),
+ ?line PidList = [mk_pid({a@b, 1}, 4710, 2),
+ mk_pid({a@b, 1}, 4712, 1),
+ mk_pid({c@b, 1}, 4711, 1),
+ mk_pid({b@b, 3}, 4711, 1),
+ mk_pid({b@b, 2}, 4711, 1)],
+
+ ?line R9Sorted = old_mod:sort_on_old_node(PidList),
+ ?line R9Sorted = lists:sort(PidList).
+
+pid_wrap(doc) -> [];
+pid_wrap(suite) -> [];
+pid_wrap(Config) when is_list(Config) -> ?line pp_wrap(pid).
+
+port_wrap(doc) -> [];
+port_wrap(suite) -> [];
+port_wrap(Config) when is_list(Config) ->
+ ?line case ?t:os_type() of
+ {unix, _} ->
+ ?line pp_wrap(port);
+ _ ->
+ ?line {skip, "Only run on unix"}
+ end.
+
+get_next_id(pid) ->
+ erts_debug:get_internal_state(next_pid);
+get_next_id(port) ->
+ erts_debug:get_internal_state(next_port).
+
+set_next_id(pid, N) ->
+ erts_debug:set_internal_state(next_pid, N);
+set_next_id(port, N) ->
+ erts_debug:set_internal_state(next_port, N).
+
+pp_wrap(What) ->
+ ?line N = set_high_pp_next(What),
+ ?line Cre = N + 100,
+ ?line ?t:format("no creations = ~p~n", [Cre]),
+ ?line PreCre = get_next_id(What),
+ ?line ?t:format("pre creations = ~p~n", [PreCre]),
+ ?line true = is_integer(PreCre),
+ ?line do_pp_creations(What, Cre),
+ ?line PostCre = get_next_id(What),
+ ?line ?t:format("post creations = ~p~n", [PostCre]),
+ ?line true = is_integer(PostCre),
+ ?line true = PreCre > PostCre,
+ ?line Now = set_next_id(What, ?MAX_PIDS_PORTS div 2),
+ ?line ?t:format("reset to = ~p~n", [Now]),
+ ?line true = is_integer(Now),
+ ?line ok.
+
+set_high_pp_next(What) ->
+ ?line set_high_pp_next(What, ?MAX_PIDS_PORTS-1).
+
+set_high_pp_next(What, N) ->
+ ?line M = set_next_id(What, N),
+ ?line true = is_integer(M),
+ ?line case {M >= N, M =< ?MAX_PIDS_PORTS} of
+ {true, true} ->
+ ?line ?MAX_PIDS_PORTS - M + 1;
+ _ ->
+ ?line set_high_pp_next(What, N - 100)
+ end.
+
+do_pp_creations(_What, N) when is_integer(N), N =< 0 ->
+ ?line done;
+do_pp_creations(pid, N) when is_integer(N) ->
+ %% Create new pid and make sure it works...
+ ?line Me = self(),
+ ?line Ref = make_ref(),
+ ?line Pid = spawn_link(fun () ->
+ receive
+ Ref ->
+ Me ! Ref
+ end
+ end),
+ ?line Pid ! Ref,
+ ?line receive
+ Ref ->
+ ?line do_pp_creations(pid, N - 1)
+ end;
+do_pp_creations(port, N) when is_integer(N) ->
+ %% Create new port and make sure it works...
+ ?line "hej" = os:cmd("echo hej") -- "\n",
+ ?line do_pp_creations(port, N - 1).
+
+bad_nc(doc) -> [];
+bad_nc(suite) -> [];
+bad_nc(Config) when is_list(Config) ->
+ % Make sure emulator don't crash on bad node containers...
+ ?line MaxPidNum = (1 bsl 15) - 1,
+ ?line MaxPidSer = ?MAX_PIDS_PORTS bsr 15,
+ ?line ThisNode = {node(), erlang:system_info(creation)},
+ ?line {'EXIT', {badarg, mk_pid, _}}
+ = (catch mk_pid(ThisNode, MaxPidNum + 1, 17)),
+ ?line {'EXIT', {badarg, mk_pid, _}}
+ = (catch mk_pid(ThisNode, 4711, MaxPidSer + 1)),
+ ?line {'EXIT', {badarg, mk_port, _}}
+ = (catch mk_port(ThisNode, ?MAX_PIDS_PORTS + 1)),
+ ?line {'EXIT', {badarg, mk_ref, _}}
+ = (catch mk_ref(ThisNode,[(1 bsl 18), 4711, 4711])),
+ ?line {'EXIT', {badarg, mk_ref, _}}
+ = (catch mk_ref(ThisNode, [4711, 4711, 4711, 4711, 4711, 4711, 4711])),
+ ?line RemNode = {x@y, 2},
+ ?line {'EXIT', {badarg, mk_pid, _}}
+ = (catch mk_pid(RemNode, MaxPidNum + 1, MaxPidSer)),
+ ?line {'EXIT', {badarg, mk_pid, _}}
+ = (catch mk_pid(RemNode, MaxPidNum, MaxPidSer + 1)),
+ ?line {'EXIT', {badarg, mk_port, _}}
+ = (catch mk_port(RemNode, ?MAX_PIDS_PORTS + 1)),
+ ?line {'EXIT', {badarg, mk_ref, _}}
+ = (catch mk_ref(RemNode, [(1 bsl 18), 4711, 4711])),
+ ?line {'EXIT', {badarg, mk_ref, _}}
+ = (catch mk_ref(RemNode, [4711, 4711, 4711, 4711, 4711, 4711, 4711])),
+ ?line BadNode = {x@y, 4},
+ ?line {'EXIT', {badarg, mk_pid, _}}
+ = (catch mk_pid(BadNode, 4711, 17)),
+ ?line {'EXIT', {badarg, mk_port, _}}
+ = (catch mk_port(BadNode, 4711)),
+ ?line {'EXIT', {badarg, mk_ref, _}}
+ = (catch mk_ref(BadNode, [4711, 4711, 17])),
+ ?line ok.
+
+
+
+-define(NO_PIDS, 1000000).
+
+unique_pid(doc) -> [];
+unique_pid(suite) -> [];
+unique_pid(Config) when is_list(Config) ->
+ case catch erlang:system_info(modified_timing_level) of
+ Level when is_integer(Level) ->
+ {skip,
+ "Modified timing (level " ++ integer_to_list(Level)
+ ++ ") is enabled. spawn() is too slow for this "
+ " test when modified timing is enabled."};
+ _ ->
+ ?line ?NO_PIDS = length(lists:usort(mkpidlist(?NO_PIDS, []))),
+ ?line ok
+ end.
+
+mkpidlist(0, Ps) -> Ps;
+mkpidlist(N, Ps) -> mkpidlist(N-1, [spawn(fun () -> ok end)|Ps]).
+
+
+iter_max_procs(doc) -> [];
+iter_max_procs(suite) -> [];
+iter_max_procs(Config) when is_list(Config) ->
+ ?line NoMoreTests = make_ref(),
+ ?line erlang:send_after(10000, self(), NoMoreTests),
+ ?line Res = chk_max_proc_line(),
+ ?line Res = chk_max_proc_line(),
+ ?line done = chk_max_proc_line_until(NoMoreTests, Res),
+ ?line {comment,
+ io_lib:format("max processes = ~p; "
+ "process line length = ~p",
+ [element(2, Res), element(1, Res)])}.
+
+
+max_proc_line(Root, Parent, N) ->
+ Me = self(),
+ case catch spawn_link(fun () -> max_proc_line(Root, Me, N+1) end) of
+ {'EXIT', {system_limit, _}} when Root /= self() ->
+ Root ! {proc_line_length, N, self()},
+ receive remove_proc_line -> Parent ! {exiting, Me} end;
+ P when is_pid(P), Root =/= self() ->
+ receive {exiting, P} -> Parent ! {exiting, Me} end;
+ P when is_pid(P) ->
+ P;
+ Unexpected ->
+ exit({unexpected_spawn_result, Unexpected})
+ end.
+
+chk_max_proc_line() ->
+ ?line Child = max_proc_line(self(), self(), 0),
+ ?line receive
+ {proc_line_length, PLL, End} ->
+ ?line PC = erlang:system_info(process_count),
+ ?line LP = length(processes()),
+ ?line ?t:format("proc line length = ~p; "
+ "process count = ~p; "
+ "length processes = ~p~n",
+ [PLL, PC, LP]),
+ ?line End ! remove_proc_line,
+ ?line PC = LP,
+ ?line receive {exiting, Child} -> ok end,
+ ?line {PLL, PC}
+ end.
+
+chk_max_proc_line_until(NoMoreTests, Res) ->
+ receive
+ NoMoreTests ->
+ ?line done
+ after 0 ->
+ ?line Res = chk_max_proc_line(),
+ ?line chk_max_proc_line_until(NoMoreTests, Res)
+ end.
+
+%%
+%% -- Internal utils ---------------------------------------------------------
+%%
+
+-define(ND_REFS, erts_debug:get_internal_state(node_and_dist_references)).
+
+node_container_refc_check(Node) when is_atom(Node) ->
+ AIS = available_internal_state(true),
+ nc_refc_check(Node),
+ available_internal_state(AIS).
+
+nc_refc_check(Node) when is_atom(Node) ->
+ Ref = make_ref(),
+ Self = self(),
+ ?t:format("Starting reference count check of node ~w~n", [Node]),
+ spawn_link(Node,
+ fun () ->
+ {{node_references, NodeRefs},
+ {dist_references, DistRefs}} = ?ND_REFS,
+ check_nd_refc({node(), erlang:system_info(creation)},
+ NodeRefs,
+ DistRefs,
+ fun (ErrMsg) ->
+ Self ! {Ref, ErrMsg, failed},
+ exit(normal)
+ end),
+ Self ! {Ref, succeded}
+ end),
+ receive
+ {Ref, ErrorMsg, failed} ->
+ ?t:format("~s~n", [ErrorMsg]),
+ ?t:fail(reference_count_check_failed);
+ {Ref, succeded} ->
+ ?t:format("Reference count check of node ~w succeded!~n", [Node]),
+ ok
+ end.
+
+check_nd_refc({ThisNodeName, ThisCreation}, NodeRefs, DistRefs, Fail) ->
+ case catch begin
+ check_refc(ThisNodeName,ThisCreation,"node table",NodeRefs),
+ check_refc(ThisNodeName,ThisCreation,"dist table",DistRefs),
+ ok
+ end of
+ ok ->
+ ok;
+ {'EXIT', Reason} ->
+ {Y,Mo,D} = date(),
+ {H,Mi,S} = time(),
+ ErrMsg = io_lib:format("~n"
+ "*** Reference count check of node ~w "
+ "failed (~p) at ~w~w~w ~w:~w:~w~n"
+ "*** Node table references:~n ~p~n"
+ "*** Dist table references:~n ~p~n",
+ [node(), Reason, Y, Mo, D, H, Mi, S,
+ NodeRefs, DistRefs]),
+ Fail(lists:flatten(ErrMsg))
+ end.
+
+
+check_refc(ThisNodeName,ThisCreation,Table,EntryList) when is_list(EntryList) ->
+ lists:foreach(
+ fun ({Entry, Refc, ReferrerList}) ->
+ FoundRefs =
+ lists:foldl(
+ fun ({_Referrer, ReferencesList}, A1) ->
+ A1 + lists:foldl(fun ({_T,Rs},A2) ->
+ A2+Rs
+ end,
+ 0,
+ ReferencesList)
+ end,
+ 0,
+ ReferrerList),
+
+ %% Reference count equals found references ?
+ case Refc =:= FoundRefs of
+ true ->
+ ok;
+ false ->
+ exit({invalid_reference_count, Table, Entry})
+ end,
+
+ %% All entries in table referred to?
+ case {Entry, Refc} of
+ {ThisNodeName, 0} -> ok;
+ {{ThisNodeName, ThisCreation}, 0} -> ok;
+ {_, 0} -> exit({not_referred_entry_in_table, Table, Entry});
+ {_, _} -> ok
+ end
+
+ end,
+ EntryList),
+ ok.
+
+get_node_references({NodeName, Creation} = Node) when is_atom(NodeName),
+ is_integer(Creation) ->
+ {{node_references, NodeRefs},
+ {dist_references, DistRefs}} = ?ND_REFS,
+ check_nd_refc({node(), erlang:system_info(creation)},
+ NodeRefs,
+ DistRefs,
+ fun (ErrMsg) ->
+ ?t:format("~s", [ErrMsg]),
+ ?t:fail(reference_count_check_failed)
+ end),
+ find_references(Node, NodeRefs).
+
+get_dist_references(NodeName) when is_atom(NodeName) ->
+ ?line {{node_references, NodeRefs},
+ {dist_references, DistRefs}} = ?ND_REFS,
+ ?line check_nd_refc({node(), erlang:system_info(creation)},
+ NodeRefs,
+ DistRefs,
+ fun (ErrMsg) ->
+ ?line ?t:format("~s", [ErrMsg]),
+ ?line ?t:fail(reference_count_check_failed)
+ end),
+ ?line find_references(NodeName, DistRefs).
+
+find_references(N, NRefList) ->
+ case lists:keysearch(N, 1, NRefList) of
+ {value, {N, _, ReferrersList}} -> ReferrersList;
+ _ -> false
+ end.
+
+%% Currently unused
+% refering_entity_type(RefererType, ReferingEntities) ->
+% lists:filter(fun ({{RT, _}, _}) when RT == RefererType ->
+% true;
+% (_) ->
+% false
+% end,
+% ReferingEntities).
+
+refering_entity_id(ReferingEntityId, [{ReferingEntityId,_} = ReferingEntity
+ | _ReferingEntities]) ->
+ ReferingEntity;
+refering_entity_id(ReferingEntityId, [_ | ReferingEntities]) ->
+ refering_entity_id(ReferingEntityId, ReferingEntities);
+refering_entity_id(_, []) ->
+ false.
+
+reference_type_count(_, false) ->
+ 0;
+reference_type_count(Type, {_, _ReferenceCountList} = ReferingEntity) ->
+ reference_type_count(Type, [ReferingEntity]);
+reference_type_count(Type, ReferingEntities) when is_list(ReferingEntities) ->
+ lists:foldl(fun ({_, ReferenceCountList}, Acc1) ->
+ lists:foldl(fun ({T, N}, Acc2) when T == Type ->
+ N + Acc2;
+ (_, Acc2) ->
+ Acc2
+ end,
+ Acc1,
+ ReferenceCountList)
+ end,
+ 0,
+ ReferingEntities).
+
+
+start_node(Name, Args) ->
+ ?line Pa = filename:dirname(code:which(?MODULE)),
+ ?line Res = test_server:start_node(Name,
+ slave,
+ [{args, "-pa "++Pa++" "++Args}]),
+ ?line {ok, Node} = Res,
+ ?line rpc:call(Node, erts_debug, set_internal_state,
+ [available_internal_state, true]),
+ ?line Res.
+
+start_node(Name) ->
+ ?line start_node(Name, "").
+
+stop_node(Node) ->
+ ?line nc_refc_check(Node),
+ ?line true = test_server:stop_node(Node).
+
+hostname() ->
+ from($@, atom_to_list(node())).
+
+from(H, [H | T]) -> T;
+from(H, [_ | T]) -> from(H, T);
+from(_H, []) -> [].
+
+wait_until(Pred) ->
+ case Pred() of
+ true -> ok;
+ false -> receive after 100 -> wait_until(Pred) end
+ end.
+
+
+get_nodefirstname() ->
+ {A, B, C} = now(),
+ list_to_atom(atom_to_list(?MODULE)
+ ++ "-"
+ ++ integer_to_list(A)
+ ++ "-"
+ ++ integer_to_list(B)
+ ++ "-"
+ ++ integer_to_list(C)).
+
+get_nodename() ->
+ {A, B, C} = now(),
+ list_to_atom(atom_to_list(?MODULE)
+ ++ "-"
+ ++ integer_to_list(A)
+ ++ "-"
+ ++ integer_to_list(B)
+ ++ "-"
+ ++ integer_to_list(C)
+ ++ "@"
+ ++ hostname()).
+
+
+
+-define(VERSION_MAGIC, 131).
+
+-define(ATOM_EXT, 100).
+-define(REFERENCE_EXT, 101).
+-define(PORT_EXT, 102).
+-define(PID_EXT, 103).
+-define(NEW_REFERENCE_EXT, 114).
+
+uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 ->
+ [(Uint bsr 24) band 16#ff,
+ (Uint bsr 16) band 16#ff,
+ (Uint bsr 8) band 16#ff,
+ Uint band 16#ff];
+uint32_be(Uint) ->
+ exit({badarg, uint32_be, [Uint]}).
+
+
+uint16_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 16 ->
+ [(Uint bsr 8) band 16#ff,
+ Uint band 16#ff];
+uint16_be(Uint) ->
+ exit({badarg, uint16_be, [Uint]}).
+
+uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 ->
+ Uint band 16#ff;
+uint8(Uint) ->
+ exit({badarg, uint8, [Uint]}).
+
+
+
+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,
+ ?PID_EXT,
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint32_be(Number),
+ uint32_be(Serial),
+ uint8(Creation)])) of
+ Pid when is_pid(Pid) ->
+ Pid;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_pid, [{NodeName, Creation}, Number, Serial]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end.
+
+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,
+ ?PORT_EXT,
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint32_be(Number),
+ uint8(Creation)])) of
+ Port when is_port(Port) ->
+ Port;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_port, [{NodeName, Creation}, Number]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end.
+
+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,
+ ?NEW_REFERENCE_EXT,
+ uint16_be(length(Numbers)),
+ ?ATOM_EXT,
+ uint16_be(length(NodeName)),
+ NodeName,
+ uint8(Creation),
+ lists:map(fun (N) ->
+ uint32_be(N)
+ end,
+ Numbers)])) of
+ Ref when is_reference(Ref) ->
+ Ref;
+ {'EXIT', {badarg, _}} ->
+ exit({badarg, mk_ref, [{NodeName, Creation}, Numbers]});
+ Other ->
+ exit({unexpected_binary_to_term_result, Other})
+ end.
+
+exec_loop() ->
+ receive
+ {exec_fun, Fun} when is_function(Fun) ->
+ Fun();
+ {sync_exec_fun, From, Fun} when is_pid(From), is_function(Fun) ->
+ From ! {sync_exec_fun_res, self(), Fun()}
+ end,
+ exec_loop().
+
+spawn_execer(Node) ->
+ spawn(Node, fun () -> exec_loop() end).
+
+spawn_link_execer(Node) ->
+ spawn_link(Node, fun () -> exec_loop() end).
+
+exec(Pid, Fun) when is_pid(Pid), is_function(Fun) ->
+ Pid ! {exec_fun, Fun}.
+
+sync_exec(Pid, Fun) when is_pid(Pid), is_function(Fun) ->
+ Pid ! {sync_exec_fun, self(), Fun},
+ receive
+ {sync_exec_fun_res, Pid, Res} ->
+ Res
+ end.