aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib')
-rw-r--r--lib/stdlib/doc/src/supervisor.xml61
-rw-r--r--lib/stdlib/src/filename.erl25
-rw-r--r--lib/stdlib/src/gen.erl4
-rw-r--r--lib/stdlib/src/gen_fsm.erl16
-rw-r--r--lib/stdlib/src/gen_server.erl6
-rw-r--r--lib/stdlib/src/re.erl2
-rw-r--r--lib/stdlib/src/stdlib.appup.src28
-rw-r--r--lib/stdlib/src/supervisor.erl6
-rw-r--r--lib/stdlib/test/binary_module_SUITE.erl18
-rw-r--r--lib/stdlib/test/ets_SUITE.erl215
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl2
-rw-r--r--lib/stdlib/test/qlc_SUITE.erl7
-rw-r--r--lib/stdlib/test/stdlib_SUITE.erl63
-rw-r--r--lib/stdlib/test/supervisor_SUITE.erl120
-rw-r--r--lib/stdlib/test/supervisor_bridge_SUITE.erl42
15 files changed, 439 insertions, 176 deletions
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index 30514dfee9..cddb55e5c5 100644
--- a/lib/stdlib/doc/src/supervisor.xml
+++ b/lib/stdlib/doc/src/supervisor.xml
@@ -158,7 +158,7 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
death causes the temporary process to be terminated) and a
<c>transient</c> child process should be restarted only if
it terminates abnormally, i.e. with another exit reason
- than <c>normal</c>.</p>
+ than <c>normal</c>, <c>shutdown</c> or <c>{shutdown,Term}</c>.</p>
</item>
<item>
<p><c>Shutdown</c> defines how a child process should be
@@ -355,14 +355,23 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
<desc>
<p>Tells the supervisor <c><anno>SupRef</anno></c> to terminate the given
child.</p>
+
<p>If the supervisor is not <c>simple_one_for_one</c>,
- <c><anno>Id</anno></c> must be the child specification identifier. The
- process, if there is one, is terminated but the child
- specification is kept by the supervisor. The child process
- may later be restarted by the supervisor. The child process
- can also be restarted explicitly by calling
+ <c><anno>Id</anno></c> must be the child specification
+ identifier. The process, if there is one, is terminated and,
+ unless it is a temporary child, the child specification is
+ kept by the supervisor. The child process may later be
+ restarted by the supervisor. The child process can also be
+ restarted explicitly by calling
<c>restart_child/2</c>. Use <c>delete_child/2</c> to remove
the child specification.</p>
+
+ <p>If the child is temporary, the child specification is deleted as
+ soon as the process terminates. This means
+ that <c>delete_child/2</c> has no meaning
+ and <c>restart_child/2</c> can not be used for these
+ children.</p>
+
<p>If the supervisor is <c>simple_one_for_one</c>, <c><anno>Id</anno></c>
must be the child process' <c>pid()</c>. I the specified
process is alive, but is not a child of the given
@@ -399,26 +408,34 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
<name name="restart_child" arity="2"/>
<fsummary>Restart a terminated child process belonging to a supervisor.</fsummary>
<desc>
- <p>Tells the supervisor <c><anno>SupRef</anno></c> to restart a child process
- corresponding to the child specification identified by
- <c><anno>Id</anno></c>. The child specification must exist and
- the corresponding child process must not be running.</p>
- <p>See <seealso marker="#SupRef"><c>start_child/2</c></seealso> for a description of
- <c>SupRef</c>.</p>
- <p>If the child specification identified by <c><anno>Id</anno></c> does not
- exist, the function returns <c>{error,not_found}</c>. If
- the child specification exists but the corresponding process
- is already running, the function returns
+ <p>Tells the supervisor <c><anno>SupRef</anno></c> to restart
+ a child process corresponding to the child specification
+ identified by <c><anno>Id</anno></c>. The child
+ specification must exist and the corresponding child process
+ must not be running.</p>
+ <p>Note that for temporary children, the child specification
+ is automatically deleted when the child terminates, and thus
+ it is not possible to restart such children.</p>
+ <p>See <seealso marker="#SupRef"><c>start_child/2</c></seealso>
+ for a description of <c>SupRef</c>.</p>
+ <p>If the child specification identified
+ by <c><anno>Id</anno></c> does not exist, the function
+ returns <c>{error,not_found}</c>. If the child specification
+ exists but the corresponding process is already running, the
+ function returns
<c>{error,running}</c>.</p>
- <p>If the child process start function returns <c>{ok,<anno>Child</anno>}</c>
- or <c>{ok,<anno>Child</anno>,<anno>Info</anno>}</c>, the pid is added to the supervisor
- and the function returns the same value.</p>
+ <p>If the child process start function
+ returns <c>{ok,<anno>Child</anno>}</c>
+ or <c>{ok,<anno>Child</anno>,<anno>Info</anno>}</c>, the pid
+ is added to the supervisor and the function returns the same
+ value.</p>
<p>If the child process start function returns <c>ignore</c>,
the pid remains set to <c>undefined</c> and the function
returns <c>{ok,undefined}</c>.</p>
- <p>If the child process start function returns an error tuple or
- an erroneous value, or if it fails, the function returns
- <c>{error,<anno>Error</anno>}</c> where <c><anno>Error</anno></c> is a term containing
+ <p>If the child process start function returns an error tuple
+ or an erroneous value, or if it fails, the function returns
+ <c>{error,<anno>Error</anno>}</c>
+ where <c><anno>Error</anno></c> is a term containing
information about the error.</p>
</desc>
</func>
diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl
index 1cb9e4a25e..2fc9128e4e 100644
--- a/lib/stdlib/src/filename.erl
+++ b/lib/stdlib/src/filename.erl
@@ -147,9 +147,10 @@ basename(Name) when is_binary(Name) ->
end;
basename(Name0) ->
- Name = flatten(Name0),
+ Name1 = flatten(Name0),
{DirSep2, DrvSep} = separators(),
- basename1(skip_prefix(Name, DrvSep), [], DirSep2).
+ Name = skip_prefix(Name1, DrvSep),
+ basename1(Name, Name, DirSep2).
win_basenameb(<<Letter,$:,Rest/binary>>) when ?IS_DRIVELETTER(Letter) ->
basenameb(Rest,[<<"/">>,<<"\\">>]);
@@ -167,16 +168,18 @@ basenameb(Bin,Sep) ->
-basename1([$/|[]], Tail, DirSep2) ->
- basename1([], Tail, DirSep2);
+basename1([$/], Tail0, _DirSep2) ->
+ %% End of filename -- must get rid of trailing directory separator.
+ [_|Tail] = lists:reverse(Tail0),
+ lists:reverse(Tail);
basename1([$/|Rest], _Tail, DirSep2) ->
- basename1(Rest, [], DirSep2);
+ basename1(Rest, Rest, DirSep2);
basename1([DirSep2|Rest], Tail, DirSep2) when is_integer(DirSep2) ->
basename1([$/|Rest], Tail, DirSep2);
basename1([Char|Rest], Tail, DirSep2) when is_integer(Char) ->
- basename1(Rest, [Char|Tail], DirSep2);
+ basename1(Rest, Tail, DirSep2);
basename1([], Tail, _DirSep2) ->
- lists:reverse(Tail).
+ Tail.
skip_prefix(Name, false) ->
Name;
@@ -369,8 +372,8 @@ extension(Name0) ->
Name = flatten(Name0),
extension(Name, [], major_os_type()).
-extension([$.|Rest], _Result, OsType) ->
- extension(Rest, [$.], OsType);
+extension([$.|Rest]=Result, _Result, OsType) ->
+ extension(Rest, Result, OsType);
extension([Char|Rest], [], OsType) when is_integer(Char) ->
extension(Rest, [], OsType);
extension([$/|Rest], _Result, OsType) ->
@@ -378,9 +381,9 @@ extension([$/|Rest], _Result, OsType) ->
extension([$\\|Rest], _Result, win32) ->
extension(Rest, [], win32);
extension([Char|Rest], Result, OsType) when is_integer(Char) ->
- extension(Rest, [Char|Result], OsType);
+ extension(Rest, Result, OsType);
extension([], Result, _OsType) ->
- lists:reverse(Result).
+ Result.
%% Joins a list of filenames with directory separators.
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 574146b1cd..5d803091b6 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. 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
@@ -273,7 +273,7 @@ reply({To, Tag}, Reply) ->
%%%-----------------------------------------------------------------
%%% Misc. functions.
%%%-----------------------------------------------------------------
-where({global, Name}) -> global:safe_whereis_name(Name);
+where({global, Name}) -> global:whereis_name(Name);
where({local, Name}) -> whereis(Name).
name_register({local, Name} = LN) ->
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index 3db8c9f4f2..57734a075c 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. 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
@@ -296,7 +296,7 @@ get_proc_name({local, Name}) ->
exit(process_not_registered)
end;
get_proc_name({global, Name}) ->
- case global:safe_whereis_name(Name) of
+ case global:whereis_name(Name) of
undefined ->
exit(process_not_registered_globally);
Pid when Pid =:= self() ->
@@ -318,7 +318,7 @@ get_parent() ->
name_to_pid(Name) ->
case whereis(Name) of
undefined ->
- case global:safe_whereis_name(Name) of
+ case global:whereis_name(Name) of
undefined ->
exit(could_not_find_registerd_name);
Pid ->
@@ -348,12 +348,15 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) ->
proc_lib:init_ack(Starter, {ok, self()}),
loop(Parent, Name, StateName, StateData, Mod, Timeout, Debug);
{stop, Reason} ->
+ unregister_name(Name0),
proc_lib:init_ack(Starter, {error, Reason}),
exit(Reason);
ignore ->
+ unregister_name(Name0),
proc_lib:init_ack(Starter, ignore),
exit(normal);
{'EXIT', Reason} ->
+ unregister_name(Name0),
proc_lib:init_ack(Starter, {error, Reason}),
exit(Reason);
Else ->
@@ -366,6 +369,13 @@ name({local,Name}) -> Name;
name({global,Name}) -> Name;
name(Pid) when is_pid(Pid) -> Pid.
+unregister_name({local,Name}) ->
+ _ = (catch unregister(Name));
+unregister_name({global,Name}) ->
+ _ = global:unregister_name(Name);
+unregister_name(Pid) when is_pid(Pid) ->
+ Pid.
+
%%-----------------------------------------------------------------
%% The MAIN loop
%%-----------------------------------------------------------------
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index dd0ef74f30..6f075bbe5a 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
+%% Copyright Ericsson AB 1996-2011. 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
@@ -820,7 +820,7 @@ get_proc_name({local, Name}) ->
exit(process_not_registered)
end;
get_proc_name({global, Name}) ->
- case global:safe_whereis_name(Name) of
+ case global:whereis_name(Name) of
undefined ->
exit(process_not_registered_globally);
Pid when Pid =:= self() ->
@@ -842,7 +842,7 @@ get_parent() ->
name_to_pid(Name) ->
case whereis(Name) of
undefined ->
- case global:safe_whereis_name(Name) of
+ case global:whereis_name(Name) of
undefined ->
exit(could_not_find_registerd_name);
Pid ->
diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl
index 99bcbd722e..246d535943 100644
--- a/lib/stdlib/src/re.erl
+++ b/lib/stdlib/src/re.erl
@@ -48,7 +48,7 @@ split(Subject,RE) ->
Subject :: iodata() | unicode:charlist(),
RE :: mp() | iodata() | unicode:charlist(),
Options :: [ Option ],
- Option :: anchored | global | notbol | noteol | notempty
+ Option :: anchored | notbol | noteol | notempty
| {offset, non_neg_integer()} | {newline, nl_spec()}
| bsr_anycrlf | bsr_unicode | {return, ReturnType}
| {parts, NumParts} | group | trim | CompileOpt,
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index 54a63833e6..94e81188b5 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -1 +1,27 @@
-{"%VSN%",[],[]}.
+%% -*- erlang -*-
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2011. 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%
+{"%VSN%",
+ %% Up from - max two major revisions back
+ [{<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
+ {<<"1\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}],%% R13
+ %% Down to - max two major revisions back
+ [{<<"1\\.18(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R15
+ {<<"1\\.17(\\.[0-9]+)*">>,[restart_new_emulator]}, %% R14
+ {<<"1\\.16(\\.[0-9]+)*">>,[restart_new_emulator]}] %% R13
+}.
diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl
index f20ea18fd0..2dd5ccce7a 100644
--- a/lib/stdlib/src/supervisor.erl
+++ b/lib/stdlib/src/supervisor.erl
@@ -1238,15 +1238,15 @@ report_error(Error, Reason, Child, SupName) ->
error_logger:error_report(supervisor_report, ErrorMsg).
-extract_child(Child) when is_pid(Child#child.pid) ->
- [{pid, Child#child.pid},
+extract_child(Child) when is_list(Child#child.pid) ->
+ [{nb_children, length(Child#child.pid)},
{name, Child#child.name},
{mfargs, Child#child.mfargs},
{restart_type, Child#child.restart_type},
{shutdown, Child#child.shutdown},
{child_type, Child#child.child_type}];
extract_child(Child) ->
- [{nb_children, length(Child#child.pid)},
+ [{pid, Child#child.pid},
{name, Child#child.name},
{mfargs, Child#child.mfargs},
{restart_type, Child#child.restart_type},
diff --git a/lib/stdlib/test/binary_module_SUITE.erl b/lib/stdlib/test/binary_module_SUITE.erl
index 8fb63f33bd..bac59a3107 100644
--- a/lib/stdlib/test/binary_module_SUITE.erl
+++ b/lib/stdlib/test/binary_module_SUITE.erl
@@ -20,7 +20,7 @@
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,
- interesting/1,random_ref_comp/1,random_ref_sr_comp/1,
+ interesting/1,scope_return/1,random_ref_comp/1,random_ref_sr_comp/1,
random_ref_fla_comp/1,parts/1, bin_to_list/1, list_to_bin/1,
copy/1, referenced/1,guard/1,encode_decode/1,badargs/1,longest_common_trap/1]).
@@ -67,7 +67,7 @@ end_per_testcase(_Case, Config) ->
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [interesting, random_ref_fla_comp, random_ref_sr_comp,
+ [scope_return,interesting, random_ref_fla_comp, random_ref_sr_comp,
random_ref_comp, parts, bin_to_list, list_to_bin, copy,
referenced, guard, encode_decode, badargs,
longest_common_trap].
@@ -379,6 +379,20 @@ subj() ->
Subject.
+scope_return(doc) ->
+ ["Test correct return values for scopes (OTP-9701)."];
+scope_return(Config) when is_list(Config) ->
+ N=10000,
+ Bin=binary:copy(<<"a">>,N),
+ scope_loop(Bin,0,N).
+
+scope_loop(_,N,N) ->
+ ok;
+scope_loop(Bin,N,M) ->
+ ?line {N,1} = binary:match(Bin,<<"a">>,[{scope,{N,1}}]),
+ ?line {N,1} = binary:match(Bin,[<<"a">>,<<"b">>],[{scope,{N,1}}]),
+ scope_loop(Bin,N+1,M).
+
interesting(doc) ->
["Try some interesting patterns"];
interesting(Config) when is_list(Config) ->
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 2f4958760b..0e8849b5b3 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -74,7 +74,7 @@
-export([bad_table/1, types/1]).
-export([otp_9423/1]).
--export([init_per_testcase/2, end_per_testcase/2]).
+-export([init_per_testcase/2]).
%% Convenience for manual testing
-export([random_test/0]).
@@ -176,6 +176,7 @@ groups() ->
meta_newdel_unnamed, meta_newdel_named]}].
init_per_suite(Config) ->
+ erts_debug:set_internal_state(available_internal_state, true),
Config.
end_per_suite(_Config) ->
@@ -304,7 +305,6 @@ t_match_spec_run(Config) when is_list(Config) ->
end,
repeat_for_permutations(F, N_MS)
end,
-
test_terms(Fun, skip_refc_check),
?line verify_etsmem(EtsMem).
@@ -324,7 +324,7 @@ t_match_spec_run_test(List, MS, Result) ->
%% Check that tracing agree
Self = self(),
- {Tracee, MonRef} = spawn_monitor(fun() -> ms_tracee(Self, List) end),
+ {Tracee, MonRef} = my_spawn_monitor(fun() -> ms_tracee(Self, List) end),
receive {Tracee, ready} -> ok end,
MST = lists:map(fun(Clause) -> ms_clause_ets_to_trace(Clause) end, MS),
@@ -585,7 +585,6 @@ select_fail_do(Opts) ->
memory(doc) -> ["Whitebox test of ets:info(X,memory)"];
memory(suite) -> [];
memory(Config) when is_list(Config) ->
- ?line erts_debug:set_internal_state(available_internal_state, true),
?line ok = chk_normal_tab_struct_size(),
repeat_for_opts(memory_do,[compressed]),
?line catch erts_debug:set_internal_state(available_internal_state, false).
@@ -1947,7 +1946,7 @@ evil_update_counter(Config) when is_list(Config) ->
evil_update_counter_do(Opts) ->
?line EtsMem = etsmem(),
?line process_flag(trap_exit, true),
- ?line Pids = [spawn_link(fun() -> evil_counter(I,Opts) end) || I <- lists:seq(1, 40)],
+ ?line Pids = [my_spawn_link(fun() -> evil_counter(I,Opts) end) || I <- lists:seq(1, 40)],
?line wait_for_all(gb_sets:from_list(Pids)),
?line verify_etsmem(EtsMem),
ok.
@@ -2153,24 +2152,24 @@ heir_do(Opts) ->
Combos),
%% No heir
- {Founder1,MrefF1} = spawn_monitor(fun()->heir_founder(Master,foo_data,Opts)end),
+ {Founder1,MrefF1} = my_spawn_monitor(fun()->heir_founder(Master,foo_data,Opts)end),
Founder1 ! {go, none},
?line {"No heir",Founder1} = receive_any(),
?line {'DOWN', MrefF1, process, Founder1, normal} = receive_any(),
?line undefined = ets:info(foo),
%% An already dead heir
- {Heir2,MrefH2} = spawn_monitor(fun()->die end),
+ {Heir2,MrefH2} = my_spawn_monitor(fun()->die end),
?line {'DOWN', MrefH2, process, Heir2, normal} = receive_any(),
- {Founder2,MrefF2} = spawn_monitor(fun()->heir_founder(Master,foo_data,Opts)end),
+ {Founder2,MrefF2} = my_spawn_monitor(fun()->heir_founder(Master,foo_data,Opts)end),
Founder2 ! {go, Heir2},
?line {"No heir",Founder2} = receive_any(),
?line {'DOWN', MrefF2, process, Founder2, normal} = receive_any(),
?line undefined = ets:info(foo),
%% When heir dies before founder
- {Founder3,MrefF3} = spawn_monitor(fun()->heir_founder(Master,"The dying heir",Opts)end),
- {Heir3,MrefH3} = spawn_monitor(fun()->heir_heir(Founder3)end),
+ {Founder3,MrefF3} = my_spawn_monitor(fun()->heir_founder(Master,"The dying heir",Opts)end),
+ {Heir3,MrefH3} = my_spawn_monitor(fun()->heir_heir(Founder3)end),
Founder3 ! {go, Heir3},
?line {'DOWN', MrefH3, process, Heir3, normal} = receive_any(),
Founder3 ! die_please,
@@ -2178,14 +2177,12 @@ heir_do(Opts) ->
?line undefined = ets:info(foo),
%% When heir dies and pid reused before founder dies
- erts_debug:set_internal_state(available_internal_state,true),
NextPidIx = erts_debug:get_internal_state(next_pid),
- {Founder4,MrefF4} = spawn_monitor(fun()->heir_founder(Master,"The dying heir",Opts)end),
- {Heir4,MrefH4} = spawn_monitor(fun()->heir_heir(Founder4)end),
+ {Founder4,MrefF4} = my_spawn_monitor(fun()->heir_founder(Master,"The dying heir",Opts)end),
+ {Heir4,MrefH4} = my_spawn_monitor(fun()->heir_heir(Founder4)end),
Founder4 ! {go, Heir4},
?line {'DOWN', MrefH4, process, Heir4, normal} = receive_any(),
erts_debug:set_internal_state(next_pid, NextPidIx),
- erts_debug:set_internal_state(available_internal_state,false),
{Heir4,MrefH4_B} = spawn_monitor_with_pid(Heir4,
fun()-> ?line die_please = receive_any() end),
Founder4 ! die_please,
@@ -2261,9 +2258,9 @@ heir_heir(Founder, Mode) ->
heir_1(HeirData,Mode,Opts) ->
io:format("test with heir_data = ~p\n", [HeirData]),
Master = self(),
- ?line Founder = spawn_link(fun() -> heir_founder(Master,HeirData,Opts) end),
+ ?line Founder = my_spawn_link(fun() -> heir_founder(Master,HeirData,Opts) end),
io:format("founder spawned = ~p\n", [Founder]),
- ?line {Heir,Mref} = spawn_monitor(fun() -> heir_heir(Founder,Mode) end),
+ ?line {Heir,Mref} = my_spawn_monitor(fun() -> heir_heir(Founder,Mode) end),
io:format("heir spawned = ~p\n", [{Heir,Mref}]),
?line Founder ! {go, Heir},
?line {'DOWN', Mref, process, Heir, normal} = receive_any().
@@ -2280,7 +2277,7 @@ give_away_do(Opts) ->
Parent = self(),
%% Give and then give back
- ?line {Receiver,Mref} = spawn_monitor(fun()-> give_away_receiver(T,Parent) end),
+ ?line {Receiver,Mref} = my_spawn_monitor(fun()-> give_away_receiver(T,Parent) end),
?line give_me = receive_any(),
?line true = ets:give_away(T,Receiver,here_you_are),
?line {'EXIT',{badarg,_}} = (catch ets:lookup(T,key)),
@@ -2291,7 +2288,7 @@ give_away_do(Opts) ->
%% Give and then let receiver keep it
?line true = ets:insert(T,{key,1}),
- ?line {Receiver3,Mref3} = spawn_monitor(fun()-> give_away_receiver(T,Parent) end),
+ ?line {Receiver3,Mref3} = my_spawn_monitor(fun()-> give_away_receiver(T,Parent) end),
?line give_me = receive_any(),
?line true = ets:give_away(T,Receiver3,here_you_are),
?line {'EXIT',{badarg,_}} = (catch ets:lookup(T,key)),
@@ -2303,7 +2300,7 @@ give_away_do(Opts) ->
?line T2 = ets_new(foo,[private | Opts]),
?line true = ets:insert(T2,{key,1}),
?line ets:setopts(T2,{heir,self(),"Som en gummiboll..."}),
- ?line {Receiver2,Mref2} = spawn_monitor(fun()-> give_away_receiver(T2,Parent) end),
+ ?line {Receiver2,Mref2} = my_spawn_monitor(fun()-> give_away_receiver(T2,Parent) end),
?line give_me = receive_any(),
?line true = ets:give_away(T2,Receiver2,here_you_are),
?line {'EXIT',{badarg,_}} = (catch ets:lookup(T2,key)),
@@ -2318,12 +2315,12 @@ give_away_do(Opts) ->
?line {'EXIT',{badarg,_}} = (catch ets:give_away(T2,"not a pid","To wrong type")),
?line true = ets:delete(T2),
- ?line {ReceiverNeg,MrefNeg} = spawn_monitor(fun()-> give_away_receiver(T2,Parent) end),
+ ?line {ReceiverNeg,MrefNeg} = my_spawn_monitor(fun()-> give_away_receiver(T2,Parent) end),
?line give_me = receive_any(),
?line {'EXIT',{badarg,_}} = (catch ets:give_away(T2,ReceiverNeg,"A deleted table")),
?line T3 = ets_new(foo,[public | Opts]),
- spawn_link(fun()-> {'EXIT',{badarg,_}} = (catch ets:give_away(T3,ReceiverNeg,"From non owner")),
+ my_spawn_link(fun()-> {'EXIT',{badarg,_}} = (catch ets:give_away(T3,ReceiverNeg,"From non owner")),
Parent ! done
end),
?line done = receive_any(),
@@ -2359,7 +2356,7 @@ setopts_do(Opts) ->
Self = self(),
?line T = ets_new(foo,[named_table, private | Opts]),
?line none = ets:info(T,heir),
- Heir = spawn_link(fun()->heir_heir(Self) end),
+ Heir = my_spawn_link(fun()->heir_heir(Self) end),
?line ets:setopts(T,{heir,Heir,"Data"}),
?line Heir = ets:info(T,heir),
?line ets:setopts(T,{heir,self(),"Data"}),
@@ -2410,14 +2407,14 @@ bad_table(Config) when is_list(Config) ->
bad_table_do(Opts, DummyFile) ->
Parent = self(),
- {Pid,Mref} = spawn_opt(fun()-> ets_new(priv,[private,named_table | Opts]),
- Priv = ets_new(priv,[private | Opts]),
- ets_new(prot,[protected,named_table | Opts]),
- Prot = ets_new(prot,[protected | Opts]),
- Parent ! {self(),Priv,Prot},
- die_please = receive_any()
- end,
- [link, monitor]),
+ {Pid,Mref} = my_spawn_opt(fun()-> ets_new(priv,[private,named_table | Opts]),
+ Priv = ets_new(priv,[private | Opts]),
+ ets_new(prot,[protected,named_table | Opts]),
+ Prot = ets_new(prot,[protected | Opts]),
+ Parent ! {self(),Priv,Prot},
+ die_please = receive_any()
+ end,
+ [link, monitor]),
{Pid,Priv,Prot} = receive_any(),
MatchSpec = {{key,'_'}, [], ['$$']},
Fun = fun(X,_) -> X end,
@@ -3263,7 +3260,7 @@ delete_large_named_table_1(Name, Flags, Data, Fix) ->
?line lists:foreach(fun({K,_}) -> ets:delete(Tab, K) end, Data)
end,
Parent = self(),
- Pid = spawn_link(fun() ->
+ Pid = my_spawn_link(fun() ->
receive
{trace,Parent,call,_} ->
ets_new(Name, [named_table])
@@ -3625,7 +3622,7 @@ cycle(Tab, L) ->
ets:insert(Tab,list_to_tuple(L)),
cycle(Tab, tl(L)++[hd(L)]).
-dynamic_go() -> spawn_link(fun dynamic_init/0).
+dynamic_go() -> my_spawn_link(fun dynamic_init/0).
dynamic_init() -> [dyn_lookup(?MODULE) || _ <- lists:seq(1, 10)].
@@ -3852,7 +3849,7 @@ safe_fixtable_do(Opts) ->
Self = self(),
?line {{_,_,_},[{Self,1}]} = ets:info(Tab,safe_fixed),
%% Test that an unjustified 'unfix' is a no-op.
- {Pid,MRef} = spawn_monitor(fun() -> true = ets:safe_fixtable(Tab,false) end),
+ {Pid,MRef} = my_spawn_monitor(fun() -> true = ets:safe_fixtable(Tab,false) end),
{'DOWN', MRef, process, Pid, normal} = receive M -> M end,
?line true = ets:info(Tab,fixed),
?line {{_,_,_},[{Self,1}]} = ets:info(Tab,safe_fixed),
@@ -4256,7 +4253,7 @@ do_heavy_concurrent(Opts) ->
?line ok = fill_tab2(Tab, 0, Size),
?line Procs = lists:map(
fun (N) ->
- spawn_link(
+ my_spawn_link(
fun () ->
do_heavy_concurrent_proc(Tab, Size, N)
end)
@@ -4860,12 +4857,7 @@ otp_7665_act(Tab,Min,Max,DelNr) ->
%% Whitebox testing of meta name table hashing.
meta_wb(Config) when is_list(Config) ->
?line EtsMem = etsmem(),
- ?line erts_debug:set_internal_state(available_internal_state, true),
- try
- repeat_for_opts(meta_wb_do)
- after
- erts_debug:set_internal_state(available_internal_state, false)
- end,
+ repeat_for_opts(meta_wb_do),
?line verify_etsmem(EtsMem).
@@ -4934,12 +4926,15 @@ colliding_names(Name) ->
grow_shrink(Config) when is_list(Config) ->
?line EtsMem = etsmem(),
- grow_shrink_0(lists:seq(3071, 5000), EtsMem).
+ ?line grow_shrink_0(lists:seq(3071, 5000), EtsMem),
+ ?line verify_etsmem(EtsMem).
grow_shrink_0([N|Ns], EtsMem) ->
?line grow_shrink_1(N, [set]),
?line grow_shrink_1(N, [ordered_set]),
- ?line verify_etsmem(EtsMem),
+ %% Verifying ets-memory here takes too long time, since
+ %% lock-free allocators were introduced...
+ %% ?line verify_etsmem(EtsMem),
grow_shrink_0(Ns, EtsMem);
grow_shrink_0([], _) -> ok.
@@ -4986,13 +4981,13 @@ grow_pseudo_deleted_do(Type) ->
?line Left = ets:info(T,size),
?line Mult = get_kept_objects(T),
filltabstr(T,Mult),
- spawn_opt(fun()-> ?line true = ets:info(T,fixed),
- Self ! start,
- io:format("Starting to filltabstr... ~p\n",[now()]),
- filltabstr(T,Mult,Mult+10000),
- io:format("Done with filltabstr. ~p\n",[now()]),
- Self ! done
- end, [link, {scheduler,2}]),
+ my_spawn_opt(fun()-> ?line true = ets:info(T,fixed),
+ Self ! start,
+ io:format("Starting to filltabstr... ~p\n",[now()]),
+ filltabstr(T,Mult,Mult+10000),
+ io:format("Done with filltabstr. ~p\n",[now()]),
+ Self ! done
+ end, [link, {scheduler,2}]),
?line start = receive_any(),
io:format("Unfixing table...~p nitems=~p\n",[now(),ets:info(T,size)]),
?line true = ets:safe_fixtable(T,false),
@@ -5026,13 +5021,13 @@ shrink_pseudo_deleted_do(Type) ->
[true]}]),
?line Half = ets:info(T,size),
?line Half = get_kept_objects(T),
- spawn_opt(fun()-> ?line true = ets:info(T,fixed),
- Self ! start,
- io:format("Starting to delete... ~p\n",[now()]),
- del_one_by_one_set(T,1,Half+1),
- io:format("Done with delete. ~p\n",[now()]),
- Self ! done
- end, [link, {scheduler,2}]),
+ my_spawn_opt(fun()-> ?line true = ets:info(T,fixed),
+ Self ! start,
+ io:format("Starting to delete... ~p\n",[now()]),
+ del_one_by_one_set(T,1,Half+1),
+ io:format("Done with delete. ~p\n",[now()]),
+ Self ! done
+ end, [link, {scheduler,2}]),
?line start = receive_any(),
io:format("Unfixing table...~p nitems=~p\n",[now(),ets:info(T,size)]),
?line true = ets:safe_fixtable(T,false),
@@ -5189,24 +5184,24 @@ smp_unfix_fix_do() ->
?line Deleted = get_kept_objects(T),
{Child, Mref} =
- spawn_opt(fun()-> ?line true = ets:info(T,fixed),
- Parent ! start,
- io:format("Child waiting for table to be unfixed... now=~p mem=~p\n",
- [now(),ets:info(T,memory)]),
- repeat_while(fun()-> ets:info(T,fixed) end),
- io:format("Table unfixed. Child Fixating! now=~p mem=~p\n",
- [now(),ets:info(T,memory)]),
- ?line true = ets:safe_fixtable(T,true),
- repeat_while(fun(Key) when Key =< NumOfObjs ->
- ets:delete(T,Key), {true,Key+1};
- (Key) -> {false,Key}
- end,
- Deleted),
- ?line 0 = ets:info(T,size),
- ?line true = get_kept_objects(T) >= Left,
- ?line done = receive_any()
- end,
- [link, monitor, {scheduler,2}]),
+ my_spawn_opt(fun()-> ?line true = ets:info(T,fixed),
+ Parent ! start,
+ io:format("Child waiting for table to be unfixed... now=~p mem=~p\n",
+ [now(),ets:info(T,memory)]),
+ repeat_while(fun()-> ets:info(T,fixed) end),
+ io:format("Table unfixed. Child Fixating! now=~p mem=~p\n",
+ [now(),ets:info(T,memory)]),
+ ?line true = ets:safe_fixtable(T,true),
+ repeat_while(fun(Key) when Key =< NumOfObjs ->
+ ets:delete(T,Key), {true,Key+1};
+ (Key) -> {false,Key}
+ end,
+ Deleted),
+ ?line 0 = ets:info(T,size),
+ ?line true = get_kept_objects(T) >= Left,
+ ?line done = receive_any()
+ end,
+ [link, monitor, {scheduler,2}]),
?line start = receive_any(),
?line true = ets:info(T,fixed),
@@ -5237,11 +5232,11 @@ otp_8166_do(WC) ->
Deleted = NumOfObjs div 2,
filltabint(T,NumOfObjs),
{ReaderPid, ReaderMref} =
- spawn_opt(fun()-> otp_8166_reader(T,NumOfObjs) end,
- [link, monitor, {scheduler,2}]),
+ my_spawn_opt(fun()-> otp_8166_reader(T,NumOfObjs) end,
+ [link, monitor, {scheduler,2}]),
{ZombieCrPid, ZombieCrMref} =
- spawn_opt(fun()-> otp_8166_zombie_creator(T,Deleted) end,
- [link, monitor, {scheduler,3}]),
+ my_spawn_opt(fun()-> otp_8166_zombie_creator(T,Deleted) end,
+ [link, monitor, {scheduler,3}]),
repeat(fun() -> ZombieCrPid ! {loop, self()},
zombies_created = receive_any(),
@@ -5501,7 +5496,7 @@ run_workers_do(InitF,ExecF,FiniF,Laps, Exclude) ->
io:format("smp starting ~p workers\n",[NumOfProcs]),
Seeds = [{ProcN,random:uniform(9999)} || ProcN <- lists:seq(1,NumOfProcs)],
Parent = self(),
- Pids = [spawn_link(fun()-> worker(Seed,InitF,ExecF,FiniF,Laps,Parent,NumOfProcs) end)
+ Pids = [my_spawn_link(fun()-> worker(Seed,InitF,ExecF,FiniF,Laps,Parent,NumOfProcs) end)
|| Seed <- Seeds],
case Laps of
infinite -> Pids;
@@ -5550,31 +5545,30 @@ my_tab_to_list(_Ts,'$end_of_table', Acc) -> lists:reverse(Acc);
my_tab_to_list(Ts,Key, Acc) ->
my_tab_to_list(Ts,ets:next(Ts,Key),[ets:lookup(Ts, Key)| Acc]).
-wait_for_all_schedulers_online_to_execute() ->
- PMs = lists:map(fun (Sched) ->
- spawn_opt(fun () -> ok end,
- [monitor, {scheduler, Sched}])
- end,
- lists:seq(1,erlang:system_info(schedulers_online))),
- lists:foreach(fun ({P, M}) ->
- receive
- {'DOWN', M, process, P, _} -> ok
- end
- end,
- PMs),
- ok.
+
+wait_for_memory_deallocations() ->
+ try
+ erts_debug:set_internal_state(wait, deallocations)
+ catch
+ error:undef ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ wait_for_memory_deallocations()
+ end.
+
etsmem() ->
- %% Wait until it is guaranteed that all already scheduled
- %% deallocations of DbTable structures have completed.
- wait_for_all_schedulers_online_to_execute(),
+ wait_for_memory_deallocations(),
AllTabs = lists:map(fun(T) -> {T,ets:info(T,name),ets:info(T,size),
ets:info(T,memory),ets:info(T,type)}
end, ets:all()),
+
+ EtsAllocInfo = erlang:system_info({allocator,ets_alloc}),
+ ErlangMemoryEts = try erlang:memory(ets) catch error:notsup -> notsup end,
+
Mem =
- {try erlang:memory(ets) catch error:notsup -> notsup end,
- case erlang:system_info({allocator,ets_alloc}) of
+ {ErlangMemoryEts,
+ case EtsAllocInfo of
false -> undefined;
MemInfo ->
CS = lists:foldl(
@@ -5651,6 +5645,7 @@ spawn_logger(Procs) ->
true -> exit(Proc, kill);
_ -> ok
end,
+ erlang:display(process_info(Proc)),
receive
{'DOWN', Mon, _, _, _} ->
ok
@@ -5686,7 +5681,7 @@ wait_for_test_procs(Kill) ->
ets_test_spawn_logger ! {sync_test_procs, Kill, self()},
receive test_procs_synced -> ok end.
-log_test_proc(Proc) ->
+log_test_proc(Proc) when is_pid(Proc) ->
ets_test_spawn_logger ! {new_test_proc, Proc},
Proc.
@@ -5698,9 +5693,17 @@ my_spawn_link(Fun) -> log_test_proc(spawn_link(Fun)).
my_spawn_link(M,F,A) -> log_test_proc(spawn_link(M,F,A)).
%%my_spawn_link(N,M,F,A) -> log_test_proc(spawn_link(N,M,F,A)).
-my_spawn_opt(Fun,Opts) -> log_test_proc(spawn_opt(Fun,Opts)).
-%%my_spawn_opt(M,F,A,Opts) -> log_test_proc(spawn_opt(M,F,A,Opts)).
-%%my_spawn_opt(N,M,F,A,Opts) -> log_test_proc(spawn_opt(N,M,F,A,Opts)).
+my_spawn_opt(Fun,Opts) ->
+ case spawn_opt(Fun,Opts) of
+ Pid when is_pid(Pid) -> log_test_proc(Pid);
+ {Pid, _} = Res when is_pid(Pid) -> log_test_proc(Pid), Res
+ end.
+
+my_spawn_monitor(Fun) ->
+ Res = spawn_monitor(Fun),
+ {Pid, _} = Res,
+ log_test_proc(Pid),
+ Res.
repeat(_Fun, 0) ->
ok;
@@ -5763,11 +5766,11 @@ spawn_monitor_with_pid(Pid, Fun, N, M) when N > M*10 ->
spawn_monitor_with_pid(Pid, Fun, N, M*10);
spawn_monitor_with_pid(Pid, Fun, N, M) ->
?line false = is_process_alive(Pid),
- case spawn(fun()-> case self() of
- Pid -> Fun();
- _ -> die
- end
- end) of
+ case my_spawn(fun()-> case self() of
+ Pid -> Fun();
+ _ -> die
+ end
+ end) of
Pid ->
{Pid, erlang:monitor(process, Pid)};
Other ->
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index a614d6595d..7fb8d54f2d 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -694,7 +694,7 @@ multicall_down(Config) when is_list(Config) ->
%% We use 'global' as a gen_server to call.
?line {Good, Bad} = gen_server:multi_call([Name, node()],
global_name_server,
- {whereis, gurkburk},
+ info,
3000),
io:format("good = ~p, bad = ~p~n", [Good, Bad]),
?line [Name] = Bad,
diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl
index 8a9d8f7883..9be547c690 100644
--- a/lib/stdlib/test/qlc_SUITE.erl
+++ b/lib/stdlib/test/qlc_SUITE.erl
@@ -6118,6 +6118,7 @@ otp_6964(Config) when is_list(Config) ->
qlc:e(Q, [{max_list_size,64*1024},{tmpdir_usage,Use}])
end,
D = erlang:system_flag(backtrace_depth, 0),
+ try
20000 = length(F(allowed)),
ErrReply = F(not_allowed),
{error, qlc, {tmpdir_usage,joining}} = ErrReply,
@@ -6129,8 +6130,10 @@ otp_6964(Config) when is_list(Config) ->
20000 = length(F(info_msg)),
{info, joining} = qlc_SUITE:read_error_logger(),
20000 = length(F(error_msg)),
- {error, joining} = qlc_SUITE:read_error_logger(),
- _ = erlang:system_flag(backtrace_depth, D),
+ {error, joining} = qlc_SUITE:read_error_logger()
+ after
+ _ = erlang:system_flag(backtrace_depth, D)
+ end,
qlc_SUITE:uninstall_error_logger()">>],
?line run(Config, T1),
diff --git a/lib/stdlib/test/stdlib_SUITE.erl b/lib/stdlib/test/stdlib_SUITE.erl
index 0cca030b3d..8a2cb5ea6b 100644
--- a/lib/stdlib/test/stdlib_SUITE.erl
+++ b/lib/stdlib/test/stdlib_SUITE.erl
@@ -33,8 +33,7 @@
-export([init_per_testcase/2, end_per_testcase/2]).
% Test cases must be exported.
--export([app_test/1]).
--define(cases, [app_test]).
+-export([app_test/1, appup_test/1]).
%%
%% all/1
@@ -42,7 +41,7 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [app_test].
+ [app_test, appup_test].
groups() ->
[].
@@ -79,3 +78,61 @@ app_test(Config) when is_list(Config) ->
?t:app_test(stdlib),
ok.
+%% Test that appup allows upgrade from/downgrade to a maximum of two
+%% major releases back.
+appup_test(_Config) ->
+ application:load(stdlib),
+ {_,_,Vsn} = lists:keyfind(stdlib,1,application:loaded_applications()),
+ AppupFile = filename:join([code:lib_dir(stdlib),ebin,"stdlib.appup"]),
+ {ok,[{Vsn,UpFrom,DownTo}=AppupScript]} = file:consult(AppupFile),
+ ct:log("~p~n",[AppupScript]),
+ {OkVsns,NokVsns} = create_test_vsns(Vsn),
+ check_appup(OkVsns,UpFrom,{ok,[restart_new_emulator]}),
+ check_appup(OkVsns,DownTo,{ok,[restart_new_emulator]}),
+ check_appup(NokVsns,UpFrom,error),
+ check_appup(NokVsns,DownTo,error),
+ ok.
+
+create_test_vsns(Current) ->
+ [XStr,YStr|Rest] = string:tokens(Current,"."),
+ X = list_to_integer(XStr),
+ Y = list_to_integer(YStr),
+ SecondMajor = vsn(X,Y-2),
+ SecondMinor = SecondMajor ++ ".1.3",
+ FirstMajor = vsn(X,Y-1),
+ FirstMinor = FirstMajor ++ ".57",
+ ThisMajor = vsn(X,Y),
+ This =
+ case Rest of
+ [] ->
+ [];
+ ["1"] ->
+ [ThisMajor];
+ _ ->
+ ThisMinor = ThisMajor ++ ".1",
+ [ThisMajor,ThisMinor]
+ end,
+ OkVsns = This ++ [FirstMajor, FirstMinor, SecondMajor, SecondMinor],
+
+ ThirdMajor = vsn(X,Y-3),
+ ThirdMinor = ThirdMajor ++ ".10.12",
+ Illegal = ThisMajor ++ ",1",
+ Newer1Major = vsn(X,Y+1),
+ Newer1Minor = Newer1Major ++ ".1",
+ Newer2Major = ThisMajor ++ "1",
+ NokVsns = [ThirdMajor,ThirdMinor,
+ Illegal,
+ Newer1Major,Newer1Minor,
+ Newer2Major],
+ {OkVsns,NokVsns}.
+
+vsn(X,Y) ->
+ integer_to_list(X) ++ "." ++ integer_to_list(Y).
+
+check_appup([Vsn|Vsns],Instrs,Expected) ->
+ case systools_relup:appup_search_for_version(Vsn, Instrs) of
+ Expected -> check_appup(Vsns,Instrs,Expected);
+ Other -> ct:fail({unexpected_result_for_vsn,Vsn,Other})
+ end;
+check_appup([],_,_) ->
+ ok.
diff --git a/lib/stdlib/test/supervisor_SUITE.erl b/lib/stdlib/test/supervisor_SUITE.erl
index e709cf62ba..d3d140abbc 100644
--- a/lib/stdlib/test/supervisor_SUITE.erl
+++ b/lib/stdlib/test/supervisor_SUITE.erl
@@ -29,7 +29,8 @@
end_per_testcase/2]).
%% Internal export
--export([init/1, terminate_all_children/1]).
+-export([init/1, terminate_all_children/1,
+ middle9212/0, gen_server9212/0, handle_info/2]).
%% API tests
-export([ sup_start_normal/1, sup_start_ignore_init/1,
@@ -58,7 +59,8 @@
-export([child_unlink/1, tree/1, count_children_memory/1,
do_not_save_start_parameters_for_temporary_children/1,
do_not_save_child_specs_for_temporary_children/1,
- simple_one_for_one_scale_many_temporary_children/1]).
+ simple_one_for_one_scale_many_temporary_children/1,
+ simple_global_supervisor/1]).
%%-------------------------------------------------------------------------
@@ -77,7 +79,8 @@ all() ->
{group, abnormal_termination}, child_unlink, tree,
count_children_memory, do_not_save_start_parameters_for_temporary_children,
do_not_save_child_specs_for_temporary_children,
- simple_one_for_one_scale_many_temporary_children, temporary_bystander].
+ simple_one_for_one_scale_many_temporary_children, temporary_bystander,
+ simple_global_supervisor].
groups() ->
[{sup_start, [],
@@ -120,7 +123,9 @@ end_per_group(_GroupName, Config) ->
init_per_testcase(count_children_memory, Config) ->
try erlang:memory() of
- _ -> Config
+ _ ->
+ erts_debug:set_internal_state(available_internal_state, true),
+ Config
catch error:notsup ->
{skip, "+Meamin used during test; erlang:memory/1 not available"}
end;
@@ -128,6 +133,9 @@ init_per_testcase(_Case, Config) ->
erlang:display(_Case),
Config.
+end_per_testcase(count_children_memory, _Config) ->
+ catch erts_debug:set_internal_state(available_internal_state, false),
+ ok;
end_per_testcase(_Case, _Config) ->
ok.
@@ -1136,25 +1144,25 @@ count_children_memory(Config) when is_list(Config) ->
[supervisor:start_child(sup_test, []) || _Ignore <- lists:seq(1,1000)],
garbage_collect(),
- _Size1 = erlang:memory(processes_used),
+ _Size1 = proc_memory(),
Children = supervisor:which_children(sup_test),
- _Size2 = erlang:memory(processes_used),
+ _Size2 = proc_memory(),
ChildCount = get_child_counts(sup_test),
- _Size3 = erlang:memory(processes_used),
+ _Size3 = proc_memory(),
[supervisor:start_child(sup_test, []) || _Ignore2 <- lists:seq(1,1000)],
garbage_collect(),
Children2 = supervisor:which_children(sup_test),
- Size4 = erlang:memory(processes_used),
+ Size4 = proc_memory(),
ChildCount2 = get_child_counts(sup_test),
- Size5 = erlang:memory(processes_used),
+ Size5 = proc_memory(),
garbage_collect(),
Children3 = supervisor:which_children(sup_test),
- Size6 = erlang:memory(processes_used),
+ Size6 = proc_memory(),
ChildCount3 = get_child_counts(sup_test),
- Size7 = erlang:memory(processes_used),
+ Size7 = proc_memory(),
1000 = length(Children),
[1,1000,0,1000] = ChildCount,
@@ -1180,6 +1188,10 @@ count_children_memory(Config) when is_list(Config) ->
[terminate(SupPid, Pid, child, kill) || {undefined, Pid, worker, _Modules} <- Children3],
[1,0,0,0] = get_child_counts(sup_test).
+proc_memory() ->
+ erts_debug:set_internal_state(wait, deallocations),
+ erlang:memory(processes_used).
+
%%-------------------------------------------------------------------------
do_not_save_start_parameters_for_temporary_children(doc) ->
["Temporary children shall not be restarted so they should not "
@@ -1379,6 +1391,92 @@ terminate_all_children([]) ->
done.
+%%-------------------------------------------------------------------------
+%% OTP-9212. Restart of global supervisor.
+simple_global_supervisor(_Config) ->
+ kill_supervisor(),
+ kill_worker(),
+ exit_worker(),
+ restart_worker(),
+ ok.
+
+kill_supervisor() ->
+ {Top, Sup2_1, Server_1} = start9212(),
+
+ %% Killing a supervisor isn't really supported, but try it anyway...
+ exit(Sup2_1, kill),
+ timer:sleep(200),
+ Sup2_2 = global:whereis_name(sup2),
+ Server_2 = global:whereis_name(server),
+ true = is_pid(Sup2_2),
+ true = is_pid(Server_2),
+ true = Sup2_1 =/= Sup2_2,
+ true = Server_1 =/= Server_2,
+
+ stop9212(Top).
+
+handle_info({fail, With, After}, _State) ->
+ timer:sleep(After),
+ erlang:error(With).
+
+kill_worker() ->
+ {Top, _Sup2, Server_1} = start9212(),
+ exit(Server_1, kill),
+ timer:sleep(200),
+ Server_2 = global:whereis_name(server),
+ true = is_pid(Server_2),
+ true = Server_1 =/= Server_2,
+ stop9212(Top).
+
+exit_worker() ->
+ %% Very much the same as kill_worker().
+ {Top, _Sup2, Server_1} = start9212(),
+ Server_1 ! {fail, normal, 0},
+ timer:sleep(200),
+ Server_2 = global:whereis_name(server),
+ true = is_pid(Server_2),
+ true = Server_1 =/= Server_2,
+ stop9212(Top).
+
+restart_worker() ->
+ {Top, _Sup2, Server_1} = start9212(),
+ ok = supervisor:terminate_child({global, sup2}, child),
+ {ok, _Child} = supervisor:restart_child({global, sup2}, child),
+ Server_2 = global:whereis_name(server),
+ true = is_pid(Server_2),
+ true = Server_1 =/= Server_2,
+ stop9212(Top).
+
+start9212() ->
+ Middle = {middle,{?MODULE,middle9212,[]}, permanent,2000,supervisor,[]},
+ InitResult = {ok, {{one_for_all,3,60}, [Middle]}},
+ {ok, TopPid} = start_link(InitResult),
+
+ Sup2 = global:whereis_name(sup2),
+ Server = global:whereis_name(server),
+ true = is_pid(Sup2),
+ true = is_pid(Server),
+ {TopPid, Sup2, Server}.
+
+stop9212(Top) ->
+ Old = process_flag(trap_exit, true),
+ exit(Top, kill),
+ timer:sleep(200),
+ undefined = global:whereis_name(sup2),
+ undefined = global:whereis_name(server),
+ check_exit([Top]),
+ _ = process_flag(trap_exit, Old),
+ ok.
+
+middle9212() ->
+ Child = {child, {?MODULE,gen_server9212,[]},permanent, 2000, worker, []},
+ InitResult = {ok, {{one_for_all,3,60}, [Child]}},
+ supervisor:start_link({global,sup2}, ?MODULE, InitResult).
+
+gen_server9212() ->
+ InitResult = {ok, []},
+ gen_server:start_link({global,server}, ?MODULE, InitResult, []).
+
%%-------------------------------------------------------------------------
terminate(Pid, Reason) when Reason =/= supervisor ->
diff --git a/lib/stdlib/test/supervisor_bridge_SUITE.erl b/lib/stdlib/test/supervisor_bridge_SUITE.erl
index c4d696564d..b3056ff41a 100644
--- a/lib/stdlib/test/supervisor_bridge_SUITE.erl
+++ b/lib/stdlib/test/supervisor_bridge_SUITE.erl
@@ -19,8 +19,9 @@
-module(supervisor_bridge_SUITE).
-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2,starting/1,
- mini_terminate/1,mini_die/1,badstart/1]).
--export([client/1,init/1,internal_loop_init/1,terminate/2]).
+ mini_terminate/1,mini_die/1,badstart/1,
+ simple_global_supervisor/1]).
+-export([client/1,init/1,internal_loop_init/1,terminate/2,server9212/0]).
-include_lib("test_server/include/test_server.hrl").
-define(bridge_name,supervisor_bridge_SUITE_server).
@@ -31,7 +32,7 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [starting, mini_terminate, mini_die, badstart].
+ [starting, mini_terminate, mini_die, badstart, simple_global_supervisor].
groups() ->
[].
@@ -138,7 +139,9 @@ init(3) ->
receive
{InternalPid,init_done} ->
{ok,InternalPid,self()}
- end.
+ end;
+init({4,Result}) ->
+ Result.
internal_loop_init(Parent) ->
register(?work_bridge_name, self()),
@@ -160,7 +163,9 @@ terminate(Reason,{Parent,Worker}) ->
io:format("Terminating bridge...\n"),
exit(Worker,kill),
Parent ! {dying,Reason},
- anything.
+ anything;
+terminate(_Reason, _State) ->
+ any.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@@ -197,3 +202,30 @@ badstart(Config) when is_list(Config) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% OTP-9212. Restart of global supervisor.
+
+simple_global_supervisor(suite) -> [];
+simple_global_supervisor(doc) -> "Globally registered supervisor.";
+simple_global_supervisor(Config) when is_list(Config) ->
+ ?line Dog = test_server:timetrap({seconds,10}),
+
+ Child = {child, {?MODULE,server9212,[]}, permanent, 2000, worker, []},
+ InitResult = {ok, {{one_for_all,3,60}, [Child]}},
+ {ok, Sup} =
+ supervisor:start_link({local,bridge9212}, ?MODULE, {4,InitResult}),
+
+ BN_1 = global:whereis_name(?bridge_name),
+ ?line exit(BN_1, kill),
+ timer:sleep(200),
+ BN_2 = global:whereis_name(?bridge_name),
+ ?line true = is_pid(BN_2),
+ ?line true = BN_1 =/= BN_2,
+
+ ?line process_flag(trap_exit, true),
+ exit(Sup, kill),
+ ?line receive {'EXIT', Sup, killed} -> ok end,
+ ?line test_server:timetrap_cancel(Dog),
+ ok.
+
+server9212() ->
+ supervisor_bridge:start_link({global,?bridge_name}, ?MODULE, 3).