aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib/test/proc_lib_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib/test/proc_lib_SUITE.erl')
-rw-r--r--lib/stdlib/test/proc_lib_SUITE.erl344
1 files changed, 344 insertions, 0 deletions
diff --git a/lib/stdlib/test/proc_lib_SUITE.erl b/lib/stdlib/test/proc_lib_SUITE.erl
new file mode 100644
index 0000000000..2fd7725335
--- /dev/null
+++ b/lib/stdlib/test/proc_lib_SUITE.erl
@@ -0,0 +1,344 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1996-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(proc_lib_SUITE).
+
+%%
+%% Define to run outside of test server
+%%
+%%-define(STANDALONE,1).
+
+-export([all/1, crash/1, sync_start/1, sync_start_nolink/1, sync_start_link/1,
+ spawn_opt/1, sp1/0, sp2/0, sp3/1, sp4/2, sp5/1,
+ hibernate/1]).
+-export([tickets/1, otp_6345/1]).
+
+-export([hib_loop/1, awaken/1]).
+
+-export([init/1,
+ handle_event/2, handle_call/2, handle_info/2,
+ terminate/2]).
+
+-export([otp_6345_init/1]).
+
+
+-ifdef(STANDALONE).
+-define(line, noop, ).
+-else.
+-include("test_server.hrl").
+-endif.
+
+all(suite) -> [crash, sync_start, spawn_opt, hibernate, tickets].
+
+tickets(suite) -> [otp_6345].
+
+%%-----------------------------------------------------------------
+%% We don't have to test that spwn and spawn_link actually spawns
+%% new processes - if they don't we can't run this suite!
+%% But we want to test that start and start_link really is
+%% synchronous, and we want to test that the crash report is ok.
+%%-----------------------------------------------------------------
+crash(Config) when is_list(Config) ->
+ error_logger:add_report_handler(?MODULE, self()),
+
+ Pid = proc_lib:spawn(?MODULE, sp1, []),
+ Pid ! die,
+ ?line Report = receive
+ {crash_report, Pid, Report0} -> Report0
+ after 2000 -> test_server:fail(no_crash_report)
+ end,
+ ?line proc_lib:format(Report),
+ ?line [PidRep, []] = Report,
+ ?line {value, {initial_call,{?MODULE,sp1,[]}}} =
+ lists:keysearch(initial_call, 1, PidRep),
+ Self = self(),
+ ?line {value, {ancestors,[Self]}} =
+ lists:keysearch(ancestors, 1, PidRep),
+ ?line {value, {error_info,{exit,die,_StackTrace1}}} =
+ lists:keysearch(error_info, 1, PidRep),
+
+ F = fun sp1/0,
+ Pid1 = proc_lib:spawn(node(), F),
+ Pid1 ! die,
+ ?line [PidRep1, []] = receive
+ {crash_report, Pid1, Report1} -> Report1
+ after 2000 -> test_server:fail(no_crash_report)
+ end,
+ ?line {value, {initial_call,{Fmod,Fname,[]}}} =
+ lists:keysearch(initial_call, 1, PidRep1),
+ ?line {module,Fmod} = erlang:fun_info(F, module),
+ ?line {name,Fname} = erlang:fun_info(F, name),
+ ?line {value, {ancestors,[Self]}} =
+ lists:keysearch(ancestors, 1, PidRep1),
+ ?line {value, {error_info,{exit,die,_StackTrace2}}} =
+ lists:keysearch(error_info, 1, PidRep1),
+
+ Pid2 = proc_lib:spawn(?MODULE, sp2, []),
+ test_server:sleep(100),
+ ?line {?MODULE,sp2,[]} = proc_lib:initial_call(Pid2),
+ ?line {?MODULE,sp2,0} = proc_lib:translate_initial_call(Pid2),
+ Pid2 ! die,
+ ?line [Pid2Rep, [{neighbour, LinkRep}]] =
+ receive
+ {crash_report, Pid2, Report2} -> Report2
+ after 2000 -> test_server:fail(no_crash_report)
+ end,
+ ?line {value, {initial_call,{?MODULE,sp2,[]}}} =
+ lists:keysearch(initial_call, 1, Pid2Rep),
+ ?line {value, {ancestors,[Self]}} =
+ lists:keysearch(ancestors, 1, Pid2Rep),
+ ?line {value, {error_info,{exit,die,_StackTrace3}}} =
+ lists:keysearch(error_info, 1, Pid2Rep),
+ ?line {value, {initial_call,{?MODULE,sp1,[]}}} =
+ lists:keysearch(initial_call, 1, LinkRep),
+
+ %% Make sure that we don't get a crash report if a process
+ %% terminates with reason 'shutdown' or reason {shutdown,Reason}.
+ ?line process_flag(trap_exit, true),
+ ?line Pid3 = proc_lib:spawn_link(erlang, apply,
+ [fun() -> exit(shutdown) end,[]]),
+
+ ?line Pid4 = proc_lib:spawn_link(erlang, apply,
+ [fun() -> exit({shutdown,{a,b,c}}) end,[]]),
+
+ ?line receive {'EXIT',Pid3,shutdown} -> ok end,
+ ?line receive {'EXIT',Pid4,{shutdown,{a,b,c}}} -> ok end,
+ ?line process_flag(trap_exit, false),
+
+ receive
+ Any ->
+ ?line ?t:fail({unexpected_message,Any})
+ after 2000 ->
+ ok
+ end.
+
+sync_start(suite) -> [sync_start_nolink, sync_start_link].
+
+sync_start_nolink(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp5, [self()]),
+ receive
+ {sync_started, F} ->
+ exit(F, kill),
+ test_server:fail(async_start)
+ after 1000 -> ok
+ end,
+ receive
+ {Pid2, init} ->
+ Pid2 ! go_on
+ end,
+ receive
+ {sync_started, _} -> ok
+ after 1000 ->
+ exit(Pid2, kill),
+ test_server:fail(no_sync_start)
+ end,
+ ok.
+
+sync_start_link(Config) when is_list(Config) ->
+ _Pid = spawn_link(?MODULE, sp3, [self()]),
+ receive
+ {sync_started, _} -> test_server:fail(async_start)
+ after 1000 -> ok
+ end,
+ receive
+ {Pid2, init} ->
+ Pid2 ! go_on
+ end,
+ receive
+ {sync_started, _} -> ok
+ after 1000 -> test_server:fail(no_sync_start)
+ end,
+ ok.
+
+spawn_opt(Config) when is_list(Config) ->
+ F = fun sp1/0,
+ {name,Fname} = erlang:fun_info(F, name),
+ FunMFArgs = {?MODULE,Fname,[]},
+ FunMFArity = {?MODULE,Fname,0},
+ ?line Pid1 = proc_lib:spawn_opt(node(), F, [{priority,low}]),
+ ?line Pid = proc_lib:spawn_opt(F, [{priority,low}]),
+ ?line test_server:sleep(100),
+ ?line FunMFArgs = proc_lib:initial_call(Pid),
+ ?line FunMFArity = proc_lib:translate_initial_call(Pid),
+ ?line Pid ! die,
+ ?line FunMFArgs = proc_lib:initial_call(Pid1),
+ ?line FunMFArity = proc_lib:translate_initial_call(Pid1),
+ ?line Pid1 ! die,
+ ok.
+
+
+sp1() ->
+ receive
+ die -> exit(die);
+ _ -> sp1()
+ end.
+
+sp2() ->
+ _Pid = proc_lib:spawn_link(?MODULE, sp1, []),
+ receive
+ die -> exit(die);
+ _ -> sp1()
+ end.
+
+sp3(Tester) ->
+ Pid = proc_lib:start_link(?MODULE, sp4, [self(), Tester]),
+ Tester ! {sync_started, Pid}.
+
+sp5(Tester) ->
+ Pid = proc_lib:start(?MODULE, sp4, [self(), Tester]),
+ Tester ! {sync_started, Pid}.
+
+sp4(Parent, Tester) ->
+ Tester ! {self(), init},
+ receive
+ go_on -> ok
+ end,
+ proc_lib:init_ack(Parent, self()).
+
+hibernate(Config) when is_list(Config) ->
+ Ref = make_ref(),
+ Self = self(),
+ LoopData = {Ref,Self},
+ ?line Pid = proc_lib:spawn_link(?MODULE, hib_loop, [LoopData]),
+
+ %% Just check that the child process can process and answer messages.
+ ?line Pid ! {Self,loop_data},
+ receive
+ {loop_data,LoopData} -> ok;
+ Unexpected0 ->
+ ?line io:format("Unexpected: ~p\n", [Unexpected0]),
+ ?line ?t:fail()
+ after 1000 ->
+ ?line io:format("Timeout"),
+ ?line ?t:fail()
+ end,
+
+ %% Hibernate the process.
+ ?line Pid ! hibernate,
+ erlang:yield(),
+ io:format("~p\n", [process_info(Pid, heap_size)]),
+
+
+ %% Send a message to the process...
+
+ ?line Pid ! {Self,loop_data},
+
+ %% ... expect first a wake up message from the process...
+ receive
+ {awaken,LoopData} -> ok;
+ Unexpected1 ->
+ ?line io:format("Unexpected: ~p\n", [Unexpected1]),
+ ?line ?t:fail()
+ after 1000 ->
+ ?line io:format("Timeout"),
+ ?line ?t:fail()
+ end,
+
+ %% ... followed by the answer to the actual request.
+ receive
+ {loop_data,LoopData} -> ok;
+ Unexpected2 ->
+ ?line io:format("Unexpected: ~p\n", [Unexpected2]),
+ ?line ?t:fail()
+ after 1000 ->
+ ?line io:format("Timeout"),
+ ?line ?t:fail()
+ end,
+
+ %% Test that errors are handled correctly after wake up from hibernation...
+
+ ?line process_flag(trap_exit, true),
+ ?line error_logger:add_report_handler(?MODULE, self()),
+ ?line Pid ! crash,
+
+ %% We should receive two messages. Especially in the SMP emulator,
+ %% we can't be sure of the message order, so sort the messages before
+ %% matching.
+
+ Messages = lists:sort(hib_receive_messages(2)),
+ io:format("~p", [Messages]),
+ ?line [{'EXIT',Pid,i_crashed},{crash_report,Pid,[Report,[]]}] = Messages,
+
+ %% Check that the initial_call has the expected format.
+ ?line {value,{initial_call,{?MODULE,hib_loop,[_]}}} =
+ lists:keysearch(initial_call, 1, Report),
+
+ ok.
+
+hib_loop(LoopData) ->
+ receive
+ hibernate ->
+ proc_lib:hibernate(?MODULE, awaken, [LoopData]);
+ {Pid,loop_data} ->
+ Pid ! {loop_data,LoopData};
+ crash ->
+ exit(i_crashed)
+ end,
+ hib_loop(LoopData).
+
+awaken({_,Parent}=LoopData) ->
+ Parent ! {awaken,LoopData},
+ hib_loop(LoopData).
+
+hib_receive_messages(0) -> [];
+hib_receive_messages(N) ->
+ receive
+ Any -> [Any|hib_receive_messages(N-1)]
+ end.
+
+otp_6345(suite) ->
+ [];
+otp_6345(doc) ->
+ ["'monitor' spawn_opt option"];
+otp_6345(Config) when is_list(Config) ->
+ Opts = [link,monitor],
+ {'EXIT', {badarg,[{proc_lib,check_for_monitor,_}|_Stack]}} =
+ (catch proc_lib:start(?MODULE, otp_6345_init, [self()],
+ 1000, Opts)),
+ ok.
+
+otp_6345_init(Parent) ->
+ proc_lib:init_ack(Parent, {ok, self()}),
+ otp_6345_loop().
+
+otp_6345_loop() ->
+ receive
+ _Msg ->
+ otp_6345_loop()
+ end.
+
+%%-----------------------------------------------------------------
+%% The error_logger handler used.
+%%-----------------------------------------------------------------
+init(Tester) ->
+ {ok, Tester}.
+
+handle_event({error_report, _GL, {Pid, crash_report, Report}}, Tester) ->
+ io:format("~s\n", [proc_lib:format(Report)]),
+ Tester ! {crash_report, Pid, Report},
+ {ok, Tester};
+handle_event(_Event, State) ->
+ {ok, State}.
+
+handle_info(_, State) ->
+ {ok, State}.
+
+handle_call(_Query, State) -> {ok, {error, bad_query}, State}.
+
+terminate(_Reason, State) ->
+ State.