diff options
Diffstat (limited to 'lib/kernel/test/global_SUITE.erl')
-rw-r--r-- | lib/kernel/test/global_SUITE.erl | 4395 |
1 files changed, 4395 insertions, 0 deletions
diff --git a/lib/kernel/test/global_SUITE.erl b/lib/kernel/test/global_SUITE.erl new file mode 100644 index 0000000000..a8c68985e2 --- /dev/null +++ b/lib/kernel/test/global_SUITE.erl @@ -0,0 +1,4395 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1997-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% +%% +-module(global_SUITE). + +-compile(r11). % some code is run from r11-nodes + +%-define(line_trace, 1). + +-export([all/1, + names/1, names_hidden/1, locks/1, locks_hidden/1, + bad_input/1, names_and_locks/1, lock_die/1, name_die/1, + basic_partition/1, basic_name_partition/1, + advanced_partition/1, stress_partition/1, + ring/1, simple_ring/1, line/1, simple_line/1, + global_lost_nodes/1, otp_1849/1, + otp_3162/1, otp_5640/1, otp_5737/1, + otp_6931/1, + simple_disconnect/1, + simple_resolve/1, simple_resolve2/1, simple_resolve3/1, + leftover_name/1, re_register_name/1, name_exit/1, external_nodes/1, + many_nodes/1, sync_0/1, + global_groups_change/1, + register_1/1, + both_known_1/1, + lost_unregister/1, + mass_death/1, + garbage_messages/1]). + +-export([global_load/3, lock_global/2, lock_global2/2]). + +-export([ttt/1]). +-export([mass_spawn/1]). + +-export([start_tracer/0, stop_tracer/0, get_trace/0]). + +-compile(export_all). + +-include("test_server.hrl"). + +-define(NODES, [node()|nodes()]). + +-define(UNTIL(Seq), loop_until_true(fun() -> Seq end, Config)). + +%% The resource used by the global module. +-define(GLOBAL_LOCK, global). + +ttt(suite) -> + [ +%% 5&6: succeeds +%% 4&5&6: succeeds +%% 3&4&5&6: succeeds +%% 1&2&3&6: fails +%% 1&2&6: succeeds +%% 3&6: succeeds + names, names_hidden, locks, locks_hidden, + bad_input, + names_and_locks, lock_die, name_die, basic_partition, +% advanced_partition, basic_name_partition, +% stress_partition, simple_ring, simple_line, + ring]. + +all(suite) -> + case init:get_argument(ring_line) of + {ok, _} -> + [ring_line]; + _ -> + [names, names_hidden, locks, locks_hidden, + bad_input, + names_and_locks, lock_die, name_die, basic_partition, + advanced_partition, basic_name_partition, + stress_partition, simple_ring, simple_line, + ring, line, global_lost_nodes, otp_1849, + otp_3162, otp_5640, otp_5737, otp_6931, + simple_disconnect, simple_resolve, simple_resolve2, + simple_resolve3, + leftover_name, re_register_name, name_exit, + external_nodes, many_nodes, sync_0, global_groups_change, + register_1, both_known_1, lost_unregister, + mass_death, garbage_messages] + end. + +-define(TESTCASE, testcase_name). +-define(testcase, ?config(?TESTCASE, Config)). +-define(nodes_tag, '$global_nodes'). +-define(registered, ?config(registered, Config)). + +init_per_testcase(Case, Config) when is_atom(Case), is_list(Config) -> + ok = gen_server:call(global_name_server, high_level_trace_start,infinity), + [{?TESTCASE, Case}, {registered, registered()} | Config]. + +fin_per_testcase(_Case, Config) -> + ?line write_high_level_trace(Config), + ?line _ = + gen_server:call(global_name_server, high_level_trace_stop, infinity), + ?line[global:unregister_name(N) || N <- global:registered_names(), + N =/= test_server], + ?line InitRegistered = ?registered, + ?line Registered = registered(), + ?line [io:format("~s local names: ~p~n", [What, N]) || + {What, N} <- [{"Added", Registered -- InitRegistered}, + {"Removed", InitRegistered -- Registered}], + N =/= []], + ok. + +%%% General comments: +%%% One source of problems with failing tests can be that the nodes from the +%%% previous test haven't died yet. +%%% So, when stressing a particular test by running it in a loop, it may +%%% fail already when starting the help nodes, even if the nodes have been +%%% monitored and the nodedowns picked up at the previous round. Waiting +%%% a few seconds between rounds seems to solve the problem. Possibly the +%%% timeout of 7 seconds for connections can also be a problem. This problem +%%% is the same with old (vsn 3) and new global (vsn 4). + + +%%% Test that register_name/2 registers the name on all nodes, even if +%%% a new node appears in the middle of the operation (OTP-3552). +%%% +%%% Test scenario: process p2 is spawned, locks global, starts a slave node, +%%% and tells the parent to do register_name. Then p2 sleeps for five seconds +%%% and releases the lock. Now the name should exist on both our own node +%%% and on the slave node (we wait until that is true; it seems that we +%%% can do rpc calls to another node before the connection is really up). +register_1(suite) -> []; +register_1(Config) when is_list(Config) -> + Timeout = 15, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + P = spawn_link(?MODULE, lock_global, [self(), Config]), + receive + {P, ok} -> + io:format("p1: received ok~n"), + ok + end, + P ! step2, + io:format("p1: sent step2~n"), + ?line yes = global:register_name(foo, self()), + io:format("p1: registered~n"), + P ! step3, + receive + {P, I, I2} -> + ok + end, + if + I =:= I2 -> + ok; + true -> + test_server:fail({notsync, I, I2}) + end, + ?line _ = global:unregister_name(foo), + write_high_level_trace(Config), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +lock_global(Parent, Config) -> + Id = {global, self()}, + io:format("p2: setting lock~n"), + global:set_lock(Id, [node()]), + Parent ! {self(), ok}, + io:format("p2: sent ok~n"), + receive + step2 -> + io:format("p2: received step2"), + ok + end, + io:format("p2: starting slave~n"), + {ok, Host} = inet:gethostname(), + {ok, N1} = slave:start(Host, node1), + io:format("p2: deleting lock~n"), + global:del_lock(Id, [node()]), + io:format("p2: deleted lock~n"), + receive + step3 -> + ok + end, + io:format("p2: received step3~n"), + I = global:whereis_name(foo), + io:format("p2: name ~p~n", [I]), + ?line ?UNTIL(I =:= rpc:call(N1, global, whereis_name, [foo])), + I2 = I, + slave:stop(N1), + io:format("p2: name2 ~p~n", [I2]), + Parent ! {self(), I, I2}, + ok. + +%%% Test for the OTP-3576 problem: if nodes 1 and 2 are separated and +%%% brought together again, while keeping connection with 3, it could +%%% happen that if someone temporarily held the 'global' lock, +%%% 'try_again_locker' would be called, and this time cause both 1 and 2 +%%% to obtain a lock for 'global' on node 3, which would keep the +%%% name registry from ever becoming consistent again. +both_known_1(suite) -> []; +both_known_1(Config) when is_list(Config) -> + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + + ?line OrigNames = global:registered_names(), + + ?line [Cp1, Cp2, Cp3] = start_nodes([cp1, cp2, cp3], slave, Config), + + ?line wait_for_ready_net(Config), + + ?line rpc_disconnect_node(Cp1, Cp2, Config), + + ?line {_Pid1, yes} = rpc:call(Cp1, ?MODULE, start_proc, [p1]), + ?line {_Pid2, yes} = rpc:call(Cp2, ?MODULE, start_proc, [p2]), + + ?line Names10 = rpc:call(Cp1, global, registered_names, []), + ?line Names20 = rpc:call(Cp2, global, registered_names, []), + ?line Names30 = rpc:call(Cp3, global, registered_names, []), + + Names1 = Names10 -- OrigNames, + Names2 = Names20 -- OrigNames, + Names3 = Names30 -- OrigNames, + + ?line [p1] = lists:sort(Names1), + ?line [p2] = lists:sort(Names2), + ?line [p1, p2] = lists:sort(Names3), + + ?line Locker = spawn(Cp3, ?MODULE, lock_global2, [{global, l3}, + self()]), + + ?line receive + {locked, S} -> + true = S + end, + + ?line pong = rpc:call(Cp1, net_adm, ping, [Cp2]), + + %% Bring cp1 and cp2 together, while someone has locked global. + %% They will now loop in 'loop_locker'. + + ?line Names10_2 = rpc:call(Cp1, global, registered_names, []), + ?line Names20_2 = rpc:call(Cp2, global, registered_names, []), + ?line Names30_2 = rpc:call(Cp3, global, registered_names, []), + + Names1_2 = Names10_2 -- OrigNames, + Names2_2 = Names20_2 -- OrigNames, + Names3_2 = Names30_2 -- OrigNames, + + ?line [p1] = lists:sort(Names1_2), + ?line [p2] = lists:sort(Names2_2), + ?line [p1, p2] = lists:sort(Names3_2), + + %% Let go of the lock, and expect the lockers to resolve the name + %% registry. + Locker ! {ok, self()}, + + ?line + ?UNTIL(begin + ?line Names10_3 = rpc:call(Cp1, global, registered_names, []), + ?line Names20_3 = rpc:call(Cp2, global, registered_names, []), + ?line Names30_3 = rpc:call(Cp3, global, registered_names, []), + + Names1_3 = Names10_3 -- OrigNames, + Names2_3 = Names20_3 -- OrigNames, + Names3_3 = Names30_3 -- OrigNames, + + N1 = lists:sort(Names1_3), + N2 = lists:sort(Names2_3), + N3 = lists:sort(Names3_3), + (N1 =:= [p1, p2]) and (N2 =:= [p1, p2]) and (N3 =:= [p1, p2]) + end), + + write_high_level_trace(Config), + stop_node(Cp1), + stop_node(Cp2), + stop_node(Cp3), + + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +lost_unregister(suite) -> []; +lost_unregister(doc) -> + ["OTP-6428. An unregistered name reappears."]; +lost_unregister(Config) when is_list(Config) -> + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + + ?line {ok, B} = start_node(b, Config), + ?line {ok, C} = start_node(c, Config), + Nodes = [node(), B, C], + + ?line wait_for_ready_net(Config), + + % start a proc and register it + ?line {Pid, yes} = start_proc(test), + + ?line ?UNTIL(Pid =:= global:whereis_name(test)), + ?line check_everywhere(Nodes, test, Config), + + ?line rpc_disconnect_node(B, C, Config), + ?line check_everywhere(Nodes, test, Config), + ?line _ = rpc:call(B, global, unregister_name, [test]), + ?line ?UNTIL(undefined =:= global:whereis_name(test)), + ?line Pid = rpc:call(C, global, whereis_name, [test]), + ?line check_everywhere(Nodes--[C], test, Config), + ?line pong = rpc:call(B, net_adm, ping, [C]), + + %% Now the name has reappeared on node B. + ?line ?UNTIL(Pid =:= global:whereis_name(test)), + ?line check_everywhere(Nodes, test, Config), + + exit_p(Pid), + + ?line ?UNTIL(undefined =:= global:whereis_name(test)), + ?line check_everywhere(Nodes, test, Config), + + write_high_level_trace(Config), + stop_node(B), + stop_node(C), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +-define(UNTIL_LOOP, 300). + +-define(end_tag, 'end at'). + +init_high_level_trace(Time) -> + Mul = try + test_server:timetrap_scale_factor() + catch _:_ -> 1 + end, + put(?end_tag, msec() + Time * Mul * 1000), + %% Assures that started nodes start the high level trace automatically. + ok = gen_server:call(global_name_server, high_level_trace_start,infinity), + os:putenv("GLOBAL_HIGH_LEVEL_TRACE", "TRUE"), + put(?nodes_tag, []). + +loop_until_true(Fun, Config) -> + case Fun() of + true -> + true; + _ -> + case get(?end_tag) of + undefined -> + timer:sleep(?UNTIL_LOOP), + loop_until_true(Fun, Config); + EndAt -> + Left = EndAt - msec(), + case Left < 6000 of + true -> + write_high_level_trace(Config), + Ref = make_ref(), + receive Ref -> ok end; + false -> + timer:sleep(?UNTIL_LOOP), + loop_until_true(Fun, Config) + end + end + end. + +write_high_level_trace(Config) -> + case erase(?nodes_tag) of + undefined -> + ok; + Nodes0 -> + Nodes = lists:usort([node() | Nodes0]), + write_high_level_trace(Nodes, Config) + end. + +write_high_level_trace(Nodes, Config) -> + When = now(), + %% 'info' returns more than the trace, which is nice. + Data = [{Node, {info, rpc:call(Node, global, info, [])}} || + Node <- Nodes], + Dir = ?config(priv_dir, Config), + DataFile = filename:join([Dir, lists:concat(["global_", ?testcase])]), + file:write_file(DataFile, term_to_binary({high_level_trace, When, Data})). + +lock_global2(Id, Parent) -> + S = global:set_lock(Id), + Parent ! {locked, S}, + receive + {ok, Parent} -> + ok + end. + +%%----------------------------------------------------------------- +%% Test suite for global names and locks. +%% Should be started in a CC view with: +%% erl -sname XXX -rsh ctrsh where XX not in [cp1, cp2, cp3] +%%----------------------------------------------------------------- + +%cp1 - cp3 are started, and the name 'test' registered for a process on +%test_server. Then it is checked that the name is registered on all +%nodes, using whereis_name and safe_whereis_name. Check that the same +%name can't be registered with another value. Exit the registered +%process and check that the name disappears. Register a new process +%(Pid2) under the name 'test'. Let another new process (Pid3) +%reregister itself under the same name. Test global:send/2. Test +%unregister. Kill Pid3. Start a process (Pid6) on cp3, +%register it as 'test', stop cp1 - cp3 and check that 'test' disappeared. +%Kill Pid2 and check that 'test' isn't registered. + +names(suite) -> []; +names(Config) when is_list(Config) -> + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + ?line {ok, Cp1} = start_node(cp1, Config), + ?line {ok, Cp2} = start_node(cp2, Config), + ?line {ok, Cp3} = start_node(cp3, Config), + + ?line wait_for_ready_net(Config), + + % start a proc and register it + ?line {Pid, yes} = start_proc(test), + + % test that it is registered at all nodes + ?line + ?UNTIL(begin + (Pid =:= global:safe_whereis_name(test)) and + (Pid =:= rpc:call(Cp1, global, safe_whereis_name, [test])) and + (Pid =:= rpc:call(Cp2, global, safe_whereis_name, [test])) and + (Pid =:= rpc:call(Cp3, global, safe_whereis_name, [test])) and + (Pid =:= global:whereis_name(test)) and + (Pid =:= rpc:call(Cp1, global, whereis_name, [test])) and + (Pid =:= rpc:call(Cp2, global, whereis_name, [test])) and + (Pid =:= rpc:call(Cp3, global, whereis_name, [test])) and + ([test] =:= global:registered_names() -- OrigNames) + end), + + % try to register the same name + ?line no = global:register_name(test, self()), + ?line no = rpc:call(Cp1, global, register_name, [test, self()]), + + % let process exit, check that it is unregistered automatically + exit_p(Pid), + + ?line + ?UNTIL((undefined =:= global:whereis_name(test)) and + (undefined =:= rpc:call(Cp1, global, whereis_name, [test])) and + (undefined =:= rpc:call(Cp2, global, whereis_name, [test])) and + (undefined =:= rpc:call(Cp3, global, whereis_name, [test]))), + + % test re_register + ?line {Pid2, yes} = start_proc(test), + ?line ?UNTIL(Pid2 =:= rpc:call(Cp3, global, whereis_name, [test])), + Pid3 = rpc:call(Cp3, ?MODULE, start_proc2, [test]), + ?line ?UNTIL(Pid3 =:= rpc:call(Cp3, global, whereis_name, [test])), + Pid3 = global:whereis_name(test), + + % test sending + global:send(test, {ping, self()}), + receive + {pong, Cp3} -> ok + after + 2000 -> test_server:fail(timeout1) + end, + + rpc:call(Cp1, global, send, [test, {ping, self()}]), + receive + {pong, Cp3} -> ok + after + 2000 -> test_server:fail(timeout2) + end, + + ?line _ = global:unregister_name(test), + ?line + ?UNTIL((undefined =:= global:whereis_name(test)) and + (undefined =:= rpc:call(Cp1, global, whereis_name, [test])) and + (undefined =:= rpc:call(Cp2, global, whereis_name, [test])) and + (undefined =:= rpc:call(Cp3, global, whereis_name, [test]))), + + exit_p(Pid3), + + ?line ?UNTIL(undefined =:= global:whereis_name(test)), + + % register a proc + ?line {_Pid6, yes} = rpc:call(Cp3, ?MODULE, start_proc, [test]), + + write_high_level_trace(Config), + % stop the nodes, and make sure names are released. + stop_node(Cp1), + stop_node(Cp2), + stop_node(Cp3), + + ?line ?UNTIL(undefined =:= global:whereis_name(test)), + exit_p(Pid2), + + ?line ?UNTIL(undefined =:= global:whereis_name(test)), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +names_hidden(suite) -> []; +names_hidden(doc) -> + ["Tests that names on a hidden node doesn't interfere with names on " + "visible nodes."]; +names_hidden(Config) when is_list(Config) -> + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + ?line OrigNodes = nodes(), + + ?line {ok, Cp1} = start_node(cp1, Config), + ?line {ok, Cp2} = start_node(cp2, Config), + ?line {ok, Cp3} = start_hidden_node(cp3, Config), + ?line pong = rpc:call(Cp1, net_adm, ping, [Cp3]), + ?line pong = rpc:call(Cp3, net_adm, ping, [Cp2]), + ?line pong = rpc:call(Cp3, net_adm, ping, [node()]), + + ?line [] = [Cp1, Cp2 | OrigNodes] -- nodes(), + + % start a proc on hidden node and register it + ?line {HPid, yes} = rpc:call(Cp3, ?MODULE, start_proc, [test]), + ?line Cp3 = node(HPid), + + % Check that it didn't get registered on visible nodes + ?line + ?UNTIL((undefined =:= global:safe_whereis_name(test)) and + (undefined =:= rpc:call(Cp1, global, safe_whereis_name, [test])) and + (undefined =:= rpc:call(Cp2, global, safe_whereis_name, [test])) and + (undefined =:= global:whereis_name(test)) and + (undefined =:= rpc:call(Cp1, global, whereis_name, [test])) and + (undefined =:= rpc:call(Cp2, global, whereis_name, [test]))), + + % start a proc on visible node and register it + ?line {Pid, yes} = start_proc(test), + ?line true = (Pid =/= HPid), + + % test that it is registered at all nodes + ?line + ?UNTIL((Pid =:= global:safe_whereis_name(test)) and + (Pid =:= rpc:call(Cp1, global, safe_whereis_name, [test])) and + (Pid =:= rpc:call(Cp2, global, safe_whereis_name, [test])) and + (HPid =:= rpc:call(Cp3, global, safe_whereis_name, [test])) and + (Pid =:= global:whereis_name(test)) and + (Pid =:= rpc:call(Cp1, global, whereis_name, [test])) and + (Pid =:= rpc:call(Cp2, global, whereis_name, [test])) and + (HPid =:= rpc:call(Cp3, global, whereis_name, [test])) and + ([test] =:= global:registered_names() -- OrigNames)), + + % try to register the same name + ?line no = global:register_name(test, self()), + ?line no = rpc:call(Cp1, global, register_name, [test, self()]), + + % let process exit, check that it is unregistered automatically + exit_p(Pid), + + ?line + ?UNTIL((undefined =:= global:whereis_name(test)) and + (undefined =:= rpc:call(Cp1, global, whereis_name, [test])) and + (undefined =:= rpc:call(Cp2, global, whereis_name, [test])) and + (HPid =:= rpc:call(Cp3, global, whereis_name, [test]))), + + % test re_register + ?line {Pid2, yes} = start_proc(test), + ?line ?UNTIL(Pid2 =:= rpc:call(Cp2, global, whereis_name, [test])), + Pid3 = rpc:call(Cp2, ?MODULE, start_proc2, [test]), + ?line ?UNTIL(Pid3 =:= rpc:call(Cp2, global, whereis_name, [test])), + ?line Pid3 = global:whereis_name(test), + + % test sending + ?line Pid3 = global:send(test, {ping, self()}), + receive + {pong, Cp2} -> ok + after + 2000 -> test_server:fail(timeout1) + end, + + rpc:call(Cp1, global, send, [test, {ping, self()}]), + receive + {pong, Cp2} -> ok + after + 2000 -> test_server:fail(timeout2) + end, + + ?line _ = rpc:call(Cp3, global, unregister_name, [test]), + ?line + ?UNTIL((Pid3 =:= global:whereis_name(test)) and + (Pid3 =:= rpc:call(Cp1, global, whereis_name, [test])) and + (Pid3 =:= rpc:call(Cp2, global, whereis_name, [test])) and + (undefined =:= rpc:call(Cp3, global, whereis_name, [test]))), + + ?line _ = global:unregister_name(test), + ?line + ?UNTIL((undefined =:= global:whereis_name(test)) and + (undefined =:= rpc:call(Cp1, global, whereis_name, [test])) and + (undefined =:= rpc:call(Cp2, global, whereis_name, [test])) and + (undefined =:= rpc:call(Cp3, global, whereis_name, [test]))), + + exit_p(Pid3), + exit_p(HPid), + + ?line ?UNTIL(undefined =:= global:whereis_name(test)), + + write_high_level_trace(Config), + % stop the nodes, and make sure names are released. + stop_node(Cp1), + stop_node(Cp2), + stop_node(Cp3), + + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +locks(suite) -> []; +locks(Config) when is_list(Config) -> + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line {ok, Cp1} = start_node(cp1, Config), + ?line {ok, Cp2} = start_node(cp2, Config), + ?line {ok, Cp3} = start_node(cp3, Config), + + ?line wait_for_ready_net(Config), + + % start two procs + ?line Pid = start_proc(), + ?line Pid2 = rpc:call(Cp1, ?MODULE, start_proc, []), + % set a lock, and make sure noone else can set the same lock + ?line true = global:set_lock({test_lock, self()}, ?NODES, 1), + ?line false = req(Pid, {set_lock, test_lock, self()}), + ?line false = req(Pid2, {set_lock, test_lock, self()}), + % delete, and let another proc set the lock + global:del_lock({test_lock, self()}), + ?line true = req(Pid, {set_lock, test_lock, self()}), + ?line false = req(Pid2, {set_lock, test_lock, self()}), + ?line false = global:set_lock({test_lock, self()}, ?NODES,1), + % kill lock-holding proc, make sure the lock is released + exit_p(Pid), + ?UNTIL(true =:= global:set_lock({test_lock, self()}, ?NODES,1)), + Pid2 ! {set_lock_loop, test_lock, self()}, + % make sure we don't have the msg + receive + {got_lock, Pid2} -> test_server:fail(got_lock) + after + 1000 -> ok + end, + global:del_lock({test_lock, self()}), + % make sure pid2 got the lock + receive + {got_lock, Pid2} -> ok + after + % 12000 >> 5000, which is the max time before a new retry for + % set_lock + 12000 -> test_server:fail(got_lock2) + end, + + % let proc set the same lock + ?line true = req(Pid2, {set_lock, test_lock, self()}), + % let proc set new lock + ?line true = req(Pid2, {set_lock, test_lock2, self()}), + ?line false = global:set_lock({test_lock, self()},?NODES,1), + ?line false = global:set_lock({test_lock2, self()}, ?NODES,1), + exit_p(Pid2), +% erlang:display({locks1, ets:tab2list(global_locks)}), + ?UNTIL(true =:= global:set_lock({test_lock, self()}, ?NODES, 1)), + ?UNTIL(true =:= global:set_lock({test_lock2, self()}, ?NODES, 1)), + ?line global:del_lock({test_lock, self()}), + ?line global:del_lock({test_lock2, self()}), + + % let proc set two locks + ?line Pid3 = rpc:call(Cp1, ?MODULE, start_proc, []), + ?line true = req(Pid3, {set_lock, test_lock, self()}), + ?line true = req(Pid3, {set_lock, test_lock2, self()}), + % del one lock + ?line Pid3 ! {del_lock, test_lock2}, + ?line test_server:sleep(100), + % check that one lock is still set, but not the other + ?line false = global:set_lock({test_lock, self()}, ?NODES, 1), + ?line true = global:set_lock({test_lock2, self()}, ?NODES, 1), + ?line global:del_lock({test_lock2, self()}), + % kill lock-holder + exit_p(Pid3), +% erlang:display({locks2, ets:tab2list(global_locks)}), + ?UNTIL(true =:= global:set_lock({test_lock, self()}, ?NODES, 1)), + ?line global:del_lock({test_lock, self()}), + ?UNTIL(true =:= global:set_lock({test_lock2, self()}, ?NODES, 1)), + ?line global:del_lock({test_lock2, self()}), + + % start one proc on each node + ?line Pid4 = start_proc(), + ?line Pid5 = rpc:call(Cp1, ?MODULE, start_proc, []), + ?line Pid6 = rpc:call(Cp2, ?MODULE, start_proc, []), + ?line Pid7 = rpc:call(Cp3, ?MODULE, start_proc, []), + % set lock on two nodes + ?line true = req(Pid4, {set_lock, test_lock, self(), [node(), Cp1]}), + ?line false = req(Pid5, {set_lock, test_lock, self(), [node(), Cp1]}), + % set same lock on other two nodes + ?line true = req(Pid6, {set_lock, test_lock, self(), [Cp2, Cp3]}), + ?line false = req(Pid7, {set_lock, test_lock, self(), [Cp2, Cp3]}), + % release lock + Pid6 ! {del_lock, test_lock, [Cp2, Cp3]}, + % try to set lock on a node that already has the lock + ?line false = req(Pid6, {set_lock, test_lock, self(), [Cp1, Cp2, Cp3]}), + + % set lock on a node + exit_p(Pid4), + ?UNTIL(true =:= req(Pid5, {set_lock, test_lock, self(), [node(), Cp1]})), + ?line Pid8 = start_proc(), + ?line false = req(Pid8, {set_lock, test_lock, self()}), + write_high_level_trace(Config), + % stop the nodes, and make sure locks are released. + stop_node(Cp1), + stop_node(Cp2), + stop_node(Cp3), + ?line test_server:sleep(100), + ?line true = req(Pid8, {set_lock, test_lock, self()}), + exit_p(Pid8), + ?line test_server:sleep(10), + + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + + +locks_hidden(suite) -> []; +locks_hidden(doc) -> + ["Tests that locks on a hidden node doesn't interere with locks on " + "visible nodes."]; +locks_hidden(Config) when is_list(Config) -> + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNodes = nodes(), + ?line {ok, Cp1} = start_node(cp1, Config), + ?line {ok, Cp2} = start_node(cp2, Config), + ?line {ok, Cp3} = start_hidden_node(cp3, Config), + ?line pong = rpc:call(Cp1, net_adm, ping, [Cp3]), + ?line pong = rpc:call(Cp3, net_adm, ping, [Cp2]), + ?line pong = rpc:call(Cp3, net_adm, ping, [node()]), + + ?line [] = [Cp1, Cp2 | OrigNodes] -- nodes(), + + % start two procs + ?line Pid = start_proc(), + ?line Pid2 = rpc:call(Cp1, ?MODULE, start_proc, []), + ?line HPid = rpc:call(Cp3, ?MODULE, start_proc, []), + % Make sure hidden node doesn't interfere with visible nodes lock + ?line true = req(HPid, {set_lock, test_lock, self()}), + ?line true = global:set_lock({test_lock, self()}, ?NODES, 1), + ?line false = req(Pid, {set_lock, test_lock, self()}), + ?line true = req(HPid, {del_lock_sync, test_lock, self()}), + ?line false = req(Pid2, {set_lock, test_lock, self()}), + % delete, and let another proc set the lock + global:del_lock({test_lock, self()}), + ?line true = req(Pid, {set_lock, test_lock, self()}), + ?line false = req(Pid2, {set_lock, test_lock, self()}), + ?line false = global:set_lock({test_lock, self()}, ?NODES,1), + % kill lock-holding proc, make sure the lock is released + exit_p(Pid), + ?UNTIL(true =:= global:set_lock({test_lock, self()}, ?NODES, 1)), + ?UNTIL(true =:= req(HPid, {set_lock, test_lock, self()})), + Pid2 ! {set_lock_loop, test_lock, self()}, + % make sure we don't have the msg + receive + {got_lock, Pid2} -> test_server:fail(got_lock) + after + 1000 -> ok + end, + global:del_lock({test_lock, self()}), + % make sure pid2 got the lock + receive + {got_lock, Pid2} -> ok + after + % 12000 >> 5000, which is the max time before a new retry for + % set_lock + 12000 -> test_server:fail(got_lock2) + end, + ?line true = req(HPid, {del_lock_sync, test_lock, self()}), + + % let proc set the same lock + ?line true = req(Pid2, {set_lock, test_lock, self()}), + % let proc set new lock + ?line true = req(Pid2, {set_lock, test_lock2, self()}), + ?line true = req(HPid, {set_lock, test_lock, self()}), + ?line true = req(HPid, {set_lock, test_lock2, self()}), + exit_p(HPid), + ?line false = global:set_lock({test_lock, self()},?NODES,1), + ?line false = global:set_lock({test_lock2, self()}, ?NODES,1), + exit_p(Pid2), +% erlang:display({locks1, ets:tab2list(global_locks)}), + ?UNTIL(true =:= global:set_lock({test_lock, self()}, ?NODES, 1)), + ?UNTIL(true =:= global:set_lock({test_lock2, self()}, ?NODES, 1)), + ?line global:del_lock({test_lock, self()}), + ?line global:del_lock({test_lock2, self()}), + + write_high_level_trace(Config), + % stop the nodes, and make sure locks are released. + stop_node(Cp1), + stop_node(Cp2), + stop_node(Cp3), + + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + + +bad_input(suite) -> []; +bad_input(Config) when is_list(Config) -> + Timeout = 15, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + Pid = whereis(global_name_server), + ?line {'EXIT', _} = (catch global:set_lock(bad_id)), + ?line {'EXIT', _} = (catch global:set_lock({id, self()}, bad_nodes)), + ?line {'EXIT', _} = (catch global:del_lock(bad_id)), + ?line {'EXIT', _} = (catch global:del_lock({id, self()}, bad_nodes)), + ?line {'EXIT', _} = (catch global:register_name(name, bad_pid)), + ?line {'EXIT', _} = (catch global:reregister_name(name, bad_pid)), + ?line {'EXIT', _} = (catch global:trans(bad_id, {m,f})), + ?line {'EXIT', _} = (catch global:trans({id, self()}, {m,f}, [node()], -1)), + ?line Pid = whereis(global_name_server), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +names_and_locks(suite) -> []; +names_and_locks(Config) when is_list(Config) -> + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + ?line {ok, Cp1} = start_node(cp1, Config), + ?line {ok, Cp2} = start_node(cp2, Config), + ?line {ok, Cp3} = start_node(cp3, Config), + + % start one proc on each node + ?line PidTS = start_proc(), + ?line Pid1 = rpc:call(Cp1, ?MODULE, start_proc, []), + ?line Pid2 = rpc:call(Cp2, ?MODULE, start_proc, []), + ?line Pid3 = rpc:call(Cp3, ?MODULE, start_proc, []), + % register some of them + ?line yes = global:register_name(test1, Pid1), + ?line yes = global:register_name(test2, Pid2), + ?line yes = global:register_name(test3, Pid3), + ?line no = global:register_name(test3, PidTS), + ?line yes = global:register_name(test4, PidTS), + + % set lock on two nodes + ?line true = req(PidTS, {set_lock, test_lock, self(), [node(), Cp1]}), + ?line false = req(Pid1, {set_lock, test_lock, self(), [node(), Cp1]}), + % set same lock on other two nodes + ?line true = req(Pid2, {set_lock, test_lock, self(), [Cp2, Cp3]}), + ?line false = req(Pid3, {set_lock, test_lock, self(), [Cp2, Cp3]}), + % release lock + Pid2 ! {del_lock, test_lock, [Cp2, Cp3]}, + ?line test_server:sleep(100), + % try to set lock on a node that already has the lock + ?line false = req(Pid2, {set_lock, test_lock, self(), [Cp1, Cp2, Cp3]}), + % set two locks + ?line true = req(Pid2, {set_lock, test_lock, self(), [Cp2, Cp3]}), + ?line true = req(Pid2, {set_lock, test_lock2, self(), [Cp2, Cp3]}), + + % kill some processes, make sure all locks/names are released + exit_p(PidTS), + ?line ?UNTIL(undefined =:= global:whereis_name(test4)), + ?line true = global:set_lock({test_lock, self()}, [node(), Cp1], 1), + global:del_lock({test_lock, self()}, [node(), Cp1]), + + exit_p(Pid2), + ?line + ?UNTIL((undefined =:= global:whereis_name(test2)) and + (true =:= global:set_lock({test_lock, self()}, [Cp2, Cp3], 1)) and + (true =:= global:set_lock({test_lock2, self()}, [Cp2, Cp3], 1))), + + global:del_lock({test_lock, self()}, [Cp2, Cp3]), + global:del_lock({test_lock2, self()}, [Cp2, Cp3]), + + exit_p(Pid1), + exit_p(Pid3), + + ?line ?UNTIL(OrigNames =:= global:registered_names()), + + write_high_level_trace(Config), + stop_node(Cp1), + stop_node(Cp2), + stop_node(Cp3), + + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +lock_die(suite) -> []; +lock_die(doc) -> + ["OTP-6341. Remove locks using monitors."]; +lock_die(Config) when is_list(Config) -> + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + ?line {ok, Cp1} = start_node(cp1, Config), + ?line {ok, Cp2} = start_node(cp2, Config), + + %% First test. + LockId = {id, self()}, + ?line Pid2 = start_proc(), + ?line true = req(Pid2, {set_lock2, LockId, self()}), + + ?line true = global:set_lock(LockId, [Cp1]), + %% Id is locked on Cp1 and Cp2 (by Pid2) but not by self(): + %% (there is no mon. ref) + ?line _ = global:del_lock(LockId, [node(), Cp1, Cp2]), + + ?line exit_p(Pid2), + + %% Second test. + ?line Pid3 = start_proc(), + ?line true = req(Pid3, {set_lock, id, self(), [Cp1]}), + %% The lock is removed from Cp1 thanks to monitors. + ?line exit_p(Pid3), + + ?line true = global:set_lock(LockId, [node(), Cp1]), + ?line _ = global:del_lock(LockId, [node(), Cp1]), + + ?line ?UNTIL(OrigNames =:= global:registered_names()), + write_high_level_trace(Config), + stop_node(Cp1), + stop_node(Cp2), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +name_die(suite) -> []; +name_die(doc) -> + ["OTP-6341. Remove names using monitors."]; +name_die(Config) when is_list(Config) -> + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + ?line [Cp1] = Cps = start_nodes([z], peer, Config), % z > test_server + Nodes = lists:sort([node() | Cps]), + ?line wait_for_ready_net(Config), + + Name = name_die, + ?line Pid = rpc:call(Cp1, ?MODULE, start_proc, []), + + %% Test 1. No resolver is called if the same pid is registered on + %% both partitions. + T1 = node(), + Part1 = [T1], + Part2 = [Cp1], + ?line rpc_cast(Cp1, + ?MODULE, part_2_2, [Config, + Part1, + Part2, + []]), + ?line ?UNTIL(is_ready_partition(Config)), + ?line ?UNTIL(undefined =:= global:whereis_name(Name)), + ?line yes = global:register_name(Name, Pid), + + ?line pong = net_adm:ping(Cp1), + ?line wait_for_ready_net(Nodes, Config), + ?line assert_pid(global:whereis_name(Name)), + exit_p(Pid), + ?line ?UNTIL(OrigNames =:= global:registered_names()), + + %% Test 2. Register a name running outside the current partition. + %% Killing the pid will not remove the name from the current + %% partition, unless monitors are used. + ?line Pid2 = rpc:call(Cp1, ?MODULE, start_proc, []), + Dir = ?config(priv_dir, Config), + KillFile = filename:join([Dir, "kill.txt"]), + file:delete(KillFile), + ?line erlang:spawn(Cp1, fun() -> kill_pid(Pid2, KillFile, Config) end), + ?line rpc_cast(Cp1, + ?MODULE, part_2_2, [Config, + Part1, + Part2, + []]), + ?line ?UNTIL(is_ready_partition(Config)), + ?line ?UNTIL(undefined =:= global:whereis_name(Name)), + ?line yes = global:register_name(Name, Pid2), + ?line touch(KillFile, "kill"), + ?line file_contents(KillFile, "done", Config), + file:delete(KillFile), + + ?line ?UNTIL(OrigNames =:= global:registered_names()), + write_high_level_trace(Config), + stop_nodes(Cps), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +kill_pid(Pid, File, Config) -> + file_contents(File, "kill", Config), + exit_p(Pid), + touch(File, "done"). + +basic_partition(suite) -> []; +basic_partition(doc) -> + ["Tests that two partitioned networks exchange correct info."]; +basic_partition(Config) when is_list(Config) -> + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + ?line [Cp1, Cp2, Cp3] = start_nodes([cp1, cp2, cp3], peer, Config), + ?line [Cp1, Cp2, Cp3] = lists:sort(nodes()), + + ?line wait_for_ready_net(Config), + + % make cp2 and cp3 connected, partitioned from us and cp1 + ?line rpc_cast(Cp2, ?MODULE, part1, [Config, node(), Cp1, Cp3]), + ?line ?UNTIL(is_ready_partition(Config)), + + % start different processes in both partitions + ?line {Pid, yes} = start_proc(test), + + % connect to other partition + ?line pong = net_adm:ping(Cp2), + ?line pong = net_adm:ping(Cp3), + ?line [Cp1, Cp2, Cp3] = lists:sort(nodes()), + + % check names + ?line ?UNTIL(Pid =:= rpc:call(Cp2, global, whereis_name, [test])), + ?line ?UNTIL(undefined =/= global:whereis_name(test2)), + ?line Pid2 = global:whereis_name(test2), + ?line Pid2 = rpc:call(Cp2, global, whereis_name, [test2]), + ?line assert_pid(Pid2), + ?line Pid3 = global:whereis_name(test4), + ?line ?UNTIL(Pid3 =:= rpc:call(Cp1, global, whereis_name, [test4])), + ?line assert_pid(Pid3), + + % kill all procs + ?line Pid3 = global:send(test4, die), + % sleep to let the proc die + wait_for_exit(Pid3), + ?line ?UNTIL(undefined =:= global:whereis_name(test4)), + + exit_p(Pid), + exit_p(Pid2), + + ?line ?UNTIL(OrigNames =:= global:registered_names()), + + write_high_level_trace(Config), + stop_node(Cp1), + stop_node(Cp2), + stop_node(Cp3), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +basic_name_partition(suite) -> + []; +basic_name_partition(doc) -> + ["Creates two partitions with two nodes in each partition.", + "Tests that names are exchanged correctly, and that EXITs", + "during connect phase are handled correctly."]; +basic_name_partition(Config) when is_list(Config) -> + Timeout = 60, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + ?line [Cp1, Cp2, Cp3] = start_nodes([cp1, cp2, cp3], peer, Config), + ?line [Cp1, Cp2, Cp3] = lists:sort(nodes()), + Nodes = ?NODES, + + ?line wait_for_ready_net(Config), + + % There used to be more than one name registered for some + % processes. That was a mistake; there is no support for more than + % one name per process, and the manual is quite clear about that + % ("equivalent to the register/2 and whereis/1 BIFs"). The + % resolver procedure did not take care of such "duplicated" names, + % which caused this testcase to fail every now and then. + + % make cp2 and cp3 connected, partitioned from us and cp1 + % us: register name03 + % cp1: register name12 + % cp2: register name12 + % cp3: register name03 + + ?line rpc_cast(Cp2, ?MODULE, part1_5, [Config, node(), Cp1, Cp3]), + ?line ?UNTIL(is_ready_partition(Config)), + + % start different processes in both partitions + ?line {_, yes} = start_proc_basic(name03), + ?line {_, yes} = rpc:call(Cp1, ?MODULE, start_proc_basic, [name12]), + test_server:sleep(1000), + + % connect to other partition + ?line pong = net_adm:ping(Cp3), + + ?line ?UNTIL([Cp1, Cp2, Cp3] =:= lists:sort(nodes())), + ?line wait_for_ready_net(Config), + % check names + ?line Pid03 = global:whereis_name(name03), + ?line assert_pid(Pid03), + ?line true = lists:member(node(Pid03), [node(), Cp3]), + ?line check_everywhere(Nodes, name03, Config), + + ?line Pid12 = global:whereis_name(name12), + ?line assert_pid(Pid12), + ?line true = lists:member(node(Pid12), [Cp1, Cp2]), + ?line check_everywhere(Nodes, name12, Config), + + % kill all procs + ?line Pid12 = global:send(name12, die), + ?line Pid03 = global:send(name03, die), + % sleep to let the procs die + wait_for_exit(Pid12), + wait_for_exit(Pid03), + ?line + ?UNTIL(begin + Names = [name03, name12], + lists:duplicate(length(Names), undefined) + =:= [global:whereis_name(Name) || Name <- Names] + end), + + ?line ?UNTIL(OrigNames =:= global:registered_names()), + + write_high_level_trace(Config), + stop_node(Cp1), + stop_node(Cp2), + stop_node(Cp3), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +%Peer nodes cp0 - cp6 are started. Break apart the connections from +%cp3-cp6 to cp0-cp2 and test_server so we get two partitions. +%In the cp3-cp6 partition, start one process on each node and register +%using both erlang:register, and global:register (test1 on cp3, test2 on +%cp4, test3 on cp5, test4 on cp6), using different resolution functions: +%default for test1, notify_all_name for test2, random_notify_name for test3 +%and one for test4 that sends a message to test_server and keeps the +%process which is greater in the standard ordering. In the other partition, +%do the same (test1 on test_server, test2 on cp0, test3 on cp1, test4 on cp2). +%Sleep a little, then from test_server, connect to cp3-cp6 in order. +%Check that the values for the registered names are the expected ones, and +%that the messages from test4 arrive. + +advanced_partition(suite) -> + []; +advanced_partition(doc) -> + ["Test that names are resolved correctly when two", + "partitioned networks connect."]; +advanced_partition(Config) when is_list(Config) -> + Timeout = 60, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + ?line [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6] + = start_nodes([cp0, cp1, cp2, cp3, cp4, cp5, cp6], peer, Config), + Nodes = lists:sort([node(), Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6]), + ?line wait_for_ready_net(Config), + + % make cp3-cp6 connected, partitioned from us and cp0-cp2 + ?line rpc_cast(Cp3, ?MODULE, part2, + [Config, self(), node(), Cp0, Cp1, Cp2, Cp3, Cp4, Cp5,Cp6]), + ?line ?UNTIL(is_ready_partition(Config)), + + % start different processes in this partition + ?line start_procs(self(), Cp0, Cp1, Cp2, Config), + + % connect to other partition + ?line pong = net_adm:ping(Cp3), + ?line pong = net_adm:ping(Cp4), + ?line pong = net_adm:ping(Cp5), + ?line pong = net_adm:ping(Cp6), + + ?line wait_for_ready_net(Config), + + ?line + ?UNTIL(lists:member(undefined, + [rpc:call(Cp3, erlang, whereis, [test1]), + rpc:call(node(), erlang, whereis, [test1])])), + + Nt1 = rpc:call(Cp3, erlang, whereis, [test1]), + Nt2 = rpc:call(Cp4, erlang, whereis, [test2]), + Nt3 = rpc:call(Cp5, erlang, whereis, [test3]), + Nt4 = rpc:call(Cp6, erlang, whereis, [test4]), + + Mt1 = rpc:call(node(), erlang, whereis, [test1]), + Mt2 = rpc:call(Cp0, erlang, whereis, [test2]), + Mt3 = rpc:call(Cp1, erlang, whereis, [test3]), + _Mt4 = rpc:call(Cp2, erlang, whereis, [test4]), + + % check names + ?line Pid1 = global:whereis_name(test1), + ?line Pid1 = rpc:call(Cp3, global, whereis_name, [test1]), + ?line assert_pid(Pid1), + ?line true = lists:member(Pid1, [Nt1, Mt1]), + ?line true = lists:member(undefined, [Nt1, Mt1]), + ?line check_everywhere(Nodes, test1, Config), + + ?line undefined = global:whereis_name(test2), + ?line undefined = rpc:call(Cp3, global, whereis_name, [test2]), + ?line yes = sreq(Nt2, {got_notify, self()}), + ?line yes = sreq(Mt2, {got_notify, self()}), + ?line check_everywhere(Nodes, test2, Config), + + ?line Pid3 = global:whereis_name(test3), + ?line Pid3 = rpc:call(Cp3, global, whereis_name, [test3]), + ?line assert_pid(Pid3), + ?line true = lists:member(Pid3, [Nt3, Mt3]), + ?line no = sreq(Pid3, {got_notify, self()}), + ?line yes = sreq(other(Pid3, [Nt2, Nt3]), {got_notify, self()}), + ?line check_everywhere(Nodes, test3, Config), + + ?line Pid4 = global:whereis_name(test4), + ?line Pid4 = rpc:call(Cp3, global, whereis_name, [test4]), + ?line assert_pid(Pid4), +% ?line true = lists:member(Pid4, [Nt4, Mt4]), + ?line Pid4 = Nt4, + ?line check_everywhere(Nodes, test4, Config), + + ?line 1 = collect_resolves(), + + ?line Pid1 = global:send(test1, die), + exit_p(Pid3), + exit_p(Pid4), + wait_for_exit(Pid1), + wait_for_exit(Pid3), + ?line ?UNTIL(OrigNames =:= global:registered_names()), + + write_high_level_trace(Config), + stop_node(Cp0), + stop_node(Cp1), + stop_node(Cp2), + stop_node(Cp3), + stop_node(Cp4), + stop_node(Cp5), + stop_node(Cp6), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +%Peer nodes cp0 - cp6 are started, and partitioned just like in +%advanced_partition. Start cp8, only connected to test_server. Let cp6 +%break apart from the rest, and 12 s later, ping cp0 and cp3, and +%register the name test5. After the same 12 s, let cp5 halt. +%Wait for the death of cp5. Ping cp3 (at the same time as cp6 does). +%Take down cp2. Start cp7, restart cp2. Ping cp4, cp6 and cp8. +%Now, expect all nodes to be connected and have the same picture of all +%registered names. + +stress_partition(suite) -> + []; +stress_partition(doc) -> + ["Stress global, make a partitioned net, make some nodes", + "go up/down a bit."]; +stress_partition(Config) when is_list(Config) -> + Timeout = 90, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + ?line [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6] + = start_nodes([cp0, cp1, cp2, cp3, cp4, cp5, cp6], peer, Config), + + ?line wait_for_ready_net(Config), + + % make cp3-cp5 connected, partitioned from us and cp0-cp2 + % cp6 is alone (single node). cp6 pings cp0 and cp3 in 12 secs... + ?line rpc_cast(Cp3, ?MODULE, part3, + [Config, self(), node(), Cp0, Cp1, Cp2, Cp3, Cp4, Cp5,Cp6]), + ?line ?UNTIL(is_ready_partition(Config)), + + % start different processes in this partition + ?line start_procs(self(), Cp0, Cp1, Cp2, Config), + + ?line {ok, Cp8} = start_peer_node(cp8, Config), + + monitor_node(Cp5, true), + receive + {nodedown, Cp5} -> ok + after + 20000 -> test_server:fail({no_nodedown, Cp5}) + end, + monitor_node(Cp5, false), + + % Ok, now cp6 pings us, and cp5 will go down. + + % connect to other partition + ?line pong = net_adm:ping(Cp3), + ?line rpc_cast(Cp2, ?MODULE, crash, [0]), + + % Start new nodes + ?line {ok, Cp7} = start_peer_node(cp7, Config), + ?line {ok, Cp2_2} = start_peer_node(cp2, Config), + Nodes = lists:sort([node(), Cp0, Cp1, Cp2_2, Cp3, Cp4, Cp6, Cp7, Cp8]), + put(?nodes_tag, Nodes), + + ?line pong = net_adm:ping(Cp4), + ?line pong = net_adm:ping(Cp6), + ?line pong = net_adm:ping(Cp8), + + ?line wait_for_ready_net(Nodes, Config), + + % Make sure that all nodes have the same picture of all names + ?line check_everywhere(Nodes, test1, Config), + ?line assert_pid(global:whereis_name(test1)), + + ?line check_everywhere(Nodes, test2, Config), + ?line undefined = global:whereis_name(test2), + + ?line check_everywhere(Nodes, test3, Config), + ?line assert_pid(global:whereis_name(test3)), + + ?line check_everywhere(Nodes, test4, Config), + ?line assert_pid(global:whereis_name(test4)), + + ?line check_everywhere(Nodes, test5, Config), + ?line ?UNTIL(undefined =:= global:whereis_name(test5)), + + ?line assert_pid(global:send(test1, die)), + ?line assert_pid(global:send(test3, die)), + ?line assert_pid(global:send(test4, die)), + + ?line ?UNTIL(OrigNames =:= global:registered_names()), + + write_high_level_trace(Config), + stop_node(Cp0), + stop_node(Cp1), + stop_node(Cp2_2), + stop_node(Cp3), + stop_node(Cp4), + stop_node(Cp5), + stop_node(Cp6), + stop_node(Cp7), + stop_node(Cp8), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + + +%% Use this one to test alot of connection tests +%% erl -sname ts -rsh ctrsh -pa /clearcase/otp/internal_tools/test_server/ebin/ -ring_line 10000 -s test_server run_test global_SUITE + +ring_line(suite) -> []; +ring_line(doc) -> [""]; +ring_line(Config) when is_list(Config) -> + {ok, [[N]]} = init:get_argument(ring_line), + loop_it(list_to_integer(N), Config). + +loop_it(N, Config) -> loop_it(N,N, Config). + +loop_it(0,_, _Config) -> ok; +loop_it(N,M, Config) -> + test_server:format(1, "Round: ~w", [M-N]), + ring(Config), + line(Config), + loop_it(N-1,M, Config). + + +ring(suite) -> + []; +ring(doc) -> + ["Make 10 single nodes, all having the same name.", + "Make all ping its predecessor, pinging in a ring.", + "Make sure that there's just one winner."]; +ring(Config) when is_list(Config) -> + Timeout = 60, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + ?line [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6, Cp7, Cp8] + = start_nodes([cp0, cp1, cp2, cp3, cp4, cp5, cp6, cp7, cp8], + peer, Config), + Nodes = lists:sort([node(), Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6, Cp7, Cp8]), + + ?line wait_for_ready_net(Config), + + Time = msec() + 7000, + + ?line rpc_cast(Cp0, ?MODULE, single_node, [Time, Cp8, Config]), + ?line rpc_cast(Cp1, ?MODULE, single_node, [Time, Cp0, Config]), + ?line rpc_cast(Cp2, ?MODULE, single_node, [Time, Cp1, Config]), + ?line rpc_cast(Cp3, ?MODULE, single_node, [Time, Cp2, Config]), + ?line rpc_cast(Cp4, ?MODULE, single_node, [Time, Cp3, Config]), + ?line rpc_cast(Cp5, ?MODULE, single_node, [Time, Cp4, Config]), + ?line rpc_cast(Cp6, ?MODULE, single_node, [Time, Cp5, Config]), + ?line rpc_cast(Cp7, ?MODULE, single_node, [Time, Cp6, Config]), + ?line rpc_cast(Cp8, ?MODULE, single_node, [Time, Cp7, Config]), + + % sleep to make the partitioned net ready + test_server:sleep(Time - msec()), + + ?line pong = net_adm:ping(Cp0), + ?line pong = net_adm:ping(Cp1), + ?line pong = net_adm:ping(Cp2), + ?line pong = net_adm:ping(Cp3), + ?line pong = net_adm:ping(Cp4), + ?line pong = net_adm:ping(Cp5), + ?line pong = net_adm:ping(Cp6), + ?line pong = net_adm:ping(Cp7), + ?line pong = net_adm:ping(Cp8), + + ?line pong = net_adm:ping(Cp0), + ?line pong = net_adm:ping(Cp1), + ?line pong = net_adm:ping(Cp2), + ?line pong = net_adm:ping(Cp3), + ?line pong = net_adm:ping(Cp4), + ?line pong = net_adm:ping(Cp5), + ?line pong = net_adm:ping(Cp6), + ?line pong = net_adm:ping(Cp7), + ?line pong = net_adm:ping(Cp8), + + ?line wait_for_ready_net(Nodes, Config), + + % Just make sure that all nodes have the same picture of all names + ?line check_everywhere(Nodes, single_name, Config), + ?line assert_pid(global:whereis_name(single_name)), + + ?line + ?UNTIL(begin + {Ns2, []} = rpc:multicall(Nodes, erlang, whereis, + [single_name]), + 9 =:= lists:foldl(fun(undefined, N) -> N + 1; + (_, N) -> N + end, + 0, Ns2) + end), + + ?line assert_pid(global:send(single_name, die)), + + ?line ?UNTIL(OrigNames =:= global:registered_names()), + + write_high_level_trace(Config), + stop_node(Cp0), + stop_node(Cp1), + stop_node(Cp2), + stop_node(Cp3), + stop_node(Cp4), + stop_node(Cp5), + stop_node(Cp6), + stop_node(Cp7), + stop_node(Cp8), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +simple_ring(suite) -> + []; +simple_ring(doc) -> + ["Simpler version of the ring case. Used because there are some", + "distribution problems with many nodes.", + "Make 6 single nodes, all having the same name.", + "Make all ping its predecessor, pinging in a ring.", + "Make sure that there's just one winner."]; +simple_ring(Config) when is_list(Config) -> + Timeout = 60, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + Names = [cp0, cp1, cp2, cp3, cp4, cp5], + ?line [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5] + = start_nodes(Names, peer, Config), + Nodes = lists:sort([node(), Cp0, Cp1, Cp2, Cp3, Cp4, Cp5]), + + ?line wait_for_ready_net(Config), + + Time = msec() + 5000, + + ?line rpc_cast(Cp0, ?MODULE, single_node, [Time, Cp5, Config]), + ?line rpc_cast(Cp1, ?MODULE, single_node, [Time, Cp0, Config]), + ?line rpc_cast(Cp2, ?MODULE, single_node, [Time, Cp1, Config]), + ?line rpc_cast(Cp3, ?MODULE, single_node, [Time, Cp2, Config]), + ?line rpc_cast(Cp4, ?MODULE, single_node, [Time, Cp3, Config]), + ?line rpc_cast(Cp5, ?MODULE, single_node, [Time, Cp4, Config]), + + % sleep to make the partitioned net ready + test_server:sleep(Time - msec()), + + ?line pong = net_adm:ping(Cp0), + ?line pong = net_adm:ping(Cp1), + ?line pong = net_adm:ping(Cp2), + ?line pong = net_adm:ping(Cp3), + ?line pong = net_adm:ping(Cp4), + ?line pong = net_adm:ping(Cp5), + + ?line pong = net_adm:ping(Cp0), + ?line pong = net_adm:ping(Cp1), + ?line pong = net_adm:ping(Cp2), + ?line pong = net_adm:ping(Cp3), + ?line pong = net_adm:ping(Cp4), + ?line pong = net_adm:ping(Cp5), + + ?line wait_for_ready_net(Nodes, Config), + + % Just make sure that all nodes have the same picture of all names + ?line check_everywhere(Nodes, single_name, Config), + ?line assert_pid(global:whereis_name(single_name)), + + ?line + ?UNTIL(begin + {Ns2, []} = rpc:multicall(Nodes, erlang, whereis, + [single_name]), + 6 =:= lists:foldl(fun(undefined, N) -> N + 1; + (_, N) -> N + end, + 0, Ns2) + end), + + ?line assert_pid(global:send(single_name, die)), + + ?line ?UNTIL(OrigNames =:= global:registered_names()), + + write_high_level_trace(Config), + stop_node(Cp0), + stop_node(Cp1), + stop_node(Cp2), + stop_node(Cp3), + stop_node(Cp4), + stop_node(Cp5), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +line(suite) -> + []; +line(doc) -> + ["Make 6 single nodes, all having the same name.", + "Make all ping its predecessor, pinging in a line.", + "Make sure that there's just one winner."]; +line(Config) when is_list(Config) -> + Timeout = 60, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + ?line [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6, Cp7, Cp8] + = start_nodes([cp0, cp1, cp2, cp3, cp4, cp5, cp6, cp7, cp8], + peer, Config), + Nodes = lists:sort([node(), Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6, Cp7, Cp8]), + + ?line wait_for_ready_net(Config), + + Time = msec() + 7000, + + ?line rpc_cast(Cp0, ?MODULE, single_node, + [Time, Cp0, Config]), % ping ourself! + ?line rpc_cast(Cp1, ?MODULE, single_node, [Time, Cp0, Config]), + ?line rpc_cast(Cp2, ?MODULE, single_node, [Time, Cp1, Config]), + ?line rpc_cast(Cp3, ?MODULE, single_node, [Time, Cp2, Config]), + ?line rpc_cast(Cp4, ?MODULE, single_node, [Time, Cp3, Config]), + ?line rpc_cast(Cp5, ?MODULE, single_node, [Time, Cp4, Config]), + ?line rpc_cast(Cp6, ?MODULE, single_node, [Time, Cp5, Config]), + ?line rpc_cast(Cp7, ?MODULE, single_node, [Time, Cp6, Config]), + ?line rpc_cast(Cp8, ?MODULE, single_node, [Time, Cp7, Config]), + + % sleep to make the partitioned net ready + test_server:sleep(Time - msec()), + + ?line pong = net_adm:ping(Cp0), + ?line pong = net_adm:ping(Cp1), + ?line pong = net_adm:ping(Cp2), + ?line pong = net_adm:ping(Cp3), + ?line pong = net_adm:ping(Cp4), + ?line pong = net_adm:ping(Cp5), + ?line pong = net_adm:ping(Cp6), + ?line pong = net_adm:ping(Cp7), + ?line pong = net_adm:ping(Cp8), + + ?line pong = net_adm:ping(Cp0), + ?line pong = net_adm:ping(Cp1), + ?line pong = net_adm:ping(Cp2), + ?line pong = net_adm:ping(Cp3), + ?line pong = net_adm:ping(Cp4), + ?line pong = net_adm:ping(Cp5), + ?line pong = net_adm:ping(Cp6), + ?line pong = net_adm:ping(Cp7), + ?line pong = net_adm:ping(Cp8), + + ?line wait_for_ready_net(Nodes, Config), + + % Just make sure that all nodes have the same picture of all names + ?line check_everywhere(Nodes, single_name, Config), + ?line assert_pid(global:whereis_name(single_name)), + + ?line + ?UNTIL(begin + {Ns2, []} = rpc:multicall(Nodes, erlang, whereis, + [single_name]), + 9 =:= lists:foldl(fun(undefined, N) -> N + 1; + (_, N) -> N + end, + 0, Ns2) + end), + + ?line assert_pid(global:send(single_name, die)), + + ?line ?UNTIL(OrigNames =:= global:registered_names()), + + write_high_level_trace(Config), + stop_node(Cp0), + stop_node(Cp1), + stop_node(Cp2), + stop_node(Cp3), + stop_node(Cp4), + stop_node(Cp5), + stop_node(Cp6), + stop_node(Cp7), + stop_node(Cp8), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + + +simple_line(suite) -> + []; +simple_line(doc) -> + ["Simpler version of the line case. Used because there are some", + "distribution problems with many nodes.", + "Make 6 single nodes, all having the same name.", + "Make all ping its predecessor, pinging in a line.", + "Make sure that there's just one winner."]; +simple_line(Config) when is_list(Config) -> + Timeout = 60, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + ?line [Cp0, Cp1, Cp2, Cp3, Cp4, Cp5] + = start_nodes([cp0, cp1, cp2, cp3, cp4, cp5], peer, Config), + Nodes = lists:sort([node(), Cp0, Cp1, Cp2, Cp3, Cp4, Cp5]), + + ?line wait_for_ready_net(Config), + + Time = msec() + 5000, + + ?line rpc_cast(Cp0, ?MODULE, single_node, + [Time, Cp0, Config]), % ping ourself! + ?line rpc_cast(Cp1, ?MODULE, single_node, [Time, Cp0, Config]), + ?line rpc_cast(Cp2, ?MODULE, single_node, [Time, Cp1, Config]), + ?line rpc_cast(Cp3, ?MODULE, single_node, [Time, Cp2, Config]), + ?line rpc_cast(Cp4, ?MODULE, single_node, [Time, Cp3, Config]), + ?line rpc_cast(Cp5, ?MODULE, single_node, [Time, Cp4, Config]), + + % sleep to make the partitioned net ready + test_server:sleep(Time - msec()), + + ?line pong = net_adm:ping(Cp0), + ?line pong = net_adm:ping(Cp1), + ?line pong = net_adm:ping(Cp2), + ?line pong = net_adm:ping(Cp3), + ?line pong = net_adm:ping(Cp4), + ?line pong = net_adm:ping(Cp5), + + ?line pong = net_adm:ping(Cp0), + ?line pong = net_adm:ping(Cp1), + ?line pong = net_adm:ping(Cp2), + ?line pong = net_adm:ping(Cp3), + ?line pong = net_adm:ping(Cp4), + ?line pong = net_adm:ping(Cp5), + + ?line wait_for_ready_net(Nodes, Config), + + % Just make sure that all nodes have the same picture of all names + ?line check_everywhere(Nodes, single_name, Config), + ?line assert_pid(global:whereis_name(single_name)), + + ?line + ?UNTIL(begin + {Ns2, []} = rpc:multicall(Nodes, erlang, whereis, + [single_name]), + 6 =:= lists:foldl(fun(undefined, N) -> N + 1; + (_, N) -> N + end, + 0, Ns2) + end), + + ?line assert_pid(global:send(single_name, die)), + + ?line ?UNTIL(OrigNames =:= global:registered_names()), + + write_high_level_trace(Config), + stop_node(Cp0), + stop_node(Cp1), + stop_node(Cp2), + stop_node(Cp3), + stop_node(Cp4), + stop_node(Cp5), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +otp_1849(suite) -> []; +otp_1849(doc) -> + ["Test ticket: Global should keep track of all pids that set the same lock."]; +otp_1849(Config) when is_list(Config) -> + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line {ok, Cp1} = start_node(cp1, Config), + ?line {ok, Cp2} = start_node(cp2, Config), + ?line {ok, Cp3} = start_node(cp3, Config), + + ?line wait_for_ready_net(Config), + + % start procs on each node + ?line Pid1 = rpc:call(Cp1, ?MODULE, start_proc, []), + ?line assert_pid(Pid1), + ?line Pid2 = rpc:call(Cp2, ?MODULE, start_proc, []), + ?line assert_pid(Pid2), + ?line Pid3 = rpc:call(Cp3, ?MODULE, start_proc, []), + ?line assert_pid(Pid3), + + % set a lock on every node + ?line true = req(Pid1, {set_lock2, {test_lock, ?MODULE}, self()}), + ?line true = req(Pid2, {set_lock2, {test_lock, ?MODULE}, self()}), + ?line true = req(Pid3, {set_lock2, {test_lock, ?MODULE}, self()}), + + ?line + ?UNTIL(begin + [{test_lock, ?MODULE, Lock1}] = + rpc:call(Cp1, ets, tab2list, [global_locks]), + 3 =:= length(Lock1) + end), + + ?line true = req(Pid3, {del_lock2, {test_lock, ?MODULE}, self()}), + ?line + ?UNTIL(begin + [{test_lock, ?MODULE, Lock2}] = + rpc:call(Cp1, ets, tab2list, [global_locks]), + 2 =:= length(Lock2) + end), + + ?line true = req(Pid2, {del_lock2, {test_lock, ?MODULE}, self()}), + ?line + ?UNTIL(begin + [{test_lock, ?MODULE, Lock3}] = + rpc:call(Cp1, ets, tab2list, [global_locks]), + 1 =:= length(Lock3) + end), + + ?line true = req(Pid1, {del_lock2, {test_lock, ?MODULE}, self()}), + ?line ?UNTIL([] =:= rpc:call(Cp1, ets, tab2list, [global_locks])), + + + ?line true = req(Pid1, {set_lock2, {test_lock, ?MODULE}, self()}), + ?line true = req(Pid2, {set_lock2, {test_lock, ?MODULE}, self()}), + ?line true = req(Pid3, {set_lock2, {test_lock, ?MODULE}, self()}), + ?line false = req(Pid2, {set_lock2, {test_lock, not_valid}, self()}), + + exit_p(Pid1), + ?line + ?UNTIL(begin + [{test_lock, ?MODULE, Lock10}] = + rpc:call(Cp1, ets, tab2list, [global_locks]), + 2 =:= length(Lock10) + end), + ?line + ?UNTIL(begin + [{test_lock, ?MODULE, Lock11}] = + rpc:call(Cp2, ets, tab2list, [global_locks]), + 2 =:= length(Lock11) + end), + ?line + ?UNTIL(begin + [{test_lock, ?MODULE, Lock12}] = + rpc:call(Cp3, ets, tab2list, [global_locks]), + 2 =:= length(Lock12) + end), + + write_high_level_trace(Config), + stop_node(Cp1), + stop_node(Cp2), + stop_node(Cp3), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + + +otp_3162(suite) -> []; +otp_3162(doc) -> + ["Test ticket: Deadlock in global"]; +otp_3162(Config) when is_list(Config) -> + StartFun = fun() -> + {ok, Cp1} = start_node(cp1, Config), + {ok, Cp2} = start_node(cp2, Config), + {ok, Cp3} = start_node(cp3, Config), + [Cp1, Cp2, Cp3] + end, + do_otp_3162(StartFun, Config). + +do_otp_3162(StartFun, Config) -> + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line [Cp1, Cp2, Cp3] = StartFun(), + + ?line wait_for_ready_net(Config), + + % start procs on each node + ?line Pid1 = rpc:call(Cp1, ?MODULE, start_proc4, [kalle]), + ?line assert_pid(Pid1), + ?line Pid2 = rpc:call(Cp2, ?MODULE, start_proc4, [stina]), + ?line assert_pid(Pid2), + ?line Pid3 = rpc:call(Cp3, ?MODULE, start_proc4, [vera]), + ?line assert_pid(Pid3), + + ?line rpc_disconnect_node(Cp1, Cp2, Config), + + ?line ?UNTIL + ([Cp3] =:= lists:sort(rpc:call(Cp1, erlang, nodes, [])) -- [node()]), + + ?line ?UNTIL([kalle, test_server, vera] =:= + lists:sort(rpc:call(Cp1, global, registered_names, []))), + ?line ?UNTIL + ([Cp3] =:= lists:sort(rpc:call(Cp2, erlang, nodes, [])) -- [node()]), + ?line ?UNTIL([stina, test_server, vera] =:= + lists:sort(rpc:call(Cp2, global, registered_names, []))), + ?line ?UNTIL + ([Cp1, Cp2] =:= + lists:sort(rpc:call(Cp3, erlang, nodes, [])) -- [node()]), + ?line ?UNTIL([kalle, stina, test_server, vera] =:= + lists:sort(rpc:call(Cp3, global, registered_names, []))), + + ?line pong = rpc:call(Cp2, net_adm, ping, [Cp1]), + + ?line ?UNTIL + ([Cp2, Cp3] =:= + lists:sort(rpc:call(Cp1, erlang, nodes, [])) -- [node()]), + ?line + ?UNTIL(begin + NN = lists:sort(rpc:call(Cp1, global, registered_names, [])), + [kalle, stina, test_server, vera] =:= NN + end), + ?line ?UNTIL + ([Cp1, Cp3] =:= + lists:sort(rpc:call(Cp2, erlang, nodes, [])) -- [node()]), + ?line ?UNTIL([kalle, stina, test_server, vera] =:= + lists:sort(rpc:call(Cp2, global, registered_names, []))), + ?line ?UNTIL + ([Cp1, Cp2] =:= + lists:sort(rpc:call(Cp3, erlang, nodes, [])) -- [node()]), + ?line ?UNTIL([kalle, stina, test_server, vera] =:= + lists:sort(rpc:call(Cp3, global, registered_names, []))), + + write_high_level_trace(Config), + stop_node(Cp1), + stop_node(Cp2), + stop_node(Cp3), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + + +otp_5640(suite) -> []; +otp_5640(doc) -> + ["OTP-5640. 'allow' multiple names for registered processes."]; +otp_5640(Config) when is_list(Config) -> + Timeout = 25, + ?line Dog = test_server:timetrap(test_server:seconds(Timeout)), + init_high_level_trace(Timeout), + init_condition(Config), + ?line {ok, B} = start_node(b, Config), + + ?line Nodes = lists:sort([node(), B]), + ?line wait_for_ready_net(Nodes, Config), + + Server = whereis(global_name_server), + ServerB = rpc:call(B, erlang, whereis, [global_name_server]), + + Me = self(), + Proc = spawn(fun() -> otp_5640_proc(Me) end), + + ?line yes = global:register_name(name1, Proc), + ?line no = global:register_name(name2, Proc), + + ?line ok = application:set_env(kernel, global_multi_name_action, allow), + ?line yes = global:register_name(name2, Proc), + + test_server:sleep(100), + ?line Proc = global:whereis_name(name1), + ?line Proc = global:whereis_name(name2), + ?line check_everywhere(Nodes, name1, Config), + ?line check_everywhere(Nodes, name2, Config), + + ?line {monitors_2levels, MonBy1} = mon_by_servers(Proc), + ?line [] = ([Server,Server,ServerB,ServerB] -- MonBy1), + ?line {links,[]} = process_info(Proc, links), + ?line _ = global:unregister_name(name1), + + test_server:sleep(100), + ?line undefined = global:whereis_name(name1), + ?line Proc = global:whereis_name(name2), + ?line check_everywhere(Nodes, name1, Config), + ?line check_everywhere(Nodes, name2, Config), + + ?line {monitors_2levels, MonBy2} = mon_by_servers(Proc), + ?line [] = ([Server,ServerB] -- MonBy2), + TmpMonBy2 = MonBy2 -- [Server,ServerB], + ?line TmpMonBy2 = TmpMonBy2 -- [Server,ServerB], + ?line {links,[]} = process_info(Proc, links), + + ?line yes = global:register_name(name1, Proc), + + Proc ! die, + + test_server:sleep(100), + ?line undefined = global:whereis_name(name1), + ?line undefined = global:whereis_name(name2), + ?line check_everywhere(Nodes, name1, Config), + ?line check_everywhere(Nodes, name2, Config), + ?line {monitors, GMonitors} = process_info(Server, monitors), + ?line false = lists:member({process, Proc}, GMonitors), + + write_high_level_trace(Config), + stop_node(B), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +otp_5640_proc(_Parent) -> + receive + die -> + exit(normal) + end. + +otp_5737(suite) -> []; +otp_5737(doc) -> + ["OTP-5737. set_lock/3 and trans/4 accept Retries = 0."]; +otp_5737(Config) when is_list(Config) -> + Timeout = 25, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + + LockId = {?MODULE,self()}, + Nodes = [node()], + ?line {'EXIT', _} = (catch global:set_lock(LockId, Nodes, -1)), + ?line {'EXIT', _} = (catch global:set_lock(LockId, Nodes, a)), + ?line true = global:set_lock(LockId, Nodes, 0), + Time1 = now(), + ?line false = global:set_lock({?MODULE,not_me}, Nodes, 0), + ?line true = timer:now_diff(now(), Time1) < 5000, + ?line _ = global:del_lock(LockId, Nodes), + + Fun = fun() -> ok end, + ?line {'EXIT', _} = (catch global:trans(LockId, Fun, Nodes, -1)), + ?line {'EXIT', _} = (catch global:trans(LockId, Fun, Nodes, a)), + ?line ok = global:trans(LockId, Fun, Nodes, 0), + + write_high_level_trace(Config), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +otp_6931(suite) -> []; +otp_6931(doc) -> ["OTP-6931. Ignore nodeup when connect_all=false."]; +otp_6931(Config) when is_list(Config) -> + Me = self(), + ?line {ok, CAf} = start_non_connecting_node(ca_false, Config), + ?line ok = rpc:call(CAf, error_logger, add_report_handler, [?MODULE, Me]), + ?line info = rpc:call(CAf, error_logger, warning_map, []), + ?line {global_name_server,CAf} ! {nodeup, fake_node}, + timer:sleep(100), + stop_node(CAf), + receive {nodeup,fake_node} -> test_server:fail({info_report, was, sent}) + after 1000 -> ok + end, + ok. + +%%%----------------------------------------------------------------- +%%% Testing a disconnected node. Not two partitions. +%%%----------------------------------------------------------------- +simple_disconnect(suite) -> []; +simple_disconnect(doc) -> ["OTP-5563. Disconnected nodes (not partitions)"]; +simple_disconnect(Config) when is_list(Config) -> + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + %% Three nodes (test_server, n_1, n_2). + ?line [Cp1, Cp2] = Cps = start_nodes([n_1, n_2], peer, Config), + ?line wait_for_ready_net(Config), + + Nodes = lists:sort([node() | Cps]), + + lists:foreach(fun(N) -> rpc:call(N, ?MODULE, start_tracer, []) end,Nodes), + + Name = name, + Resolver = {no_module, resolve_none}, % will never be called + PingNode = Cp2, + + ?line {_Pid1, yes} = + rpc:call(Cp1, ?MODULE, start_resolver, [Name, Resolver]), + test_server:sleep(100), + + %% Disconnect test_server and Cp2. + ?line true = erlang:disconnect_node(Cp2), + test_server:sleep(500), + + %% _Pid is registered on Cp1. The exchange of names between Cp2 and + %% test_server sees two identical pids. + ?line pong = net_adm:ping(PingNode), + ?line ?UNTIL(Cps =:= lists:sort(nodes())), + + ?line {_, Trace0} = collect_tracers(Nodes), + ?line Resolvers = [P || {_Node,new_resolver,{pid,P}} <- Trace0], + ?line lists:foreach(fun(P) -> P ! die end, Resolvers), + ?line lists:foreach(fun(P) -> wait_for_exit(P) end, Resolvers), + ?line check_everywhere(Nodes, Name, Config), + ?line undefined = global:whereis_name(Name), + + ?line {_, Trace1} = collect_tracers(Nodes), + Trace = Trace0 ++ Trace1, + ?line [] = [foo || {_, resolve_none, _, _} <- Trace], + + ?line Gs = name_servers(Nodes), + ?line [_, _, _] = monitored_by_node(Trace, Gs), + + lists:foreach(fun(N) -> rpc:call(N, ?MODULE, stop_tracer, []) end, Nodes), + + ?line OrigNames = global:registered_names(), + write_high_level_trace(Config), + stop_nodes(Cps), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +%% Not used right now. +simple_dis(Nodes0, Name, Resolver, Config) -> + Nodes = [node() | Nodes0], + NN = lists:zip(Nodes, lists:seq(1, length(Nodes))), + [{_Node,Other} | Dis] = + [{N,[N1 || {N1,I1} <- NN, I1 > I + 1]} || {N,I} <- NN], + lists:foreach( + fun({Node, DisNodes}) -> + Args = [Node, DisNodes, Name, Resolver], + ok = rpc:call(Node, ?MODULE, simple_dis_node, Args) + end, Dis), + ok = simple_dis_node(node(), Other, Name, Resolver, Config). + +simple_dis_node(_Node, DisNodes, _Name, _Resolver, Config) -> + lists:foreach( + fun(OtherNode) -> _ = erlang:disconnect_node(OtherNode) end, DisNodes), + ?line ?UNTIL(DisNodes -- nodes() =:= DisNodes), + ok. + + + +%%%----------------------------------------------------------------- +%%% Testing resolve of name. Many combinations with four nodes. +%%%----------------------------------------------------------------- +-record(cf, { + link, % node expected to have registered process running + ping, % node in partition 2 to be pinged + n1, % node starting registered process in partition 1 + n2, % node starting registered process in partition 2 + nodes, % nodes expected to exist after ping + n_res, % expected number of resolvers after ping + config + }). + +-define(RES(F), {F, fun ?MODULE:F/3}). + +simple_resolve(suite) -> []; +simple_resolve(doc) -> ["OTP-5563. Partitions and names."]; +simple_resolve(Config) when is_list(Config) -> + Timeout = 360, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + ?line [N1, A2, Z2] = Cps = start_nodes([n_1, a_2, z_2], peer, Config), + Nodes = lists:sort([node() | Cps]), + ?line wait_for_ready_net(Config), + + lists:foreach(fun(N) -> + rpc:call(N, ?MODULE, start_tracer, []) + end, Nodes), + + %% There used to be a link between global_name_server and the + %% registered name. Now there are only monitors, but the field + %% name 'link' remains... + + Cf = #cf{link = none, ping = A2, n1 = node(), n2 = A2, + nodes = [node(), N1, A2, Z2], n_res = 2, config = Config}, + + %% There is no test with a resolver that deletes a pid (like + %% global_exit_name does). The resulting DOWN signal just clears + %% out the pid from the tables, which should be harmless. So all + %% tests are done with resolvers that keep both processes. This + %% should catch all cases which used to result in bogus process + %% links (now: only monitors are used). + + %% Two partitions are created in each case below: [node(), n_1] + %% and [a_2, z_2]. A name ('name') is registered in both + %% partitions whereafter node() or n_1 pings a_2 or z_2. Note that + %% node() = test_server, which means that node() < z_2 and node() + %% > a_2. The lesser node calls the resolver. + + %% [The following comment does not apply now that monitors are used.] + %% The resolver is run on a_2 with the process on node() + %% as first argument. The process registered as 'name' on a_2 is + %% removed from the tables. It is unlinked from a_2, and the new + %% process (on node()) is inserted without trying to link to it + %% (it it known to run on some other node, in the other + %% partition). The new process is not sent to the other partition + %% for update since it already exists there. + res(?RES(resolve_first), Cps, Cf#cf{link = node(), n2 = A2}), + %% The same, but the z_2 takes the place of a_2. + res(?RES(resolve_first), Cps, Cf#cf{link = node(), n2 = Z2}), + %% The resolver is run on test_server. + res(?RES(resolve_first), Cps, Cf#cf{link = A2, n2 = A2, ping = Z2}), + res(?RES(resolve_first), Cps, Cf#cf{link = Z2, n2 = Z2, ping = Z2}), + %% Now the same tests but with n_1 taking the place of test_server. + res(?RES(resolve_first), Cps, Cf#cf{link = N1, n1 = N1, n2 = A2}), + res(?RES(resolve_first), Cps, Cf#cf{link = N1, n1 = N1, n2 = Z2}), + res(?RES(resolve_first), Cps, Cf#cf{link = A2, n1 = N1, n2 = A2, ping = Z2}), + res(?RES(resolve_first), Cps, Cf#cf{link = Z2, n1 = N1, n2 = Z2, ping = Z2}), + + %% [Maybe this set of tests is the same as (ismorphic to?) the last one.] + %% The resolver is run on a_2 with the process on node() + %% as first argument. The process registered as 'name' on a_2 is + %% the one kept. The old process is unlinked on node(), and the + %% new process (on a_2) is inserted without trying to link to it + %% (it it known to run on some other node). + res(?RES(resolve_second), Cps, Cf#cf{link = A2, n2 = A2}), + %% The same, but the z_2 takes the place of a_2. + res(?RES(resolve_second), Cps, Cf#cf{link = Z2, n2 = Z2}), + %% The resolver is run on test_server. + res(?RES(resolve_second), Cps, Cf#cf{link = node(), n2 = A2, ping = Z2}), + res(?RES(resolve_second), Cps, Cf#cf{link = node(), n2 = Z2, ping = Z2}), + %% Now the same tests but with n_1 taking the place of test_server. + res(?RES(resolve_second), Cps, Cf#cf{link = A2, n1 = N1, n2 = A2}), + res(?RES(resolve_second), Cps, Cf#cf{link = Z2, n1 = N1, n2 = Z2}), + res(?RES(resolve_second), Cps, Cf#cf{link = N1, n1 = N1, n2 = A2, ping = Z2}), + res(?RES(resolve_second), Cps, Cf#cf{link = N1, n1 = N1, n2 = Z2, ping = Z2}), + + %% A resolver that does not return one of the pids. + res(?RES(bad_resolver), Cps, Cf#cf{n2 = A2}), + res(?RES(bad_resolver), Cps, Cf#cf{n2 = Z2}), + %% The resolver is run on test_server. + res(?RES(bad_resolver), Cps, Cf#cf{n2 = A2, ping = Z2}), + res(?RES(bad_resolver), Cps, Cf#cf{n2 = Z2, ping = Z2}), + %% Now the same tests but with n_1 taking the place of test_server. + res(?RES(bad_resolver), Cps, Cf#cf{n1 = N1, n2 = A2}), + res(?RES(bad_resolver), Cps, Cf#cf{n1 = N1, n2 = Z2}), + res(?RES(bad_resolver), Cps, Cf#cf{n1 = N1, n2 = A2, ping = Z2}), + res(?RES(bad_resolver), Cps, Cf#cf{n1 = N1, n2 = Z2, ping = Z2}), + + %% Both processes are unlinked (demonitored). + res(?RES(resolve_none), Cps, Cf#cf{n2 = A2}), + res(?RES(resolve_none), Cps, Cf#cf{n2 = Z2}), + res(?RES(resolve_none), Cps, Cf#cf{n2 = A2, ping = Z2}), + res(?RES(resolve_none), Cps, Cf#cf{n2 = Z2, ping = Z2}), + res(?RES(resolve_none), Cps, Cf#cf{n1 = N1, n2 = A2}), + res(?RES(resolve_none), Cps, Cf#cf{n1 = N1, n2 = Z2}), + res(?RES(resolve_none), Cps, Cf#cf{n1 = N1, n2 = A2, ping = Z2}), + res(?RES(resolve_none), Cps, Cf#cf{n1 = N1, n2 = Z2, ping = Z2}), + + %% A resolver faking badrpc. The resolver is run on a_2, and the + %% process on node() is kept. + res(?RES(badrpc_resolver), Cps, Cf#cf{link = node(), n2 = A2}), + + %% An exiting resolver. A kind of badrpc. + res(?RES(exit_resolver), Cps, Cf#cf{link = node(), n2 = A2}), + res(?RES(exit_resolver), Cps, Cf#cf{link = node(), n2 = Z2}), + res(?RES(exit_resolver), Cps, Cf#cf{link = A2, n2 = A2, ping = Z2}), + res(?RES(exit_resolver), Cps, Cf#cf{link = Z2, n2 = Z2, ping = Z2}), + res(?RES(exit_resolver), Cps, Cf#cf{link = N1, n1 = N1, n2 = A2}), + res(?RES(exit_resolver), Cps, Cf#cf{link = N1, n1 = N1, n2 = Z2}), + res(?RES(exit_resolver), Cps, Cf#cf{link = A2, n1 = N1, n2 = A2, ping = Z2}), + res(?RES(exit_resolver), Cps, Cf#cf{link = Z2, n1 = N1, n2 = Z2, ping = Z2}), + + %% A locker that takes a lock. It used to be that the + %% global_name_server was busy exchanging names, which caused a + %% deadlock. + res(?RES(lock_resolver), Cps, Cf#cf{link = node()}), + + %% A resolver that disconnects from the node of the first pid + %% once. The nodedown message is processed (the resolver killed), + %% then a new attempt (nodeup etc.) is made. This time the + %% resolver does not disconnect any node. + res(?RES(disconnect_first), Cps, Cf#cf{link = Z2, n2 = Z2, + nodes = [node(), N1, A2, Z2]}), + + ?line lists:foreach(fun(N) -> + rpc:call(N, ?MODULE, stop_tracer, []) + end, Nodes), + + ?line OrigNames = global:registered_names(), + write_high_level_trace(Config), + stop_nodes(Cps), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +simple_resolve2(suite) -> []; +simple_resolve2(doc) -> ["OTP-5563. Partitions and names."]; +simple_resolve2(Config) when is_list(Config) -> + %% Continuation of simple_resolve. Of some reason it did not + %% always work to re-start z_2. "Cannot be a global bug." + + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + ?line [N1, A2, Z2] = Cps = start_nodes([n_1, a_2, z_2], peer, Config), + ?line wait_for_ready_net(Config), + Nodes = lists:sort([node() | Cps]), + + lists:foreach(fun(N) -> + rpc:call(N, ?MODULE, start_tracer, []) + end, Nodes), + + Cf = #cf{link = none, ping = A2, n1 = node(), n2 = A2, + nodes = [node(), N1, A2, Z2], n_res = 2, config = Config}, + + %% Halt z_2. + res(?RES(halt_second), Cps, Cf#cf{link = N1, n1 = N1, n2 = Z2, ping = A2, + nodes = [node(), N1, A2], n_res = 1}), + + ?line lists:foreach(fun(N) -> + rpc:call(N, ?MODULE, stop_tracer, []) + end, Nodes), + + ?line OrigNames = global:registered_names(), + write_high_level_trace(Config), + stop_nodes(Cps), % Not all nodes may be present, but it works anyway. + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +simple_resolve3(suite) -> []; +simple_resolve3(doc) -> ["OTP-5563. Partitions and names."]; +simple_resolve3(Config) when is_list(Config) -> + %% Continuation of simple_resolve. + + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + ?line [N1, A2, Z2] = Cps = start_nodes([n_1, a_2, z_2], peer, Config), + ?line wait_for_ready_net(Config), + Nodes = lists:sort([node() | Cps]), + + lists:foreach(fun(N) -> + rpc:call(N, ?MODULE, start_tracer, []) + end, Nodes), + + Cf = #cf{link = none, ping = A2, n1 = node(), n2 = A2, + nodes = [node(), N1, A2, Z2], n_res = 2, config = Config}, + + %% Halt a_2. + res(?RES(halt_second), Cps, Cf#cf{link = node(), n2 = A2, + nodes = [node(), N1], n_res = 1}), + + ?line lists:foreach(fun(N) -> + rpc:call(N, ?MODULE, stop_tracer, []) + end, Nodes), + + ?line OrigNames = global:registered_names(), + write_high_level_trace(Config), + stop_nodes(Cps), % Not all nodes may be present, but it works anyway. + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +res({Res,Resolver}, [N1, A2, Z2], Cf) -> + %% Note: there are no links anymore, but monitors. + #cf{link = LinkedNode, ping = PingNode, n1 = Res1, n2 = OtherNode, + nodes = Nodes0, n_res = NRes, config = Config} = Cf, + ?t:format("~n~nResolver: ~p", [Res]), + ?t:format(" Registered on partition 1: ~p", [Res1]), + ?t:format(" Registered on partition 2: ~p", [OtherNode]), + ?t:format(" Pinged node: ~p", [PingNode]), + ?t:format(" Linked node: ~p", [LinkedNode]), + ?t:format(" Expected # resolvers: ~p", [NRes]), + Nodes = lists:sort(Nodes0), + T1 = node(), + Part1 = [T1, N1], + Part2 = [A2, Z2], + Name = name, + + %% A registered name is resolved in different scenarios with just + %% four nodes. In each scenario it is checked that exactly the + %% expected monitors remain between registered processes and the + %% global_name_server. + + ?line rpc_cast(OtherNode, + ?MODULE, + part_2_2, + [Config, Part1, Part2, [{Name, Resolver}]]), + ?line ?UNTIL(is_ready_partition(Config)), + ?line {_Pid1, yes} = + rpc:call(Res1, ?MODULE, start_resolver, [Name, Resolver]), + + ?line pong = net_adm:ping(PingNode), + ?line wait_for_ready_net(Nodes, Config), + + ?line check_everywhere(Nodes, Name, Config), + ?line case global:whereis_name(Name) of + undefined when LinkedNode =:= none -> ok; + Pid -> assert_pid(Pid) + end, + + ?line {_, Trace0} = collect_tracers(Nodes), + ?line Resolvers = [P || {_Node,new_resolver,{pid,P}} <- Trace0], + + ?line NRes = length(Resolvers), + + %% Wait for extra monitor processes to be created. + %% This applies as long as global:do_monitor/1 spawns processes. + %% (Some day monitor() will be truly synchronous.) + test_server:sleep(100), + + ?line lists:foreach(fun(P) -> P ! die end, Resolvers), + ?line lists:foreach(fun(P) -> wait_for_exit(P) end, Resolvers), + + ?line check_everywhere(Nodes, Name, Config), + ?line undefined = global:whereis_name(Name), + + %% Wait for monitors to remove names. + test_server:sleep(100), + + ?line {_, Trace1} = collect_tracers(Nodes), + Trace = Trace0 ++ Trace1, + + ?line Gs = name_servers([T1, N1, A2, Z2]), + ?line MonitoredByNode = monitored_by_node(Trace, Gs), + ?line MonitoredBy = [M || {_N,M} <- MonitoredByNode], + + X = MonitoredBy -- Gs, + LengthGs = length(Gs), + ?line case MonitoredBy of + [] when LinkedNode =:= none -> ok; + Gs -> ok; + _ when LengthGs < 4, X =:= [] -> ok; + _ -> ?t:format("ERROR:~nMonitoredBy ~p~n" + "global_name_servers ~p~n", + [MonitoredByNode, Gs]), + ?t:fail(monitor_mismatch) + end, + ok. + +name_servers(Nodes) -> + lists:sort([rpc:call(N, erlang, whereis, [global_name_server]) || + N <- Nodes, + pong =:= net_adm:ping(N)]). + +monitored_by_node(Trace, Servers) -> + lists:sort([{node(M),M} || + {_Node,_P,died,{monitors_2levels,ML}} <- Trace, + M <- ML, + lists:member(M, Servers)]). + +%% Runs on a node in Part2 +part_2_2(Config, Part1, Part2, NameResolvers) -> + make_partition(Config, Part1, Part2), + lists:foreach + (fun({Name, Resolver}) -> + ?line {Pid2, yes} = start_resolver(Name, Resolver), + trace_message({node(), part_2_2, nodes(), {pid2,Pid2}}) + end, NameResolvers). + +resolve_first(name, Pid1, _Pid2) -> + Pid1. + +resolve_second(name, _Pid1, Pid2) -> + Pid2. + +resolve_none(name, _Pid1, _Pid2) -> + none. + +bad_resolver(name, _Pid1, _Pid2) -> + bad_answer. + +badrpc_resolver(name, _Pid1, _Pid2) -> + {badrpc, badrpc}. + +exit_resolver(name, _Pid1, _Pid2) -> + erlang:error(bad_resolver). + +lock_resolver(name, Pid1, _Pid2) -> + Id = {?MODULE, self()}, + Nodes = [node()], + ?line true = global:set_lock(Id, Nodes), + _ = global:del_lock(Id, Nodes), + Pid1. + +disconnect_first(name, Pid1, Pid2) -> + Name = disconnect_first_name, + case whereis(Name) of + undefined -> + spawn(fun() -> disconnect_first_name(Name) end), + true = erlang:disconnect_node(node(Pid1)); + Pid when is_pid(Pid) -> + Pid ! die + end, + Pid2. + +disconnect_first_name(Name) -> + register(Name, self()), + receive die -> ok end. + +halt_second(name, _Pid1, Pid2) -> + rpc:call(node(Pid2), erlang, halt, []), + Pid2. + +start_resolver(Name, Resolver) -> + Self = self(), + Pid = spawn(fun() -> init_resolver(Self, Name, Resolver) end), + trace_message({node(), new_resolver, {pid, Pid}}), + receive + {Pid, Res} -> {Pid, Res} + end. + +init_resolver(Parent, Name, Resolver) -> + X = global:register_name(Name, self(), Resolver), + Parent ! {self(), X}, + loop_resolver(). + +loop_resolver() -> + receive + die -> + trace_message({node(), self(), died, mon_by_servers(self())}), + exit(normal) + end. + +%% The server sometimes uses an extra process for monitoring. +%% The server monitors that extra process. +mon_by_servers(Proc) -> + {monitored_by, ML} = process_info(Proc, monitored_by), + {monitors_2levels, + lists:append([ML | + [begin + {monitored_by, MML} = rpc:call(node(M), + erlang, + process_info, + [M, monitored_by]), + MML + end || M <- ML]])}. + +-define(REGNAME, contact_a_2). + +leftover_name(suite) -> []; +leftover_name(doc) -> ["OTP-5563. Bug: nodedown while synching."]; +leftover_name(Config) when is_list(Config) -> + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + ?line [N1, A2, Z2] = Cps = start_nodes([n_1, a_2, z_2], peer, Config), + Nodes = lists:sort([node() | Cps]), + ?line wait_for_ready_net(Config), + + lists:foreach(fun(N) -> + rpc:call(N, ?MODULE, start_tracer, []) + end, Nodes), + + Name = name, % registered on a_2 + ResName = resolved_name, % registered on n_1 and a_2 + %% + ?line _Pid = ping_a_2_fun(?REGNAME, N1, A2), + + T1 = node(), + Part1 = [T1, N1], + Part2 = [A2, Z2], + NoResolver = {no_module, resolve_none}, + Resolver = fun contact_a_2/3, + ?line rpc_cast(A2, + ?MODULE, part_2_2, [Config, + Part1, + Part2, + [{Name, NoResolver}, + {ResName, Resolver}]]), + ?line ?UNTIL(is_ready_partition(Config)), + + %% resolved_name is resolved to run on a_2, an insert operation is + %% sent to n_1. The resolver function halts a_2, but the nodedown + %% message is handled by n_1 _before_ the insert operation is run + %% (at least every now and then; sometimes it seems to be + %% delayed). Unless "artificial" nodedown messages are sent the + %% name would linger on indefinitely. [There is no test case for + %% the situation that no nodedown message at all is sent.] + ?line {_Pid1, yes} = + rpc:call(N1, ?MODULE, start_resolver, + [ResName, fun contact_a_2/3]), + test_server:sleep(1000), + + ?line trace_message({node(), pinging, z_2}), + ?line pong = net_adm:ping(Z2), + ?line ?UNTIL((Nodes -- [A2]) =:= lists:sort(?NODES)), + ?t:sleep(1000), + + ?line {_,Trace0} = collect_tracers(Nodes), + + ?line Resolvers = [P || {_Node,new_resolver,{pid,P}} <- Trace0], + ?line lists:foreach(fun(P) -> P ! die end, Resolvers), + ?line lists:foreach(fun(P) -> wait_for_exit(P) end, Resolvers), + + ?line lists:foreach(fun(N) -> + rpc:call(N, ?MODULE, stop_tracer, []) + end, Nodes), + + ?line ?UNTIL(OrigNames =:= global:registered_names()), + write_high_level_trace(Config), + stop_nodes(Cps), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +%% Runs on n_1 +contact_a_2(resolved_name, Pid1, Pid2) -> + trace_message({node(), ?REGNAME, {pid1,Pid1}, {pid2,Pid2}, + {node1,node(Pid1)}, {node2,node(Pid2)}}), + ?REGNAME ! doit, + Pid2. + +ping_a_2_fun(RegName, N1, A2) -> + spawn(N1, fun() -> ping_a_2(RegName, N1, A2) end). + +ping_a_2(RegName, N1, A2) -> + register(RegName, self()), + receive doit -> + trace_message({node(), ping_a_2, {a2, A2}}), + monitor_node(A2, true), + %% Establish contact with a_2, then take it down. + rpc:call(N1, ?MODULE, halt_node, [A2]), + receive + {nodedown, A2} -> ok + end + end. + +halt_node(Node) -> + rpc:call(Node, erlang, halt, []). + +%%%----------------------------------------------------------------- +%%% Testing re-registration of a name. +%%%----------------------------------------------------------------- +re_register_name(suite) -> []; +re_register_name(doc) -> ["OTP-5563. Name is re-registered."]; +re_register_name(Config) when is_list(Config) -> + %% When re-registering a name the link to the old pid used to + %% linger on. Don't think is was a serious bug though--some memory + %% occupied by links, that's all. + %% Later: now monitors are checked. + Timeout = 15, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + Me = self(), + Pid1 = spawn(fun() -> proc(Me) end), + ?line yes = global:register_name(name, Pid1), + Pid2 = spawn(fun() -> proc(Me) end), + ?line _ = global:re_register_name(name, Pid2), + Pid2 ! die, + Pid1 ! die, + receive {Pid1, MonitoredBy1} -> [] = MonitoredBy1 end, + receive {Pid2, MonitoredBy2} -> [_] = MonitoredBy2 end, + ?line _ = global:unregister_name(name), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +proc(Parent) -> + receive die -> ok end, + {monitored_by, MonitoredBy} = process_info(self(), monitored_by), + Parent ! {self(), MonitoredBy}. + + +%%%----------------------------------------------------------------- +%%% +%%%----------------------------------------------------------------- +name_exit(suite) -> []; +name_exit(doc) -> ["OTP-5563. Registered process dies."]; +name_exit(Config) when is_list(Config) -> + case ?t:is_release_available("r11b") of + true -> + StartOldFun = + fun() -> + {ok, N1} = start_node_rel(n_1, r11b, Config), + {ok, N2} = start_node_rel(n_2, this, Config), + [N1, N2] + end, + ?t:format("Test of r11~n"), + do_name_exit(StartOldFun, old, Config); + false -> + ok + end, + StartFun = fun() -> + {ok, N1} = start_node_rel(n_1, this, Config), + {ok, N2} = start_node_rel(n_2, this, Config), + [N1, N2] + end, + ?t:format("Test of current release~n"), + do_name_exit(StartFun, current, Config). + +do_name_exit(StartFun, Version, Config) -> + %% When a registered process dies, the node where it is registered + %% removes the name from the table immediately, and then removes + %% it from other nodes using a lock. + %% This is perhaps not how it should work, but it is not easy to + %% change. + %% See also OTP-3737. + %% + %% The current release uses monitors so this test is not so relevant. + + Timeout = 60, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + %% Three nodes (test_server, n_1, n_2). + ?line Cps = StartFun(), + Nodes = lists:sort([node() | Cps]), + ?line wait_for_ready_net(Config), + lists:foreach(fun(N) -> rpc:call(N, ?MODULE, start_tracer, []) end,Nodes), + + Name = name, + ?line {Pid, yes} = start_proc(Name), + + Me = self(), + LL = spawn(fun() -> long_lock(Me) end), + receive + long_lock_taken -> ok + end, + + Pid ! die, + wait_for_exit_fast(Pid), + + ?t:sleep(100), + %% Name has been removed from node()'s table, but nowhere else + %% since there is a lock on 'global'. + {R1,[]} = rpc:multicall(Nodes, global, whereis_name, [Name]), + ?line case Version of + old -> [_,_] = lists:usort(R1); + current -> [undefined, undefined, undefined] = R1 + end, + ?t:sleep(3000), + ?line check_everywhere(Nodes, Name, Config), + + lists:foreach(fun(N) -> rpc:call(N, ?MODULE, stop_tracer, []) end, Nodes), + ?line OrigNames = global:registered_names(), + exit(LL, kill), + write_high_level_trace(Config), + stop_nodes(Cps), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +long_lock(Parent) -> + global:trans({?GLOBAL_LOCK,self()}, + fun() -> + Parent ! long_lock_taken, + timer:sleep(3000) + end). + +%%%----------------------------------------------------------------- +%%% Testing the support for external nodes (cnodes) +%%%----------------------------------------------------------------- +external_nodes(suite) -> []; +external_nodes(doc) -> ["OTP-5563. External nodes (cnodes)."]; +external_nodes(Config) when is_list(Config) -> + Timeout = 30, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + ?line [NodeB, NodeC] = start_nodes([b, c], peer, Config), + ?line wait_for_ready_net(Config), + + %% Nodes = ?NODES, + %% lists:foreach(fun(N) -> rpc:call(N, ?MODULE, start_tracer, []) end, + %% Nodes), + Name = name, + + %% Two partitions: [test_server] and [b, c]. + %% c registers an external name on b + ?line rpc_cast(NodeB, ?MODULE, part_ext, + [Config, node(), NodeC, Name]), + ?line ?UNTIL(is_ready_partition(Config)), + + ?line pong = net_adm:ping(NodeB), + ?line ?UNTIL([NodeB, NodeC] =:= lists:sort(nodes())), + ?line wait_for_ready_net(Config), + + ?line Cpid = rpc:call(NodeC, erlang, whereis, [Name]), + ExternalName = [{name,Cpid,NodeB}], + ?line ExternalName = get_ext_names(), + ?line ExternalName = rpc:call(NodeB, gen_server, call, + [global_name_server, get_names_ext]), + ?line ExternalName = rpc:call(NodeC, gen_server, call, + [global_name_server, get_names_ext]), + + ?line [_] = cnode_links(Cpid), + ?line [_,_,_] = cnode_monitored_by(Cpid), + ?line no = global:register_name(Name, self()), + ?line yes = global:re_register_name(Name, self()), + ?line ?UNTIL([] =:= cnode_monitored_by(Cpid)), + ?line ?UNTIL([] =:= cnode_links(Cpid)), + ?line [] = gen_server:call(global_name_server, get_names_ext, infinity), + + ?line Cpid ! {register, self(), Name}, + ?line receive {Cpid, Reply1} -> no = Reply1 end, + ?line _ = global:unregister_name(Name), + test_server:sleep(1000), + ?line Cpid ! {register, self(), Name}, + ?line ?UNTIL(length(get_ext_names()) =:= 1), + ?line receive {Cpid, Reply2} -> yes = Reply2 end, + + ?line Cpid ! {unregister, self(), Name}, + ?line ?UNTIL(length(get_ext_names()) =:= 0), + ?line receive {Cpid, Reply3} -> ok = Reply3 end, + + Cpid ! die, + ?line ?UNTIL(OrigNames =:= global:registered_names()), + ?line [] = get_ext_names(), + ?line [] = rpc:call(NodeB, gen_server, call, + [global_name_server, get_names_ext]), + ?line [] = rpc:call(NodeC, gen_server, call, + [global_name_server, get_names_ext]), + + ?line Cpid2 = erlang:spawn(NodeC, fun() -> cnode_proc(NodeB) end), + ?line Cpid2 ! {register, self(), Name}, + ?line receive {Cpid2, Reply4} -> yes = Reply4 end, + + %% It could be a bug that Cpid2 is linked to 'global_name_server' + %% at node 'b'. The effect: Cpid2 dies when node 'b' crashes. + stop_node(NodeB), + ?line ?UNTIL(OrigNames =:= global:registered_names()), + ?line [] = get_ext_names(), + ?line [] = rpc:call(NodeC, gen_server, call, + [global_name_server, get_names_ext]), + + %% ?line {_, Trace} = collect_tracers(Nodes), + %% lists:foreach(fun(M) -> erlang:display(M) end, Trace), + + ThisNode = node(), + ?line Cpid3 = erlang:spawn(NodeC, fun() -> cnode_proc(ThisNode) end), + ?line Cpid3 ! {register, self(), Name}, + ?line receive {Cpid3, Reply5} -> yes = Reply5 end, + + ?line ?UNTIL(length(get_ext_names()) =:= 1), + stop_node(NodeC), + ?line ?UNTIL(length(get_ext_names()) =:= 0), + + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +get_ext_names() -> + gen_server:call(global_name_server, get_names_ext, infinity). + +%% Runs at B +part_ext(Config, Main, C, Name) -> + make_partition(Config, [Main], [node(), C]), + ThisNode = node(), + Pid = erlang:spawn(C, fun() -> cnode_proc(ThisNode) end), + Pid ! {register, self(), Name}, + receive {Pid, Reply} -> yes = Reply end, + rpc:call(C, erlang, register, [Name, Pid]). + +cnode_links(Pid) -> + Pid ! {links, self()}, + receive + {links, Links} -> + Links + end. + +cnode_monitored_by(Pid) -> + Pid ! {monitored_by, self()}, + receive + {monitored_by, MonitoredBy} -> + MonitoredBy + end. + +cnode_proc(E) -> + receive + {register, From, Name} -> + Rep = rpc:call(E, global, register_name_external, [Name, self()]), + From ! {self(), Rep}; + {unregister, From, Name} -> + _ = rpc:call(E, global, unregister_name_external, [Name]), + From ! {self(), ok}; + {links, From} -> + From ! process_info(self(), links); + {monitored_by, From} -> + From ! process_info(self(), monitored_by); + die -> + exit(normal) + end, + cnode_proc(E). + + +many_nodes(suite) -> + []; +many_nodes(doc) -> + ["OTP-5770. Start many nodes. Make them connect at the same time."]; +many_nodes(Config) when is_list(Config) -> + Timeout = 180, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + + {Rels, N_cps} = + case ?t:os_type() of + {unix, Osname} when Osname =:= linux; + Osname =:= openbsd; + Osname =:= darwin -> + N_nodes = quite_a_few_nodes(32), + {node_rel(1, N_nodes, this), N_nodes}; + {unix, _} -> + case ?t:is_release_available("r11b") of + true -> + This = node_rel(1, 16, this), + R11B = node_rel(17, 32, r11b), + {This ++ R11B, 32}; + false -> + {node_rel(1, 32, this), 32} + end; + _ -> + {node_rel(1, 32, this), 32} + end, + ?line Cps = [begin {ok, Cp} = start_node_rel(Name, Rel, Config), Cp end || + {Name,Rel} <- Rels], + Nodes = lists:sort(?NODES), + ?line wait_for_ready_net(Nodes, Config), + + ?line Dir = ?config(priv_dir, Config), + GoFile = filename:join([Dir, "go.txt"]), + file:delete(GoFile), + + CpsFiles = [{N, filename:join([Dir, atom_to_list(N)++".node"])} || + N <- Cps], + IsoFun = + fun({N, File}) -> + file:delete(File), + rpc_cast(N, ?MODULE, isolated_node, [File, GoFile, Cps, Config]) + end, + ?line lists:foreach(IsoFun, CpsFiles), + + ?line all_nodes_files(CpsFiles, "isolated", Config), + ?line Time = msec(), + ?line sync_until(), + erlang:display(ready_to_go), + ?line touch(GoFile, "go"), + ?line all_nodes_files(CpsFiles, "done", Config), + ?line Time2 = msec(), + + ?line lists:foreach(fun(N) -> pong = net_adm:ping(N) end, Cps), + + ?line wait_for_ready_net(Config), + + write_high_level_trace(Config), % The test succeeded, but was it slow? + + ?line lists:foreach(fun({_N, File}) -> file:delete(File) end, CpsFiles), + ?line file:delete(GoFile), + + ?line ?UNTIL(OrigNames =:= global:registered_names()), + write_high_level_trace(Config), + ?line stop_nodes(Cps), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + Diff = Time2 - Time, + Return = lists:flatten(io_lib:format("~w nodes took ~w ms", + [N_cps, Diff])), + erlang:display({{nodes,N_cps},{time,Diff}}), + ?t:format("~s~n", [Return]), + {comment, Return}. + +node_rel(From, To, Rel) -> + [{lists:concat([cp, N]), Rel} || N <- lists:seq(From, To)]. + +isolated_node(File, GoFile, Nodes, Config) -> + Ns = lists:sort(Nodes), + exit(erlang:whereis(user), kill), + touch(File, "start_isolated"), + NodesList = nodes(), + append_to_file(File, [{nodes,Nodes},{nodes_list,NodesList}]), + Replies = + lists:map(fun(N) -> _ = erlang:disconnect_node(N) end, NodesList), + append_to_file(File, {replies,Replies}), + ?UNTIL(begin + Known = get_known(node()), + append_to_file(File, {known,Known}), + Known =:= [node()] + end), + touch(File, "isolated"), + sync_until(File), + file_contents(GoFile, "go", Config, File), + touch(File, "got_go"), + lists:foreach(fun(N) -> _ = net_adm:ping(N) end, shuffle(Nodes)), + touch(File, "pinged"), + ?line ?UNTIL((Ns -- get_known(node())) =:= []), + touch(File, "done"). + +touch(File, List) -> + ok = file:write_file(File, list_to_binary(List)). + +append_to_file(File, Term) -> + {ok, Fd} = file:open(File, [raw,binary,append]), + ok = file:write(Fd, io_lib:format("~p.~n", [Term])), + ok = file:close(Fd). + +all_nodes_files(CpsFiles, ContentsList, Config) -> + lists:all(fun({_N,File}) -> + file_contents(File, ContentsList, Config) + end, CpsFiles). + +file_contents(File, ContentsList, Config) -> + file_contents(File, ContentsList, Config, no_log_file). + +file_contents(File, ContentsList, Config, LogFile) -> + Contents = list_to_binary(ContentsList), + Sz = size(Contents), + ?UNTIL(begin + case file:read_file(File) of + {ok, FileContents}=Reply -> + case catch split_binary(FileContents, Sz) of + {Contents,_} -> + true; + _ -> + catch append_to_file(LogFile, + {File,Contents,Reply}), + false + end; + Reply -> + catch append_to_file(LogFile, {File, Contents, Reply}), + false + end + end). + +sync_until() -> + sync_until(no_log_file). + +sync_until(LogFile) -> + Time = ?UNTIL_LOOP - (msec(now()) rem ?UNTIL_LOOP), + catch append_to_file(LogFile, {sync_until, Time}), + timer:sleep(Time). + +shuffle(L) -> + [E || {_, E} <- lists:keysort(1, [{random:uniform(), E} || E <- L])]. + +sync_0(suite) -> []; +sync_0(doc) -> + ["OTP-5770. sync/0."]; +sync_0(Config) when is_list(Config) -> + Timeout = 180, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + + N_cps = + case ?t:os_type() of + {unix, Osname} when Osname =:= linux; + Osname =:= openbsd; + Osname =:= darwin -> + quite_a_few_nodes(30); + {unix, sunos} -> + 30; + {unix, _} -> + 16; + _ -> + 30 + end, + + Names = [lists:concat([cp,N]) || N <- lists:seq(1, N_cps)], + Cps = start_and_sync(Names), + ?line wait_for_ready_net(Config), + write_high_level_trace(Config), + stop_nodes(Cps), + + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +start_and_sync([]) -> + []; +start_and_sync([Name | Names]) -> + ?line {ok, N} = start_node(Name, slave, []), + ?line {Time, _Void} = rpc:call(N, timer, tc, [global, sync, []]), + ?t:format("~p: ~p~n", [Name, Time]), + [N | start_and_sync(Names)]. + +%%%----------------------------------------------------------------- +%%% Testing of change of global_groups parameter. +%%%----------------------------------------------------------------- +global_groups_change(suite) -> []; +global_groups_change(doc) -> ["Test change of global_groups parameter."]; +global_groups_change(Config) -> + Timeout = 90, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line M = from($@, atom_to_list(node())), + + % Create the .app files and the boot script + ?line {KernelVer, StdlibVer} = create_script_dc("dc"), + ?line case is_real_system(KernelVer, StdlibVer) of + true -> + Options = []; + false -> + Options = [local] + end, + + ?line ok = systools:make_script("dc", Options), + + [Ncp1,Ncp2,Ncp3,Ncp4,Ncp5,NcpA,NcpB,NcpC,NcpD,NcpE] = + node_names([cp1,cp2,cp3,cp4,cp5,cpA,cpB,cpC,cpD,cpE], Config), + + % Write config files + ?line Dir = ?config(priv_dir,Config), + ?line {ok, Fd_dc} = file:open(filename:join(Dir, "sys.config"), [write]), + ?line config_dc1(Fd_dc, Ncp1, Ncp2, Ncp3, NcpA, NcpB, NcpC, NcpD, NcpE), + ?line file:close(Fd_dc), + ?line Config1 = filename:join(Dir, "sys"), + + % Test [cp1, cp2, cp3] + ?line {ok, Cp1} = start_node_boot(Ncp1, Config1, dc), + ?line {ok, Cp2} = start_node_boot(Ncp2, Config1, dc), + ?line {ok, Cp3} = start_node_boot(Ncp3, Config1, dc), + ?line {ok, CpA} = start_node_boot(NcpA, Config1, dc), + ?line {ok, CpB} = start_node_boot(NcpB, Config1, dc), + ?line {ok, CpC} = start_node_boot(NcpC, Config1, dc), + ?line {ok, CpD} = start_node_boot(NcpD, Config1, dc), + ?line {ok, CpE} = start_node_boot(NcpE, Config1, dc), + + ?line pong = rpc:call(Cp1, net_adm, ping, [Cp2]), + ?line pong = rpc:call(Cp1, net_adm, ping, [Cp3]), + ?line pang = rpc:call(Cp1, net_adm, ping, + [list_to_atom(lists:concat(["cp5@", M]))]), + ?line pong = rpc:call(Cp2, net_adm, ping, [Cp3]), + ?line pang = rpc:call(Cp2, net_adm, ping, + [list_to_atom(lists:concat(["cp5@", M]))]), + + ?line {TestGG4, yes} = rpc:call(CpB, ?MODULE, start_proc, [test]), + ?line {TestGG5, yes} = rpc:call(CpE, ?MODULE, start_proc, [test]), + + + ?line pong = rpc:call(CpA, net_adm, ping, [CpC]), + ?line pong = rpc:call(CpC, net_adm, ping, [CpB]), + ?line pong = rpc:call(CpD, net_adm, ping, [CpC]), + ?line pong = rpc:call(CpE, net_adm, ping, [CpD]), + + ?line + ?UNTIL(begin + TestGG4_1 = rpc:call(CpA, global, whereis_name, [test]), + TestGG4_2 = rpc:call(CpB, global, whereis_name, [test]), + TestGG4_3 = rpc:call(CpC, global, whereis_name, [test]), + + TestGG5_1 = rpc:call(CpD, global, whereis_name, [test]), + TestGG5_2 = rpc:call(CpE, global, whereis_name, [test]), + io:format("~p~n", [[TestGG4, TestGG4_1, TestGG4_2,TestGG4_3]]), + io:format("~p~n", [[TestGG5, TestGG5_1, TestGG5_2]]), + (TestGG4_1 =:= TestGG4) and + (TestGG4_2 =:= TestGG4) and + (TestGG4_3 =:= TestGG4) and + (TestGG5_1 =:= TestGG5) and + (TestGG5_2 =:= TestGG5) + end), + + ?line ?t:format( "#### nodes() ~p~n",[nodes()]), + + ?line XDcWa1 = rpc:call(Cp1, global_group, info, []), + ?line XDcWa2 = rpc:call(Cp2, global_group, info, []), + ?line XDcWa3 = rpc:call(Cp3, global_group, info, []), + ?line ?t:format( "#### XDcWa1 ~p~n",[XDcWa1]), + ?line ?t:format( "#### XDcWa2 ~p~n",[XDcWa2]), + ?line ?t:format( "#### XDcWa3 ~p~n",[XDcWa3]), + + ?line stop_node(CpC), + + %% Read the current configuration parameters, and change them + ?line OldEnv = + rpc:call(Cp1, application_controller, prep_config_change, []), + ?line {value, {kernel, OldKernel}} = lists:keysearch(kernel, 1, OldEnv), + + ?line GG1 = + lists:sort([mk_node(Ncp1, M), mk_node(Ncp2, M), mk_node(Ncp5, M)]), + ?line GG2 = lists:sort([mk_node(Ncp3, M)]), + ?line GG3 = lists:sort([mk_node(Ncp4, M)]), + ?line GG4 = lists:sort([mk_node(NcpA, M), mk_node(NcpB, M)]), + ?line GG5 = + lists:sort([mk_node(NcpC, M), mk_node(NcpD, M), mk_node(NcpE, M)]), + + ?line NewNG = {global_groups,[{gg1, normal, GG1}, + {gg2, normal, GG2}, + {gg3, normal, GG3}, + {gg4, normal, GG4}, + {gg5, hidden, GG5}]}, + + ?line NewKernel = + [{kernel, lists:keyreplace(global_groups, 1, OldKernel, NewNG)}], + ?line ok = rpc:call(Cp1, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + ?line ok = rpc:call(Cp2, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + ?line ok = rpc:call(Cp3, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + ?line ok = rpc:call(CpA, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + ?line ok = rpc:call(CpB, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + ?line ok = rpc:call(CpD, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + ?line ok = rpc:call(CpE, application_controller, test_change_apps, + [[kernel], [NewKernel]]), + + ?line ?t:format("#### ~p~n",[multicall]), + ?line ?t:format( "#### ~p~n",[multicall]), + %% no idea to check the result from the rpc because the other + %% nodes will disconnect test server, and thus the result will + %% always be {badrpc, nodedown} + ?line rpc:multicall([Cp1, Cp2, Cp3, CpA, CpB, CpD, CpE], + application_controller, config_change, [OldEnv]), + + ?line {ok, Fd_dc2} = file:open(filename:join(Dir, "sys2.config"), [write]), + ?line config_dc2(Fd_dc2, NewNG, Ncp1, Ncp2, Ncp3), + ?line file:close(Fd_dc2), + ?line Config2 = filename:join(Dir, "sys2"), + ?line {ok, CpC} = start_node_boot(NcpC, Config2, dc), + + ?line sync_and_wait(CpA), + ?line sync_and_wait(CpD), + + ?line pong = rpc:call(CpA, net_adm, ping, [CpC]), + ?line pong = rpc:call(CpC, net_adm, ping, [CpB]), + ?line pong = rpc:call(CpD, net_adm, ping, [CpC]), + ?line pong = rpc:call(CpE, net_adm, ping, [CpD]), + + ?line GG5 = + lists:sort([mk_node(NcpC, M)|rpc:call(CpC, erlang, nodes, [])]), + ?line GG5 = + lists:sort([mk_node(NcpD, M)|rpc:call(CpD, erlang, nodes, [])]), + ?line GG5 = + lists:sort([mk_node(NcpE, M)|rpc:call(CpE, erlang, nodes, [])]), + + ?line false = + lists:member(mk_node(NcpC, M), rpc:call(CpA, erlang, nodes, [])), + ?line false = + lists:member(mk_node(NcpC, M), rpc:call(CpB, erlang, nodes, [])), + + ?line + ?UNTIL(begin + TestGG4a = rpc:call(CpA, global, whereis_name, [test]), + TestGG4b = rpc:call(CpB, global, whereis_name, [test]), + + TestGG5c = rpc:call(CpC, global, whereis_name, [test]), + TestGG5d = rpc:call(CpD, global, whereis_name, [test]), + TestGG5e = rpc:call(CpE, global, whereis_name, [test]), + io:format("~p~n", [[TestGG4, TestGG4a, TestGG4b]]), + io:format("~p~n", [[TestGG5, TestGG5c, TestGG5d, TestGG5e]]), + (TestGG4 =:= TestGG4a) and + (TestGG4 =:= TestGG4b) and + (TestGG5 =:= TestGG5c) and + (TestGG5 =:= TestGG5d) and + (TestGG5 =:= TestGG5e) + end), + + ?line Info1 = rpc:call(Cp1, global_group, info, []), + ?line Info2 = rpc:call(Cp2, global_group, info, []), + ?line Info3 = rpc:call(Cp3, global_group, info, []), + ?line InfoA = rpc:call(CpA, global_group, info, []), + ?line InfoB = rpc:call(CpB, global_group, info, []), + ?line InfoC = rpc:call(CpC, global_group, info, []), + ?line InfoD = rpc:call(CpD, global_group, info, []), + ?line InfoE = rpc:call(CpE, global_group, info, []), + ?line ?t:format( "#### Info1 ~p~n",[Info1]), + ?line ?t:format( "#### Info2 ~p~n",[Info2]), + ?line ?t:format( "#### Info3 ~p~n",[Info3]), + ?line ?t:format( "#### InfoA ~p~n",[InfoA]), + ?line ?t:format( "#### InfoB ~p~n",[InfoB]), + ?line ?t:format( "#### InfoC ~p~n",[InfoC]), + ?line ?t:format( "#### InfoD ~p~n",[InfoD]), + ?line ?t:format( "#### InfoE ~p~n",[InfoE]), + + ?line {global_groups, GGNodes} = NewNG, + + ?line Info1ok = [{state, synced}, + {own_group_name, gg1}, + {own_group_nodes, GG1}, + {synced_nodes, [mk_node(Ncp2, M)]}, + {sync_error, []}, + {no_contact, [mk_node(Ncp5, M)]}, + {other_groups, remove_gg_pub_type(lists:keydelete + (gg1, 1, GGNodes))}, + {monitoring, []}], + + + ?line Info2ok = [{state, synced}, + {own_group_name, gg1}, + {own_group_nodes, GG1}, + {synced_nodes, [mk_node(Ncp1, M)]}, + {sync_error, []}, + {no_contact, [mk_node(Ncp5, M)]}, + {other_groups, remove_gg_pub_type(lists:keydelete + (gg1, 1, GGNodes))}, + {monitoring, []}], + + ?line Info3ok = [{state, synced}, + {own_group_name, gg2}, + {own_group_nodes, GG2}, + {synced_nodes, []}, + {sync_error, []}, + {no_contact, []}, + {other_groups, remove_gg_pub_type(lists:keydelete + (gg2, 1, GGNodes))}, + {monitoring, []}], + + ?line InfoAok = [{state, synced}, + {own_group_name, gg4}, + {own_group_nodes, GG4}, + {synced_nodes, lists:delete(mk_node(NcpA, M), GG4)}, + {sync_error, []}, + {no_contact, []}, + {other_groups, remove_gg_pub_type(lists:keydelete + (gg4, 1, GGNodes))}, + {monitoring, []}], + + ?line InfoBok = [{state, synced}, + {own_group_name, gg4}, + {own_group_nodes, GG4}, + {synced_nodes, lists:delete(mk_node(NcpB, M), GG4)}, + {sync_error, []}, + {no_contact, []}, + {other_groups, remove_gg_pub_type(lists:keydelete + (gg4, 1, GGNodes))}, + {monitoring, []}], + + ?line InfoCok = [{state, synced}, + {own_group_name, gg5}, + {own_group_nodes, GG5}, + {synced_nodes, lists:delete(mk_node(NcpC, M), GG5)}, + {sync_error, []}, + {no_contact, []}, + {other_groups, remove_gg_pub_type(lists:keydelete + (gg5, 1, GGNodes))}, + {monitoring, []}], + + ?line InfoDok = [{state, synced}, + {own_group_name, gg5}, + {own_group_nodes, GG5}, + {synced_nodes, lists:delete(mk_node(NcpD, M), GG5)}, + {sync_error, []}, + {no_contact, []}, + {other_groups, remove_gg_pub_type(lists:keydelete + (gg5, 1, GGNodes))}, + {monitoring, []}], + + ?line InfoEok = [{state, synced}, + {own_group_name, gg5}, + {own_group_nodes, GG5}, + {synced_nodes, lists:delete(mk_node(NcpE, M), GG5)}, + {sync_error, []}, + {no_contact, []}, + {other_groups, remove_gg_pub_type(lists:keydelete + (gg5, 1, GGNodes))}, + {monitoring, []}], + + + ?line case Info1 of + Info1ok -> + ok; + _ -> + test_server:fail({{"could not change the global groups" + " in node", Cp1}, {Info1, Info1ok}}) + end, + + ?line case Info2 of + Info2ok -> + ok; + _ -> + test_server:fail({{"could not change the global groups" + " in node", Cp2}, {Info2, Info2ok}}) + end, + + ?line case Info3 of + Info3ok -> + ok; + _ -> + test_server:fail({{"could not change the global groups" + " in node", Cp3}, {Info3, Info3ok}}) + end, + + ?line case InfoA of + InfoAok -> + ok; + _ -> + test_server:fail({{"could not change the global groups" + " in node", CpA}, {InfoA, InfoAok}}) + end, + + ?line case InfoB of + InfoBok -> + ok; + _ -> + test_server:fail({{"could not change the global groups" + " in node", CpB}, {InfoB, InfoBok}}) + end, + + + ?line case InfoC of + InfoCok -> + ok; + _ -> + test_server:fail({{"could not change the global groups" + " in node", CpC}, {InfoC, InfoCok}}) + end, + + ?line case InfoD of + InfoDok -> + ok; + _ -> + test_server:fail({{"could not change the global groups" + " in node", CpD}, {InfoD, InfoDok}}) + end, + + ?line case InfoE of + InfoEok -> + ok; + _ -> + test_server:fail({{"could not change the global groups" + " in node", CpE}, {InfoE, InfoEok}}) + end, + + write_high_level_trace(Config), % no good since CpC was restarted + stop_node(Cp1), + stop_node(Cp2), + stop_node(Cp3), + stop_node(CpA), + stop_node(CpB), + stop_node(CpC), + stop_node(CpD), + stop_node(CpE), + + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +sync_and_wait(Node) -> + Ref = make_ref(), + Self = self(), + spawn(Node, fun () -> + global_group:sync(), + case whereis(global_group_check) of + P when is_pid(P) -> + Self ! {Ref, P}; + _ -> + Self ! {Ref, done} + end + end), + receive + {Ref, P} when is_pid(P) -> + MonRef = erlang:monitor(process, P), + receive + {'DOWN',MonRef,process,P,_} -> + ok + end; + {Ref, _} -> + ok + end. + +%%% Copied from init_SUITE.erl. +is_real_system(KernelVsn, StdlibVsn) -> + LibDir = code:lib_dir(), + filelib:is_dir(filename:join(LibDir, "kernel-" ++ KernelVsn)) + andalso + filelib:is_dir(filename:join(LibDir, "stdlib-" ++ StdlibVsn)). + +create_script_dc(ScriptName) -> + ?line Name = filename:join(".", ScriptName), + ?line Apps = application_controller:which_applications(), + ?line {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + ?line {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + ?line {ok,Fd} = file:open(Name ++ ".rel", [write]), + ?line {_, Version} = init:script_id(), + ?line io:format(Fd, + "{release, {\"Test release 3\", \"~s\"}, \n" + " {erts, \"4.4\"}, \n" + " [{kernel, \"~s\"}, {stdlib, \"~s\"}]}.\n", + [Version, KernelVer, StdlibVer]), + ?line file:close(Fd), + {KernelVer, StdlibVer}. + +%% Not used? +config_dc(Fd, Ncp1, Ncp2, Ncp3) -> + M = from($@, atom_to_list(node())), + io:format(Fd, "[{kernel, [{sync_nodes_optional, ['~s@~s','~s@~s','~s@~s']}," + "{sync_nodes_timeout, 1000}," + "{global_groups, [{gg1, ['~s@~s', '~s@~s']}," + " {gg2, ['~s@~s']}]}" + " ]}].~n", + [Ncp1, M, Ncp2, M, Ncp3, M, Ncp1, M, Ncp2, M, Ncp3, M]). + + +config_dc1(Fd, Ncp1, Ncp2, Ncp3, NcpA, NcpB, NcpC, NcpD, NcpE) -> + M = from($@, atom_to_list(node())), + io:format(Fd, "[{kernel, [{sync_nodes_optional, ['~s@~s','~s@~s','~s@~s','~s@~s','~s@~s','~s@~s','~s@~s','~s@~s']}," + "{sync_nodes_timeout, 1000}," + "{global_groups, [{gg1, ['~s@~s', '~s@~s']}," + " {gg2, ['~s@~s']}," + " {gg4, normal, ['~s@~s','~s@~s','~s@~s']}," + " {gg5, hidden, ['~s@~s','~s@~s']}]}]}].~n", + [Ncp1, M, Ncp2, M, Ncp3, M, + NcpA, M, NcpB, M, NcpC, M, NcpD, M, NcpE, M, + Ncp1, M, Ncp2, M, + Ncp3, M, + NcpA, M, NcpB, M, NcpC, M, + NcpD, M, NcpE, M]). + +config_dc2(Fd, NewGG, Ncp1, Ncp2, Ncp3) -> + M = from($@, atom_to_list(node())), + io:format(Fd, "[{kernel, [{sync_nodes_optional, ['~s@~s','~s@~s','~s@~s']}," + "{sync_nodes_timeout, 1000}," + "~p]}].~n", + [Ncp1, M, Ncp2, M, Ncp3, M, NewGG]). + + +from(H, [H | T]) -> T; +from(H, [_ | T]) -> from(H, T); +from(_H, []) -> []. + + + +other(A, [A, _B]) -> A; +other(_, [_A, B]) -> B. + + +%% this one runs at cp2 +part1(Config, Main, Cp1, Cp3) -> + case catch begin + make_partition(Config, [Main, Cp1], [node(), Cp3]), + ?line {_Pid, yes} = start_proc(test2), + ?line {_Pid2, yes} = start_proc(test4) + end of + {_, yes} -> ok; % w("ok", []); + {'EXIT', _R} -> + ok + % w("global_SUITE line:~w: ~p", [?LINE, _R]) + end. + +%% Runs at Cp2 +part1_5(Config, Main, Cp1, Cp3) -> + case catch begin + make_partition(Config, [Main, Cp1], [node(), Cp3]), + ?line {_Pid1, yes} = start_proc_basic(name12), + ?line {_Pid2, yes} = + rpc:call(Cp3, ?MODULE, start_proc_basic, [name03]) + end of + {_, yes} -> ok; % w("ok", []); + {'EXIT', _R} -> + ok + % w("global_SUITE line:~w: ~p", [?LINE, _R]) + end. + +w(X,Y) -> + {ok, F} = file:open("cp2.log", [write]), + io:format(F, X, Y), + file:close(F). + +%% this one runs on one node in Part2 +%% The partition is ready when is_ready_partition(Config) returns (true). +make_partition(Config, Part1, Part2) -> + Dir = ?config(priv_dir, Config), + Ns = [begin + Name = lists:concat([atom_to_list(N),"_",msec(),".part"]), + File = filename:join([Dir, Name]), + file:delete(File), + rpc_cast(N, ?MODULE, mk_part_node, [File, Part, Config], File), + {N, File} + end || Part <- [Part1, Part2], N <- Part], + all_nodes_files(Ns, "done", Config), + lists:foreach(fun({_N,File}) -> file:delete(File) end, Ns), + PartFile = make_partition_file(Config), + touch(PartFile, "done"). + +%% The node signals its success by touching a file. +mk_part_node(File, MyPart0, Config) -> + touch(File, "start"), % debug + MyPart = lists:sort(MyPart0), + ?UNTIL(is_node_in_part(File, MyPart)), + touch(File, "done"). + +%% The calls to append_to_file are for debugging. +is_node_in_part(File, MyPart) -> + lists:foreach(fun(N) -> + _ = erlang:disconnect_node(N) + end, nodes() -- MyPart), + case {(Known = get_known(node())) =:= MyPart, + (Nodes = lists:sort([node() | nodes()])) =:= MyPart} of + {true, true} -> + %% Make sure the resolvers have been terminated, + %% otherwise they may pop up and send some message. + %% (This check is probably unnecessary.) + case element(5, global:info()) of + [] -> + true; + Rs -> + erlang:display({is_node_in_part, resolvers, Rs}), + trace_message({node(), is_node_in_part, Rs}), + append_to_file(File, {now(), Known, Nodes, Rs}), + false + end; + _ -> + append_to_file(File, {now(), Known, Nodes}), + false + end. + +is_ready_partition(Config) -> + File = make_partition_file(Config), + file_contents(File, "done", Config), + file:delete(File), + true. + +make_partition_file(Config) -> + Dir = ?config(priv_dir, Config), + filename:join([Dir, atom_to_list(make_partition_done)]). + +%% this one runs at cp3 +part2(Config, Parent, Main, Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6) -> + make_partition(Config, [Main, Cp0, Cp1, Cp2], [Cp3, Cp4, Cp5, Cp6]), + start_procs(Parent, Cp4, Cp5, Cp6, Config). + +part3(Config, Parent, Main, Cp0, Cp1, Cp2, Cp3, Cp4, Cp5, Cp6) -> + make_partition(Config, [Main, Cp0, Cp1, Cp2], [Cp3, Cp4, Cp5, Cp6]), + start_procs(Parent, Cp4, Cp5, Cp6, Config), + % Make Cp6 alone + ?line rpc_cast(Cp5, ?MODULE, crash, [12000]), + ?line rpc_cast(Cp6, ?MODULE, alone, [Cp0, Cp3]). + +start_procs(Parent, N1, N2, N3, Config) -> + S1 = lists:sort([N1, N2, N3]), + ?line + ?UNTIL(begin + NN = lists:sort(nodes()), + S1 =:= NN + end), + ?line Pid3 = start_proc3(test1), + ?line Pid4 = rpc:call(N1, ?MODULE, start_proc3, [test2]), + ?line assert_pid(Pid4), + ?line Pid5 = rpc:call(N2, ?MODULE, start_proc3, [test3]), + ?line assert_pid(Pid5), + ?line Pid6 = rpc:call(N3, ?MODULE, start_proc3, [test4]), + ?line assert_pid(Pid6), + ?line yes = global:register_name(test1, Pid3), + ?line yes = global:register_name(test2, Pid4, {global, notify_all_name}), + ?line yes = global:register_name(test3, Pid5, {global, random_notify_name}), + Resolve = fun(Name, Pid1, Pid2) -> + Parent ! {resolve_called, Name, node()}, + {Min, Max} = minmax(Pid1, Pid2), + exit(Min, kill), + Max + end, + ?line yes = global:register_name(test4, Pid6, Resolve). + + + +collect_resolves() -> cr(0). +cr(Res) -> + receive + {resolve_called, Name, Node} -> + io:format("resolve called: ~w ~w~n", [Name, Node]), + cr(Res+1) + after + 0 -> Res + end. + +minmax(P1,P2) -> + if node(P1) < node(P2) -> {P1, P2}; true -> {P2, P1} end. + +fix_basic_name(name03, Pid1, Pid2) -> + case atom_to_list(node(Pid1)) of + [$c, $p, $3|_] -> exit(Pid2, kill), Pid1; + _ -> exit(Pid1, kill), Pid2 + end; +fix_basic_name(name12, Pid1, Pid2) -> + case atom_to_list(node(Pid1)) of + [$c, $p, $2|_] -> exit(Pid2, kill), Pid1; + _ -> exit(Pid1, kill), Pid2 + end. + +start_proc() -> + Pid = spawn(?MODULE, p_init, [self()]), + receive + Pid -> Pid + end. + + +start_proc(Name) -> + Pid = spawn(?MODULE, p_init, [self(), Name]), + receive + {Pid, Res} -> {Pid, Res} + end. + +start_proc2(Name) -> + Pid = spawn(?MODULE, p_init2, [self(), Name]), + receive + Pid -> Pid + end. + +start_proc3(Name) -> + Pid = spawn(?MODULE, p_init, [self()]), + register(Name, Pid), + receive + Pid -> Pid + end. + +start_proc4(Name) -> + Pid = spawn(?MODULE, p_init, [self()]), + yes = global:register_name(Name, Pid), + receive + Pid -> Pid + end. + +start_proc_basic(Name) -> + Pid = spawn(?MODULE, init_proc_basic, [self(), Name]), + receive + {Pid, Res} -> {Pid, Res} + end. + +init_proc_basic(Parent, Name) -> + X = global:register_name(Name, self(), {?MODULE, fix_basic_name}), + Parent ! {self(),X}, + loop(). + +single_node(Time, Node, Config) -> + exit(erlang:whereis(user), kill), + lists:foreach(fun(N) -> _ = erlang:disconnect_node(N) end, nodes()), + ?UNTIL(get_known(node()) =:= [node()]), + spawn(?MODULE, init_2, []), + test_server:sleep(Time - msec()), + net_adm:ping(Node). + +init_2() -> + register(single_name, self()), + yes = global:register_name(single_name, self()), + loop_2(). + +loop_2() -> + receive + die -> ok + end. + +msec() -> + msec(now()). + +msec(T) -> + element(1,T)*1000000000 + element(2,T)*1000 + element(3,T) div 1000. + +assert_pid(Pid) -> + if + is_pid(Pid) -> true; + true -> exit({not_a_pid, Pid}) + end. + +check_same([H|T]) -> check_same(T, H). + +check_same([H|T], H) -> check_same(T, H); +check_same([], _H) -> ok. + +check_same_p([H|T]) -> check_same_p(T, H). + +check_same_p([H|T], H) -> check_same_p(T, H); +check_same_p([], _H) -> true; +check_same_p(_, _) -> false. + +p_init(Parent) -> + Parent ! self(), + loop(). + +p_init(Parent, Name) -> + X = global:register_name(Name, self()), + Parent ! {self(),X}, + loop(). + +p_init2(Parent, Name) -> + _ = global:re_register_name(Name, self()), + Parent ! self(), + loop(). + +req(Pid, Msg) -> + Pid ! Msg, + receive X -> X end. + +sreq(Pid, Msg) -> + Ref = make_ref(), + Pid ! {Msg, Ref}, + receive {Ref, X} -> X end. + +alone(N1, N2) -> + lists:foreach(fun(Node) -> true = erlang:disconnect_node(Node) end, + nodes()), + test_server:sleep(12000), + net_adm:ping(N1), + net_adm:ping(N2), + yes = global:register_name(test5, self()). + +crash(Time) -> + test_server:sleep(Time), + erlang:halt(). + +loop() -> + receive + {ping, From} -> + From ! {pong, node()}, + loop(); + {del_lock, Id} -> + global:del_lock({Id, self()}), + loop(); + {del_lock_sync, Id, From} -> + global:del_lock({Id, self()}), + From ! true, + loop(); + {del_lock, Id, Nodes} -> + global:del_lock({Id, self()}, Nodes), + loop(); + {del_lock2, Id, From} -> + global:del_lock(Id), + From ! true, + loop(); + {del_lock2, Id, From, Nodes} -> + global:del_lock(Id, Nodes), + From ! true, + loop(); + {set_lock, Id, From} -> + Res = global:set_lock({Id, self()}, ?NODES, 1), + From ! Res, + loop(); + {set_lock, Id, From, Nodes} -> + Res = global:set_lock({Id, self()}, Nodes, 1), + From ! Res, + loop(); + {set_lock_loop, Id, From} -> + true = global:set_lock({Id, self()}, ?NODES), + From ! {got_lock, self()}, + loop(); + {set_lock2, Id, From} -> + Res = global:set_lock(Id, ?NODES, 1), + From ! Res, + loop(); + {{got_notify, From}, Ref} -> + receive + X when element(1, X) =:= global_name_conflict -> + From ! {Ref, yes} + after + 0 -> From ! {Ref, no} + end, + loop(); + die -> + exit(normal); + drop_dead -> + exit(drop_dead) + end. + +-ifdef(unused). +pr_diff(Str, T0, T1) -> + Diff = begin + {_, {H,M,S}} = calendar:time_difference(T0, T1), + ((H*60+M)*60)+S + end, + test_server:format(1,"~13s: ~w (diff: ~w)",[Str, T1, Diff]), + if + Diff > 100 -> + test_server:format(1,"~s: ** LARGE DIFF ~w~n", [Str, Diff]); + true -> + ok + end. +-endif. + +now_diff({A1,B1,C1},{A2,B2,C2}) -> + C1-C2 + 1000000*((B1-B2) + 1000000*(A1-A2)). + +start_node_boot(Name, Config, Boot) -> + Pa = filename:dirname(code:which(?MODULE)), + Res = test_server:start_node(Name, peer, [{args, " -pa " ++ Pa ++ + " -config " ++ Config ++ + " -boot " ++ atom_to_list(Boot)}]), + record_started_node(Res). + +%% Increase the timeout for when an upcoming connection is teared down +%% again (default is 7 seconds, and can be exceeded by some tests). +%% The default remains in effect for the test_server node itself, though. +start_node(Name, Config) -> + start_node(Name, slave, Config). + +start_hidden_node(Name, Config) -> + start_node(Name, slave, "-hidden", Config). + +start_non_connecting_node(Name, Config) -> + start_node(Name, slave, "-connect_all false +W i", Config). + +start_peer_node(Name, Config) -> + start_node(Name, peer, Config). + +start_node(Name, How, Config) -> + start_node(Name, How, "", Config). + +start_node(Name0, How, Args, Config) -> + Name = node_name(Name0, Config), + Pa = filename:dirname(code:which(?MODULE)), + R = test_server:start_node(Name, How, [{args, + Args ++ " " ++ + "-kernel net_setuptime 100 " +% "-noshell " + "-pa " ++ Pa}, + {linked, false} +]), + %% {linked,false} only seems to work for slave nodes. +% test_server:sleep(1000), + record_started_node(R). + +start_node_rel(Name0, Rel, Config) -> + Name = node_name(Name0, Config), + {Release, Compat} = case Rel of + this -> + {[this], "+R8"}; + Rel when is_atom(Rel) -> + {[{release, atom_to_list(Rel)}], ""}; + RelList -> + {RelList, ""} + end, + Env = case Rel of + r11b -> + [{env, [{"ERL_R11B_FLAGS", []}]}]; + _ -> + [] + end, + Pa = filename:dirname(code:which(?MODULE)), + Res = test_server:start_node(Name, peer, + [{args, + Compat ++ + " -kernel net_setuptime 100 " + " -pa " ++ Pa}, + {erl, Release}] ++ Env), + record_started_node(Res). + +record_started_node({ok, Node}) -> + case erase(?nodes_tag) of + undefined -> ok; + Nodes -> put(?nodes_tag, [Node | Nodes]) + end, + {ok, Node}; +record_started_node(R) -> + R. + +node_names(Names, Config) -> + [node_name(Name, Config) || Name <- Names]. + +%% simple_resolve assumes that the node name comes first. +node_name(Name, Config) -> + U = "_", + {{Y,M,D}, {H,Min,S}} = calendar:now_to_local_time(now()), + Date = io_lib:format("~4w_~2..0w_~2..0w__~2..0w_~2..0w_~2..0w", + [Y,M,D, H,Min,S]), + L = lists:flatten(Date), + lists:concat([Name,U,?testcase,U,U,L]). + +stop_nodes(Nodes) -> + lists:foreach(fun(Node) -> stop_node(Node) end, Nodes). + +stop_node(Node) -> + ?line ?t:stop_node(Node). + + +stop() -> + lists:foreach(fun(Node) -> + ?t:stop_node(Node) + end, nodes()). + +dbg_logs(Name) -> dbg_logs(Name, ?NODES). + +dbg_logs(Name, Nodes) -> + lists:foreach(fun(N) -> + F = lists:concat([Name, ".log.", N, ".txt"]), + ?line ok = sys:log_to_file({global_name_server, N}, F) + end, Nodes). + + +global_lost_nodes(suite) -> + []; +global_lost_nodes(doc) -> + ["Tests that locally loaded nodes do not loose contact with other nodes."]; +global_lost_nodes(Config) when is_list(Config) -> + Timeout = 60, + Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + + ?line {ok, Node1} = start_node(node1, Config), + ?line {ok, Node2} = start_node(node2, Config), + + ?line wait_for_ready_net(Config), + + ?line io:format("Nodes: ~p", [nodes()]), + ?line io:format("Nodes at node1: ~p", + [rpc:call(Node1, erlang, nodes, [])]), + ?line io:format("Nodes at node2: ~p", + [rpc:call(Node2, erlang, nodes, [])]), + + ?line rpc_cast(Node1, ?MODULE, global_load, [node_1,Node2,node_2]), + ?line rpc_cast(Node2, ?MODULE, global_load, [node_2,Node1,node_1]), + + lost_nodes_waiter(Node1, Node2), + + write_high_level_trace(Config), + ?line stop_node(Node1), + ?line stop_node(Node2), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +global_load(MyName, OtherNode, OtherName) -> + ?line yes = global:register_name(MyName, self()), + io:format("Registered ~p",[MyName]), + global_load1(OtherNode, OtherName, 0). + +global_load1(_OtherNode, _OtherName, 2) -> + io:format("*** ~p giving up. No use.", [node()]), + init:stop(); +global_load1(OtherNode, OtherName, Fails) -> + test_server:sleep(1000), + ?line case catch global:whereis_name(OtherName) of + Pid when is_pid(Pid) -> + io:format("~p says: ~p is still there.", + [node(),OtherName]), + global_load1(OtherNode, OtherName, Fails); + Other -> + io:format("~p says: ~p is lost (~p) Pinging.", + [ node(), OtherName, Other]), + case net_adm:ping(OtherNode) of + pong -> + io:format("Re-established contact to ~p", + [OtherName]); + pang -> + io:format("PANIC! Other node is DEAD.", []), + init:stop() + end, + global_load1(OtherNode, OtherName, Fails+1) + end. + +lost_nodes_waiter(N1, N2) -> + ?line net_kernel:monitor_nodes(true), + receive + {nodedown, Node} when Node =:= N1 ; Node =:= N2 -> + io:format("~p went down!",[Node]), + ?line ?t:fail("Node went down.") + after 10000 -> + ok + end, + ok. + + + +mass_death(suite) -> + []; +mass_death(doc) -> + ["Tests the simultaneous death of many processes with registered names"]; +mass_death(Config) when is_list(Config) -> + Timeout = 90, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line OrigNames = global:registered_names(), + %% Start nodes + ?line Cps = [cp1,cp2,cp3,cp4,cp5], + ?line Nodes = [begin {ok, Node} = start_node(Cp, Config), Node end || + Cp <- Cps], + ?line io:format("Nodes: ~p~n", [Nodes]), + ?line Ns = lists:seq(1, 40), + %% Start processes with globally registered names on the nodes + ?line {Pids,[]} = rpc:multicall(Nodes, ?MODULE, mass_spawn, [Ns]), + ?line io:format("Pids: ~p~n", [Pids]), + %% Wait... + ?line test_server:sleep(10000), + %% Check the globally registered names + ?line NewNames = global:registered_names(), + ?line io:format("NewNames: ~p~n", [NewNames]), + ?line Ndiff = lists:sort(NewNames--OrigNames), + ?line io:format("Ndiff: ~p~n", [Ndiff]), + ?line Ndiff = lists:sort(mass_names(Nodes, Ns)), + %% + %% Kill the root pids + ?line lists:foreach(fun (Pid) -> Pid ! drop_dead end, Pids), + %% Start probing and wait for all registered names to disappear + {YYYY,MM,DD} = date(), + {H,M,S} = time(), + io:format("Started probing: ~.4.0w-~.2.0w-~.2.0w ~.2.0w:~.2.0w:~.2.0w~n", + [YYYY,MM,DD,H,M,S]), + wait_mass_death(Dog, Nodes, OrigNames, erlang:now(), Config). + +wait_mass_death(Dog, Nodes, OrigNames, Then, Config) -> + ?line Names = global:registered_names(), + ?line + case Names--OrigNames of + [] -> + ?line T = now_diff(erlang:now(), Then) div 1000, + ?line lists:foreach( + fun (Node) -> + stop_node(Node) + end, Nodes), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + {comment,lists:flatten(io_lib:format("~.3f s~n", [T/1000.0]))}; + Ndiff -> + ?line io:format("Ndiff: ~p~n", [Ndiff]), + ?line test_server:sleep(1000), + ?line wait_mass_death(Dog, Nodes, OrigNames, Then, Config) + end. + +mass_spawn([]) -> + ok; +mass_spawn([N|T]) -> + Parent = self(), + Pid = + spawn_link( + fun () -> + Name = mass_name(node(), N), + yes = global:register_name(Name, self()), + mass_spawn(T), + Parent ! self(), + loop() + end), + receive Pid -> Pid end. + +mass_names([], _) -> + []; +mass_names([Node|T],Ns) -> + [mass_name(Node, N) || N <- Ns] ++ mass_names(T, Ns). + +mass_name(Node, N) -> + list_to_atom(atom_to_list(Node)++"_"++integer_to_list(N)). + + + +start_nodes(L, How, Config) -> + start_nodes2(L, How, 0, Config), + Nodes = collect_nodes(0, length(L)), + ?line ?UNTIL([] =:= Nodes -- nodes()), + put(?nodes_tag, Nodes), + %% Pinging doesn't help, we have to wait too, for nodes() to become + %% correct on the other node. + lists:foreach(fun(E) -> + net_adm:ping(E) + end, + Nodes), + verify_nodes(Nodes, Config), + Nodes. + +%% Not used? +start_nodes_serially([], _, _Config) -> + []; +start_nodes_serially([Name | Rest], How, Config) -> + {ok, R} = start_node(Name, How, Config), + [R | start_nodes_serially(Rest, How, Config)]. + +verify_nodes(Nodes, Config) -> + verify_nodes(Nodes, lists:sort([node() | Nodes]), Config). + +verify_nodes([], _N, _Config) -> + []; +verify_nodes([Node | Rest], N, Config) -> + ?line ?UNTIL( + case rpc:call(Node, erlang, nodes, []) of + Nodes when is_list(Nodes) -> + case N =:= lists:sort([Node | Nodes]) of + true -> + true; + false -> + lists:foreach(fun(Nd) -> + rpc:call(Nd, net_adm, ping, + [Node]) + end, + nodes()), + false + end; + _ -> + false + end + ), + verify_nodes(Rest, N, Config). + + +start_nodes2([], _How, _, _Config) -> + []; +start_nodes2([Name | Rest], How, N, Config) -> + Self = self(), + spawn(fun() -> + erlang:display({starting, Name}), + {ok, R} = start_node(Name, How, Config), + erlang:display({started, Name, R}), + Self ! {N, R}, + %% sleeping is necessary, or with peer nodes, they will + %% go down again, despite {linked, false}. + test_server:sleep(100000) + end), + start_nodes2(Rest, How, N+1, Config). + +collect_nodes(N, N) -> + []; +collect_nodes(N, Max) -> + receive + {N, Node} -> + [Node | collect_nodes(N+1, Max)] + end. + +only_element(_E, []) -> + true; +only_element(E, [E|R]) -> + only_element(E, R); +only_element(_E, _) -> + false. + +exit_p(Pid) -> + Ref = erlang:monitor(process, Pid), + Pid ! die, + receive + {'DOWN', Ref, process, Pid, _Reason} -> + ok + end. + +wait_for_exit(Pid) -> + Ref = erlang:monitor(process, Pid), + receive + {'DOWN', Ref, process, Pid, _Reason} -> + ok + end. + +wait_for_exit_fast(Pid) -> + Ref = erlang:monitor(process, Pid), + receive + {'DOWN', Ref, process, Pid, _Reason} -> + ok + end. + +check_everywhere(Nodes, Name, Config) -> + ?UNTIL(begin + case rpc:multicall(Nodes, global, whereis_name, [Name]) of + {Ns1, []} -> + check_same_p(Ns1); + _R -> + false + end + end). + +init_condition(Config) -> + io:format("globally registered names: ~p~n", [global:registered_names()]), + io:format("nodes: ~p~n", [nodes()]), + io:format("known: ~p~n", [get_known(node()) -- [node()]]), + io:format("Info ~p~n", [setelement(11, global:info(), trace)]), + _ = [io:format("~s: ~p~n", [TN, ets:tab2list(T)]) || + {TN, T} <- [{"Global Names (ETS)", global_names}, + {"Global Names Ext (ETS)", global_names_ext}, + {"Global Locks (ETS)", global_locks}, + {"Global Pid Names (ETS)", global_pid_names}, + {"Global Pid Ids (ETS)", global_pid_ids}]], + ?UNTIL([test_server] =:= global:registered_names()), + ?UNTIL([] =:= nodes()), + ?UNTIL([node()] =:= get_known(node())), + ok. + +mk_node(N, H) when is_list(N), is_list(H) -> + list_to_atom(N ++ "@" ++ H). + +remove_gg_pub_type([]) -> + []; +remove_gg_pub_type([{GG, Nodes}|Rest]) -> + [{GG, Nodes}|remove_gg_pub_type(Rest)]; +remove_gg_pub_type([{GG, _, Nodes}|Rest]) -> + [{GG, Nodes}|remove_gg_pub_type(Rest)]. + +%% Send garbage message to all processes that are linked to global. +%% Better do this in a slave node. +%% (The transition from links to monitors does not affect this case.) + +garbage_messages(suite) -> + []; +garbage_messages(Config) when is_list(Config) -> + Timeout = 25, + ?line Dog = test_server:timetrap({seconds,Timeout}), + init_high_level_trace(Timeout), + ?line init_condition(Config), + ?line [Slave] = start_nodes([garbage_messages], slave, Config), + Fun = fun() -> + {links,L} = process_info(whereis(global_name_server), links), + lists:foreach(fun(Pid) -> Pid ! {garbage,to,you} end, L), + receive + _Any -> ok + end + end, + ?line Pid = spawn_link(Slave, erlang, apply, [Fun,[]]), + ?t:sleep(2000), + ?line Global = rpc:call(Slave, erlang, whereis, [global_name_server]), + ?line {registered_name,global_name_server} = + rpc:call(Slave, erlang, process_info, [Global,registered_name]), + ?line true = unlink(Pid), + write_high_level_trace(Config), + ?line stop_node(Slave), + ?line init_condition(Config), + ?line test_server:timetrap_cancel(Dog), + ok. + +wait_for_ready_net(Config) -> + wait_for_ready_net(?NODES, Config). + +wait_for_ready_net(Nodes0, Config) -> + Nodes = lists:sort(Nodes0), + ?t:format("wait_for_ready_net ~p~n", [Nodes]), + ?UNTIL(begin + lists:all(fun(N) -> Nodes =:= get_known(N) end, Nodes) and + lists:all(fun(N) -> + LNs = rpc:call(N, erlang, nodes, []), + Nodes =:= lists:sort([N | LNs]) + end, Nodes) + end). + +get_known(Node) -> + case catch gen_server:call({global_name_server,Node},get_known,infinity) of + {'EXIT', _} -> + [list, without, nodenames]; + Known when is_list(Known) -> + lists:sort([Node | Known]) + end. + +quite_a_few_nodes(Max) -> + N = try + ulimit("ulimit -u") + catch _:_ -> + ulimit("ulimit -p") % can fail... + end, + lists:min([(N - 40) div 3, Max]). + +ulimit(Cmd) -> + N0 = os:cmd(Cmd), + N1 = lists:reverse(N0), + N2 = lists:dropwhile(fun($\r) -> true; + ($\n) -> true; + (_) -> false + end, N1), + case lists:reverse(N2) of + "unlimited" -> 10000; + N -> list_to_integer(N) + end. + +%% To make it less probable that some low-level problem causes +%% problems, the receiving node is ping:ed. +rpc_cast(Node, Module, Function, Args) -> + {_,pong,Node}= {node(),net_adm:ping(Node),Node}, + rpc:cast(Node, Module, Function, Args). + +rpc_cast(Node, Module, Function, Args, File) -> + case net_adm:ping(Node) of + pong -> + rpc:cast(Node, Module, Function, Args); + Else -> + append_to_file(File, {now(), {rpc_cast, Node, Module, Function, + Args, Else}}) + %% Maybe we should crash, but it probably doesn't matter. + end. + +%% The emulator now ensures that the node has been removed from +%% nodes(). +rpc_disconnect_node(Node, DisconnectedNode, _Config) -> + True = rpc:call(Node, erlang, disconnect_node, [DisconnectedNode]), + False = lists:member(DisconnectedNode, rpc:call(Node, erlang, nodes, [])), + {true, false} = {True, False}. + +%%% +%%% Utility +%%% + +%% It is a bit awkward to collect data from different nodes. One way +%% of doing is to use a named tracer process on each node. Interesting +%% data is banged to the tracer and when the test is finished data is +%% collected on some node by sending messages to the tracers. One +%% cannot do this if the net has been set up to be less than fully +%% connected. One can also prepare other modules, such as 'global', by +%% inserting lines like +%% trace_message({node(), {at,?LINE}, {tag, message}) +%% where appropriate. + +start_tracer() -> + Pid = spawn(fun() -> tracer([]) end), + case catch register(my_tracer, Pid) of + {'EXIT', _} -> + ?t:fail(re_register_my_tracer); + _ -> + ok + end. + +tracer(L) -> + receive + % {save, Term} -> + % tracer([{now(),Term} | L]); + {get, From} -> + From ! {trace, lists:reverse(L)}, + tracer([]); + stop -> + exit(normal); + Term -> + tracer([{now(),Term} | L]) + end. + +stop_tracer() -> + trace_message(stop). + +get_trace() -> + trace_message({get, self()}), + receive {trace, L} -> + L + end. + +collect_tracers(Nodes) -> + Traces0 = [rpc:call(N, ?MODULE, get_trace, []) || N <- Nodes], + Traces = [L || L <- Traces0, is_list(L)], + try begin + Stamped = lists:keysort(1, lists:append(Traces)), + NotStamped = [T || {_, T} <- Stamped], + {Stamped, NotStamped} + end + catch _:_ -> {[], []} + end. + +trace_message(M) -> + case catch my_tracer ! M of + {'EXIT', _} -> + ?t:fail(my_tracer_not_registered); + _ -> + ok + end. + +%%----------------------------------------------------------------- +%% The error_logger handler used for OTP-6931. +%%----------------------------------------------------------------- +init(Tester) -> + {ok, Tester}. + +handle_event({_, _GL, {_Pid,_String,[{nodeup,fake_node}=Msg]}}, Tester) -> + Tester ! Msg, + {ok, Tester}; +handle_event(_Event, State) -> + {ok, State}. + +handle_info(_Info, State) -> + {ok, State}. + +handle_call(_Query, State) -> {ok, {error, bad_query}, State}. + +terminate(_Reason, State) -> + State. + |