diff options
Diffstat (limited to 'lib/stdlib')
-rw-r--r-- | lib/stdlib/doc/src/gen_event.xml | 10 | ||||
-rw-r--r-- | lib/stdlib/doc/src/gen_server.xml | 12 | ||||
-rw-r--r-- | lib/stdlib/doc/src/gen_statem.xml | 22 | ||||
-rw-r--r-- | lib/stdlib/src/erl_lint.erl | 73 | ||||
-rw-r--r-- | lib/stdlib/src/escript.erl | 3 | ||||
-rw-r--r-- | lib/stdlib/src/gen.erl | 10 | ||||
-rw-r--r-- | lib/stdlib/src/gen_event.erl | 71 | ||||
-rw-r--r-- | lib/stdlib/src/gen_fsm.erl | 81 | ||||
-rw-r--r-- | lib/stdlib/src/gen_server.erl | 106 | ||||
-rw-r--r-- | lib/stdlib/src/gen_statem.erl | 18 | ||||
-rw-r--r-- | lib/stdlib/src/proc_lib.erl | 27 | ||||
-rw-r--r-- | lib/stdlib/src/qlc.erl | 6 | ||||
-rw-r--r-- | lib/stdlib/test/dummy_h.erl | 6 | ||||
-rw-r--r-- | lib/stdlib/test/erl_lint_SUITE.erl | 63 | ||||
-rw-r--r-- | lib/stdlib/test/gen_event_SUITE.erl | 46 | ||||
-rw-r--r-- | lib/stdlib/test/gen_fsm_SUITE.erl | 46 | ||||
-rw-r--r-- | lib/stdlib/test/gen_server_SUITE.erl | 73 | ||||
-rw-r--r-- | lib/stdlib/test/gen_statem_SUITE.erl | 93 |
18 files changed, 603 insertions, 163 deletions
diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml index 09bde3e397..012737c390 100644 --- a/lib/stdlib/doc/src/gen_event.xml +++ b/lib/stdlib/doc/src/gen_event.xml @@ -358,7 +358,7 @@ gen_event:stop -----> Module:terminate/2 <v> Name = atom()</v> <v> GlobalName = ViaName = term()</v> <v>Options = [Option]</v> - <v> Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}</v> + <v> Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}</v> <v> Dbgs = [Dbg]</v> <v> Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}</v> <v> SOpts = [term()]</v> @@ -385,7 +385,7 @@ gen_event:stop -----> Module:terminate/2 <v> Name = atom()</v> <v> GlobalName = ViaName = term()</v> <v>Options = [Option]</v> - <v> Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}</v> + <v> Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}</v> <v> Dbgs = [Dbg]</v> <v> Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}</v> <v> SOpts = [term()]</v> @@ -419,6 +419,12 @@ gen_event:stop -----> Module:terminate/2 <seealso marker="kernel:global"><c>global</c></seealso>. Thus, <c>{via,global,GlobalName}</c> is a valid reference.</p> </item> + <item> + <p>If option <c>{hibernate_after,HibernateAfterTimeout}</c> is present, the <c>gen_event</c> + process awaits any message for <c>HibernateAfterTimeout</c> milliseconds and + if no message is received, the process goes into hibernation automatically + (by calling <seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>).</p> + </item> </list> <p>If the event manager is successfully created, the function returns <c>{ok,Pid}</c>, where <c>Pid</c> is the pid of diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml index a8006bb870..7d137fc772 100644 --- a/lib/stdlib/doc/src/gen_server.xml +++ b/lib/stdlib/doc/src/gen_server.xml @@ -199,7 +199,7 @@ gen_server:abcast -----> Module:handle_cast/2 <type> <v>Module = atom()</v> <v>Options = [Option]</v> - <v> Option = {debug,Dbgs}</v> + <v> Option = {debug,Dbgs} | {hibernate_after,HibernateAfterTimeout}</v> <v> Dbgs = [Dbg]</v> <v> Dbg = trace | log | statistics</v> <v> | {log_to_file,FileName} | {install,{Func,FuncState}}</v> @@ -334,7 +334,7 @@ gen_server:abcast -----> Module:handle_cast/2 <v>Module = atom()</v> <v>Args = term()</v> <v>Options = [Option]</v> - <v> Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}</v> + <v> Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}</v> <v> Dbgs = [Dbg]</v> <v> Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}</v> <v> SOpts = [term()]</v> @@ -364,7 +364,7 @@ gen_server:abcast -----> Module:handle_cast/2 <v>Module = atom()</v> <v>Args = term()</v> <v>Options = [Option]</v> - <v> Option = {debug,Dbgs} | {timeout,Time} | {spawn_opt,SOpts}</v> + <v> Option = {debug,Dbgs} | {timeout,Time} | {hibernate_after,HibernateAfterTimeout} | {spawn_opt,SOpts}</v> <v> Dbgs = [Dbg]</v> <v> Dbg = trace | log | statistics | {log_to_file,FileName} | {install,{Func,FuncState}}</v> <v> SOpts = [term()]</v> @@ -417,6 +417,12 @@ gen_server:abcast -----> Module:handle_cast/2 returns <c>{error,timeout}</c>.</p> </item> <item> + <p>If option <c>{hibernate_after,HibernateAfterTimeout}</c> is present, the <c>gen_server</c> + process awaits any message for <c>HibernateAfterTimeout</c> milliseconds and + if no message is received, the process goes into hibernation automatically + (by calling <seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>).</p> + </item> + <item> <p>If option <c>{debug,Dbgs}</c> is present, the corresponding <c>sys</c> function is called for each item in <c>Dbgs</c>; see diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml index 17a3a3c83c..1aac88c308 100644 --- a/lib/stdlib/doc/src/gen_statem.xml +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -453,6 +453,21 @@ handle_event(_, _, State, Data) -> </desc> </datatype> <datatype> + <name name="hibernate_after_opt"/> + <desc> + <p> + hibernate_after option that can be used when starting + a <c>gen_statem</c> server through, + <seealso marker="#enter_loop/4"><c>enter_loop/4-6</c></seealso>. + </p> + <p>If option<seealso marker="#type-hibernate_after_opt"><c>{hibernate_after,HibernateAfterTimeout}</c></seealso> is present, the <c>gen_statem</c> + process awaits any message for <c>HibernateAfterTimeout</c> milliseconds and + if no message is received, the process goes into hibernation automatically + (by calling <seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>). + </p> + </desc> + </datatype> + <datatype> <name name="start_opt"/> <desc> <p> @@ -1551,6 +1566,13 @@ handle_event(_, _, State, Data) -> </p> </item> <item> + <p>If option<seealso marker="#type-hibernate_after_opt"><c>{hibernate_after,HibernateAfterTimeout}</c></seealso> is present, the <c>gen_statem</c> + process awaits any message for <c>HibernateAfterTimeout</c> milliseconds and + if no message is received, the process goes into hibernation automatically + (by calling <seealso marker="proc_lib#hibernate/3"><c>proc_lib:hibernate/3</c></seealso>). + </p> + </item> + <item> <p> If option <seealso marker="#type-debug_opt"><c>{debug,Dbgs}</c></seealso> diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 7c40058dd8..d53a31db0d 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -92,6 +92,14 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> :: dict:dict(ta(), line()) }). + +%% Are we outside or inside a catch or try/catch? +-type catch_scope() :: 'none' + | 'after_old_catch' + | 'after_try' + | 'wrong_part_of_try' + | 'try_catch'. + %% Define the lint state record. %% 'called' and 'exports' contain {Line, {Function, Arity}}, %% the other function collections contain {Function, Arity}. @@ -135,7 +143,9 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> types = dict:new() %Type definitions :: dict:dict(ta(), #typeinfo{}), exp_types=gb_sets:empty() %Exported types - :: gb_sets:set(ta()) + :: gb_sets:set(ta()), + catch_scope = none %Inside/outside try or catch + :: catch_scope() }). -type lint_state() :: #lint{}. @@ -223,7 +233,15 @@ format_error({redefine_old_bif_import,{F,A}}) -> format_error({redefine_bif_import,{F,A}}) -> io_lib:format("import directive overrides auto-imported BIF ~w/~w~n" " - use \"-compile({no_auto_import,[~w/~w]}).\" to resolve name clash", [F,A,F,A]); - +format_error({get_stacktrace,wrong_part_of_try}) -> + "erlang:get_stacktrace/0 used in the wrong part of 'try' expression. " + "(Use it in the block between 'catch' and 'end'.)"; +format_error({get_stacktrace,after_old_catch}) -> + "erlang:get_stacktrace/0 used following an old-style 'catch' " + "may stop working in a future release. (Use it inside 'try'.)"; +format_error({get_stacktrace,after_try}) -> + "erlang:get_stacktrace/0 used following a 'try' expression " + "may stop working in a future release. (Use it inside 'try'.)"; format_error({deprecated, MFA, ReplacementMFA, Rel}) -> io_lib:format("~s is deprecated and will be removed in ~s; use ~s", [format_mfa(MFA), Rel, format_mfa(ReplacementMFA)]); @@ -568,7 +586,10 @@ start(File, Opts) -> false, Opts)}, {missing_spec_all, bool_option(warn_missing_spec_all, nowarn_missing_spec_all, - false, Opts)} + false, Opts)}, + {get_stacktrace, + bool_option(warn_get_stacktrace, nowarn_get_stacktrace, + true, Opts)} ], Enabled1 = [Category || {Category,true} <- Enabled0], Enabled = ordsets:from_list(Enabled1), @@ -1405,8 +1426,9 @@ call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func,file=File}=St) %% function(Line, Name, Arity, Clauses, State) -> State. function(Line, Name, Arity, Cs, St0) -> - St1 = define_function(Line, Name, Arity, St0#lint{func={Name,Arity}}), - clauses(Cs, St1). + St1 = St0#lint{func={Name,Arity},catch_scope=none}, + St2 = define_function(Line, Name, Arity, St1), + clauses(Cs, St2). -spec define_function(line(), atom(), arity(), lint_state()) -> lint_state(). @@ -2338,22 +2360,24 @@ expr({call,Line,F,As}, Vt, St0) -> expr({'try',Line,Es,Scs,Ccs,As}, Vt, St0) -> %% Currently, we don't allow any exports because later %% passes cannot handle exports in combination with 'after'. - {Evt0,St1} = exprs(Es, Vt, St0), + {Evt0,St1} = exprs(Es, Vt, St0#lint{catch_scope=wrong_part_of_try}), TryLine = {'try',Line}, Uvt = vtunsafe(TryLine, Evt0, Vt), Evt1 = vtupdate(Uvt, Evt0), - {Sccs,St2} = icrt_clauses(Scs++Ccs, TryLine, vtupdate(Evt1, Vt), St1), + {Sccs,St2} = try_clauses(Scs, Ccs, TryLine, + vtupdate(Evt1, Vt), St1), Rvt0 = Sccs, Rvt1 = vtupdate(vtunsafe(TryLine, Rvt0, Vt), Rvt0), Evt2 = vtmerge(Evt1, Rvt1), {Avt0,St} = exprs(As, vtupdate(Evt2, Vt), St2), Avt1 = vtupdate(vtunsafe(TryLine, Avt0, Vt), Avt0), Avt = vtmerge(Evt2, Avt1), - {Avt,St}; + {Avt,St#lint{catch_scope=after_try}}; expr({'catch',Line,E}, Vt, St0) -> %% No new variables added, flag new variables as unsafe. {Evt,St} = expr(E, Vt, St0), - {vtupdate(vtunsafe({'catch',Line}, Evt, Vt), Evt),St}; + {vtupdate(vtunsafe({'catch',Line}, Evt, Vt), Evt), + St#lint{catch_scope=after_old_catch}}; expr({match,_Line,P,E}, Vt, St0) -> {Evt,St1} = expr(E, Vt, St0), {Pvt,Bvt,St2} = pattern(P, vtupdate(Evt, Vt), St1), @@ -3173,6 +3197,17 @@ is_module_dialyzer_option(Option) -> error_handling,race_conditions,no_missing_calls, specdiffs,overspecs,underspecs,unknown]). +%% try_catch_clauses(Scs, Ccs, In, ImportVarTable, State) -> +%% {UpdVt,State}. + +try_clauses(Scs, Ccs, In, Vt, St0) -> + {Csvt0,St1} = icrt_clauses(Scs, Vt, St0), + St2 = St1#lint{catch_scope=try_catch}, + {Csvt1,St3} = icrt_clauses(Ccs, Vt, St2), + Csvt = Csvt0 ++ Csvt1, + UpdVt = icrt_export(Csvt, Vt, In, St3), + {UpdVt,St3}. + %% icrt_clauses(Clauses, In, ImportVarTable, State) -> %% {UpdVt,State}. @@ -3657,7 +3692,8 @@ has_wildcard_field([]) -> false. check_remote_function(Line, M, F, As, St0) -> St1 = deprecated_function(Line, M, F, As, St0), St2 = check_qlc_hrl(Line, M, F, As, St1), - format_function(Line, M, F, As, St2). + St3 = check_get_stacktrace(Line, M, F, As, St2), + format_function(Line, M, F, As, St3). %% check_qlc_hrl(Line, ModName, FuncName, [Arg], State) -> State %% Add warning if qlc:q/1,2 has been called but qlc.hrl has not @@ -3706,6 +3742,23 @@ deprecated_function(Line, M, F, As, St) -> St end. +check_get_stacktrace(Line, erlang, get_stacktrace, [], St) -> + case St of + #lint{catch_scope=none} -> + St; + #lint{catch_scope=try_catch} -> + St; + #lint{catch_scope=Scope} -> + case is_warn_enabled(get_stacktrace, St) of + false -> + St; + true -> + add_warning(Line, {get_stacktrace,Scope}, St) + end + end; +check_get_stacktrace(_, _, _, _, St) -> + St. + -dialyzer({no_match, deprecated_type/5}). deprecated_type(L, M, N, As, St) -> diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl index 6e8f780f7c..f2629a47c2 100644 --- a/lib/stdlib/src/escript.erl +++ b/lib/stdlib/src/escript.erl @@ -284,8 +284,9 @@ start(EscriptOptions) -> io:format("escript: ~s\n", [Str]), my_halt(127); _:Reason -> + Stk = erlang:get_stacktrace(), io:format("escript: Internal error: ~p\n", [Reason]), - io:format("~p\n", [erlang:get_stacktrace()]), + io:format("~p\n", [Stk]), my_halt(127) end. diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl index 597830cf9a..257c829801 100644 --- a/lib/stdlib/src/gen.erl +++ b/lib/stdlib/src/gen.erl @@ -26,7 +26,7 @@ %%% %%% The standard behaviour should export init_it/6. %%%----------------------------------------------------------------- --export([start/5, start/6, debug_options/2, +-export([start/5, start/6, debug_options/2, hibernate_after/1, name/1, unregister_name/1, get_proc_name/1, get_parent/0, call/3, call/4, reply/2, stop/1, stop/3]). @@ -408,6 +408,14 @@ spawn_opts(Options) -> [] end. +hibernate_after(Options) -> + case lists:keyfind(hibernate_after, 1, Options) of + {_,HibernateAfterTimeout} -> + HibernateAfterTimeout; + false -> + infinity + end. + debug_options(Name, Opts) -> case lists:keyfind(debug, 1, Opts) of {_,Options} -> diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl index 4c80464680..da2b0da3ca 100644 --- a/lib/stdlib/src/gen_event.erl +++ b/lib/stdlib/src/gen_event.erl @@ -37,7 +37,7 @@ stop/1, stop/3, notify/2, sync_notify/2, add_handler/3, add_sup_handler/3, delete_handler/3, swap_handler/3, - swap_sup_handler/3, which_handlers/1, call/3, call/4, wake_hib/4]). + swap_sup_handler/3, which_handlers/1, call/3, call/4, wake_hib/5]). -export([init_it/6, system_continue/3, @@ -186,8 +186,9 @@ init_it(Starter, Parent, Name0, _, _, Options) -> process_flag(trap_exit, true), Name = gen:name(Name0), Debug = gen:debug_options(Name, Options), + HibernateAfterTimeout = gen:hibernate_after(Options), proc_lib:init_ack(Starter, {ok, self()}), - loop(Parent, Name, [], Debug, false). + loop(Parent, Name, [], HibernateAfterTimeout, Debug, false). -spec add_handler(emgr_ref(), handler(), term()) -> term(). add_handler(M, Handler, Args) -> rpc(M, {add_handler, Handler, Args}). @@ -264,81 +265,83 @@ send(M, Cmd) -> M ! Cmd, ok. -loop(Parent, ServerName, MSL, Debug, true) -> - proc_lib:hibernate(?MODULE, wake_hib, [Parent, ServerName, MSL, Debug]); -loop(Parent, ServerName, MSL, Debug, _) -> - fetch_msg(Parent, ServerName, MSL, Debug, false). +loop(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, true) -> + proc_lib:hibernate(?MODULE, wake_hib, [Parent, ServerName, MSL, HibernateAfterTimeout, Debug]); +loop(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, _) -> + fetch_msg(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, false). -wake_hib(Parent, ServerName, MSL, Debug) -> - fetch_msg(Parent, ServerName, MSL, Debug, true). +wake_hib(Parent, ServerName, MSL, HibernateAfterTimeout, Debug) -> + fetch_msg(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, true). -fetch_msg(Parent, ServerName, MSL, Debug, Hib) -> +fetch_msg(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, Hib) -> receive {system, From, Req} -> sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug, - [ServerName, MSL, Hib],Hib); + [ServerName, MSL, HibernateAfterTimeout, Hib],Hib); {'EXIT', Parent, Reason} -> terminate_server(Reason, Parent, MSL, ServerName); Msg when Debug =:= [] -> - handle_msg(Msg, Parent, ServerName, MSL, []); + handle_msg(Msg, Parent, ServerName, MSL, HibernateAfterTimeout, []); Msg -> Debug1 = sys:handle_debug(Debug, fun print_event/3, ServerName, {in, Msg}), - handle_msg(Msg, Parent, ServerName, MSL, Debug1) + handle_msg(Msg, Parent, ServerName, MSL, HibernateAfterTimeout, Debug1) + after HibernateAfterTimeout -> + loop(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, true) end. -handle_msg(Msg, Parent, ServerName, MSL, Debug) -> +handle_msg(Msg, Parent, ServerName, MSL, HibernateAfterTimeout, Debug) -> case Msg of {notify, Event} -> {Hib,MSL1} = server_notify(Event, handle_event, MSL, ServerName), - loop(Parent, ServerName, MSL1, Debug, Hib); + loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, Hib); {_From, Tag, {sync_notify, Event}} -> {Hib, MSL1} = server_notify(Event, handle_event, MSL, ServerName), reply(Tag, ok), - loop(Parent, ServerName, MSL1, Debug, Hib); + loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, Hib); {'EXIT', From, Reason} -> MSL1 = handle_exit(From, Reason, MSL, ServerName), - loop(Parent, ServerName, MSL1, Debug, false); + loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, false); {_From, Tag, {call, Handler, Query}} -> {Hib, Reply, MSL1} = server_call(Handler, Query, MSL, ServerName), reply(Tag, Reply), - loop(Parent, ServerName, MSL1, Debug, Hib); + loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, Hib); {_From, Tag, {add_handler, Handler, Args}} -> {Hib, Reply, MSL1} = server_add_handler(Handler, Args, MSL), reply(Tag, Reply), - loop(Parent, ServerName, MSL1, Debug, Hib); + loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, Hib); {_From, Tag, {add_sup_handler, Handler, Args, SupP}} -> {Hib, Reply, MSL1} = server_add_sup_handler(Handler, Args, MSL, SupP), reply(Tag, Reply), - loop(Parent, ServerName, MSL1, Debug, Hib); + loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, Hib); {_From, Tag, {delete_handler, Handler, Args}} -> {Reply, MSL1} = server_delete_handler(Handler, Args, MSL, ServerName), reply(Tag, Reply), - loop(Parent, ServerName, MSL1, Debug, false); + loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, false); {_From, Tag, {swap_handler, Handler1, Args1, Handler2, Args2}} -> {Hib, Reply, MSL1} = server_swap_handler(Handler1, Args1, Handler2, Args2, MSL, ServerName), reply(Tag, Reply), - loop(Parent, ServerName, MSL1, Debug, Hib); + loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, Hib); {_From, Tag, {swap_sup_handler, Handler1, Args1, Handler2, Args2, Sup}} -> {Hib, Reply, MSL1} = server_swap_handler(Handler1, Args1, Handler2, Args2, MSL, Sup, ServerName), reply(Tag, Reply), - loop(Parent, ServerName, MSL1, Debug, Hib); + loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, Hib); {_From, Tag, stop} -> catch terminate_server(normal, Parent, MSL, ServerName), reply(Tag, ok); {_From, Tag, which_handlers} -> reply(Tag, the_handlers(MSL)), - loop(Parent, ServerName, MSL, Debug, false); + loop(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, false); {_From, Tag, get_modules} -> reply(Tag, get_modules(MSL)), - loop(Parent, ServerName, MSL, Debug, false); + loop(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, false); Other -> {Hib, MSL1} = server_notify(Other, handle_info, MSL, ServerName), - loop(Parent, ServerName, MSL1, Debug, Hib) + loop(Parent, ServerName, MSL1, HibernateAfterTimeout, Debug, Hib) end. terminate_server(Reason, Parent, MSL, ServerName) -> @@ -392,18 +395,18 @@ terminate_supervised(Pid, Reason, MSL, SName) -> %%----------------------------------------------------------------- %% Callback functions for system messages handling. %%----------------------------------------------------------------- -system_continue(Parent, Debug, [ServerName, MSL, Hib]) -> - loop(Parent, ServerName, MSL, Debug, Hib). +system_continue(Parent, Debug, [ServerName, MSL, HibernateAfterTimeout, Hib]) -> + loop(Parent, ServerName, MSL, HibernateAfterTimeout, Debug, Hib). -spec system_terminate(_, _, _, [_]) -> no_return(). -system_terminate(Reason, Parent, _Debug, [ServerName, MSL, _Hib]) -> +system_terminate(Reason, Parent, _Debug, [ServerName, MSL, _HibernateAfterTimeout, _Hib]) -> terminate_server(Reason, Parent, MSL, ServerName). %%----------------------------------------------------------------- %% Module here is sent in the system msg change_code. It specifies %% which module should be changed. %%----------------------------------------------------------------- -system_code_change([ServerName, MSL, Hib], Module, OldVsn, Extra) -> +system_code_change([ServerName, MSL, HibernateAfterTimeout, Hib], Module, OldVsn, Extra) -> MSL1 = lists:zf(fun(H) when H#handler.module =:= Module -> {ok, NewState} = Module:code_change(OldVsn, @@ -412,12 +415,12 @@ system_code_change([ServerName, MSL, Hib], Module, OldVsn, Extra) -> (_) -> true end, MSL), - {ok, [ServerName, MSL1, Hib]}. + {ok, [ServerName, MSL1, HibernateAfterTimeout, Hib]}. -system_get_state([_ServerName, MSL, _Hib]) -> +system_get_state([_ServerName, MSL, _HibernateAfterTimeout, _Hib]) -> {ok, [{Mod,Id,State} || #handler{module=Mod, id=Id, state=State} <- MSL]}. -system_replace_state(StateFun, [ServerName, MSL, Hib]) -> +system_replace_state(StateFun, [ServerName, MSL, HibernateAfterTimeout, Hib]) -> {NMSL, NStates} = lists:unzip([begin Cur = {Mod,Id,State}, @@ -429,7 +432,7 @@ system_replace_state(StateFun, [ServerName, MSL, Hib]) -> {HS, Cur} end end || #handler{module=Mod, id=Id, state=State}=HS <- MSL]), - {ok, NStates, [ServerName, NMSL, Hib]}. + {ok, NStates, [ServerName, NMSL, HibernateAfterTimeout, Hib]}. %%----------------------------------------------------------------- %% Format debug messages. Print them as the call-back module sees @@ -798,7 +801,7 @@ get_modules(MSL) -> %% Status information %%----------------------------------------------------------------- format_status(Opt, StatusData) -> - [PDict, SysState, Parent, _Debug, [ServerName, MSL, _Hib]] = StatusData, + [PDict, SysState, Parent, _Debug, [ServerName, MSL, _HibernateAfterTimeout, _Hib]] = StatusData, Header = gen:format_status_header("Status for event handler", ServerName), FmtMSL = [case erlang:function_exported(Mod, format_status, 2) of diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl index f9d4286a7c..9ef0ca818c 100644 --- a/lib/stdlib/src/gen_fsm.erl +++ b/lib/stdlib/src/gen_fsm.erl @@ -113,7 +113,7 @@ sync_send_all_state_event/2, sync_send_all_state_event/3, reply/2, start_timer/2,send_event_after/2,cancel_timer/1, - enter_loop/4, enter_loop/5, enter_loop/6, wake_hib/6]). + enter_loop/4, enter_loop/5, enter_loop/6, wake_hib/7]). %% Internal exports -export([init_it/6, @@ -329,7 +329,8 @@ enter_loop(Mod, Options, StateName, StateData, ServerName, Timeout) -> Name = gen:get_proc_name(ServerName), Parent = gen:get_parent(), Debug = gen:debug_options(Name, Options), - loop(Parent, Name, StateName, StateData, Mod, Timeout, Debug). + HibernateAfterTimeout = gen:hibernate_after(Options), + loop(Parent, Name, StateName, StateData, Mod, Timeout, HibernateAfterTimeout, Debug). %%% --------------------------------------------------- %%% Initiate the new process. @@ -343,13 +344,14 @@ init_it(Starter, self, Name, Mod, Args, Options) -> init_it(Starter, Parent, Name0, Mod, Args, Options) -> Name = gen:name(Name0), Debug = gen:debug_options(Name, Options), - case catch Mod:init(Args) of + HibernateAfterTimeout = gen:hibernate_after(Options), + case catch Mod:init(Args) of {ok, StateName, StateData} -> proc_lib:init_ack(Starter, {ok, self()}), - loop(Parent, Name, StateName, StateData, Mod, infinity, Debug); + loop(Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, Debug); {ok, StateName, StateData, Timeout} -> proc_lib:init_ack(Starter, {ok, self()}), - loop(Parent, Name, StateName, StateData, Mod, Timeout, Debug); + loop(Parent, Name, StateName, StateData, Mod, Timeout, HibernateAfterTimeout, Debug); {stop, Reason} -> gen:unregister_name(Name0), proc_lib:init_ack(Starter, {error, Reason}), @@ -371,68 +373,77 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) -> %%----------------------------------------------------------------- %% The MAIN loop %%----------------------------------------------------------------- -loop(Parent, Name, StateName, StateData, Mod, hibernate, Debug) -> +loop(Parent, Name, StateName, StateData, Mod, hibernate, HibernateAfterTimeout, Debug) -> proc_lib:hibernate(?MODULE,wake_hib, - [Parent, Name, StateName, StateData, Mod, + [Parent, Name, StateName, StateData, Mod, HibernateAfterTimeout, Debug]); -loop(Parent, Name, StateName, StateData, Mod, Time, Debug) -> + +loop(Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, Debug) -> + receive + Msg -> + decode_msg(Msg,Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, Debug, false) + after HibernateAfterTimeout -> + loop(Parent, Name, StateName, StateData, Mod, hibernate, HibernateAfterTimeout, Debug) + end; + +loop(Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout, Debug) -> Msg = receive Input -> Input after Time -> {'$gen_event', timeout} end, - decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, Debug, false). + decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout, Debug, false). -wake_hib(Parent, Name, StateName, StateData, Mod, Debug) -> +wake_hib(Parent, Name, StateName, StateData, Mod, HibernateAfterTimeout, Debug) -> Msg = receive Input -> Input end, - decode_msg(Msg, Parent, Name, StateName, StateData, Mod, hibernate, Debug, true). + decode_msg(Msg, Parent, Name, StateName, StateData, Mod, hibernate, HibernateAfterTimeout, Debug, true). -decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, Debug, Hib) -> +decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout, Debug, Hib) -> case Msg of {system, From, Req} -> sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug, - [Name, StateName, StateData, Mod, Time], Hib); + [Name, StateName, StateData, Mod, Time, HibernateAfterTimeout], Hib); {'EXIT', Parent, Reason} -> terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug); _Msg when Debug =:= [] -> - handle_msg(Msg, Parent, Name, StateName, StateData, Mod, Time); + handle_msg(Msg, Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout); _Msg -> Debug1 = sys:handle_debug(Debug, fun print_event/3, {Name, StateName}, {in, Msg}), handle_msg(Msg, Parent, Name, StateName, StateData, - Mod, Time, Debug1) + Mod, Time, HibernateAfterTimeout, Debug1) end. %%----------------------------------------------------------------- %% Callback functions for system messages handling. %%----------------------------------------------------------------- -system_continue(Parent, Debug, [Name, StateName, StateData, Mod, Time]) -> - loop(Parent, Name, StateName, StateData, Mod, Time, Debug). +system_continue(Parent, Debug, [Name, StateName, StateData, Mod, Time, HibernateAfterTimeout]) -> + loop(Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout, Debug). -spec system_terminate(term(), _, _, [term(),...]) -> no_return(). system_terminate(Reason, _Parent, Debug, - [Name, StateName, StateData, Mod, _Time]) -> + [Name, StateName, StateData, Mod, _Time, _HibernateAfterTimeout]) -> terminate(Reason, Name, [], Mod, StateName, StateData, Debug). -system_code_change([Name, StateName, StateData, Mod, Time], +system_code_change([Name, StateName, StateData, Mod, Time, HibernateAfterTimeout], _Module, OldVsn, Extra) -> case catch Mod:code_change(OldVsn, StateName, StateData, Extra) of {ok, NewStateName, NewStateData} -> - {ok, [Name, NewStateName, NewStateData, Mod, Time]}; + {ok, [Name, NewStateName, NewStateData, Mod, Time, HibernateAfterTimeout]}; Else -> Else end. -system_get_state([_Name, StateName, StateData, _Mod, _Time]) -> +system_get_state([_Name, StateName, StateData, _Mod, _Time, _HibernateAfterTimeout]) -> {ok, {StateName, StateData}}. -system_replace_state(StateFun, [Name, StateName, StateData, Mod, Time]) -> +system_replace_state(StateFun, [Name, StateName, StateData, Mod, Time, HibernateAfterTimeout]) -> Result = {NStateName, NStateData} = StateFun({StateName, StateData}), - {ok, Result, [Name, NStateName, NStateData, Mod, Time]}. + {ok, Result, [Name, NStateName, NStateData, Mod, Time, HibernateAfterTimeout]}. %%----------------------------------------------------------------- %% Format debug messages. Print them as the call-back module sees @@ -467,19 +478,19 @@ print_event(Dev, return, {Name, StateName}) -> io:format(Dev, "*DBG* ~p switched to state ~w~n", [Name, StateName]). -handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time) -> %No debug here +handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, HibernateAfterTimeout) -> %No debug here From = from(Msg), case catch dispatch(Msg, Mod, StateName, StateData) of {next_state, NStateName, NStateData} -> - loop(Parent, Name, NStateName, NStateData, Mod, infinity, []); + loop(Parent, Name, NStateName, NStateData, Mod, infinity, HibernateAfterTimeout, []); {next_state, NStateName, NStateData, Time1} -> - loop(Parent, Name, NStateName, NStateData, Mod, Time1, []); + loop(Parent, Name, NStateName, NStateData, Mod, Time1, HibernateAfterTimeout, []); {reply, Reply, NStateName, NStateData} when From =/= undefined -> reply(From, Reply), - loop(Parent, Name, NStateName, NStateData, Mod, infinity, []); + loop(Parent, Name, NStateName, NStateData, Mod, infinity, HibernateAfterTimeout, []); {reply, Reply, NStateName, NStateData, Time1} when From =/= undefined -> reply(From, Reply), - loop(Parent, Name, NStateName, NStateData, Mod, Time1, []); + loop(Parent, Name, NStateName, NStateData, Mod, Time1, HibernateAfterTimeout, []); {stop, Reason, NStateData} -> terminate(Reason, Name, Msg, Mod, StateName, NStateData, []); {stop, Reason, Reply, NStateData} when From =/= undefined -> @@ -490,7 +501,7 @@ handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time) -> %No debug her {'EXIT', {undef, [{Mod, handle_info, [_,_,_], _}|_]}} -> error_logger:warning_msg("** Undefined handle_info in ~p~n" "** Unhandled message: ~p~n", [Mod, Msg]), - loop(Parent, Name, StateName, StateData, Mod, infinity, []); + loop(Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, []); {'EXIT', What} -> terminate(What, Name, Msg, Mod, StateName, StateData, []); Reply -> @@ -498,23 +509,23 @@ handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time) -> %No debug her Name, Msg, Mod, StateName, StateData, []) end. -handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, Debug) -> +handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, HibernateAfterTimeout, Debug) -> From = from(Msg), case catch dispatch(Msg, Mod, StateName, StateData) of {next_state, NStateName, NStateData} -> Debug1 = sys:handle_debug(Debug, fun print_event/3, {Name, NStateName}, return), - loop(Parent, Name, NStateName, NStateData, Mod, infinity, Debug1); + loop(Parent, Name, NStateName, NStateData, Mod, infinity, HibernateAfterTimeout, Debug1); {next_state, NStateName, NStateData, Time1} -> Debug1 = sys:handle_debug(Debug, fun print_event/3, {Name, NStateName}, return), - loop(Parent, Name, NStateName, NStateData, Mod, Time1, Debug1); + loop(Parent, Name, NStateName, NStateData, Mod, Time1, HibernateAfterTimeout, Debug1); {reply, Reply, NStateName, NStateData} when From =/= undefined -> Debug1 = reply(Name, From, Reply, Debug, NStateName), - loop(Parent, Name, NStateName, NStateData, Mod, infinity, Debug1); + loop(Parent, Name, NStateName, NStateData, Mod, infinity, HibernateAfterTimeout, Debug1); {reply, Reply, NStateName, NStateData, Time1} when From =/= undefined -> Debug1 = reply(Name, From, Reply, Debug, NStateName), - loop(Parent, Name, NStateName, NStateData, Mod, Time1, Debug1); + loop(Parent, Name, NStateName, NStateData, Mod, Time1, HibernateAfterTimeout, Debug1); {stop, Reason, NStateData} -> terminate(Reason, Name, Msg, Mod, StateName, NStateData, Debug); {stop, Reason, Reply, NStateData} when From =/= undefined -> @@ -645,7 +656,7 @@ get_msg(Msg) -> Msg. %% Status information %%----------------------------------------------------------------- format_status(Opt, StatusData) -> - [PDict, SysState, Parent, Debug, [Name, StateName, StateData, Mod, _Time]] = + [PDict, SysState, Parent, Debug, [Name, StateName, StateData, Mod, _Time, _HibernateAfterTimeout]] = StatusData, Header = gen:format_status_header("Status for state machine", Name), diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl index e628fec00f..ba0a7ae8e5 100644 --- a/lib/stdlib/src/gen_server.erl +++ b/lib/stdlib/src/gen_server.erl @@ -94,7 +94,7 @@ cast/2, reply/2, abcast/2, abcast/3, multi_call/2, multi_call/3, multi_call/4, - enter_loop/3, enter_loop/4, enter_loop/5, wake_hib/5]). + enter_loop/3, enter_loop/4, enter_loop/5, wake_hib/6]). %% System exports -export([system_continue/3, @@ -307,7 +307,8 @@ enter_loop(Mod, Options, State, ServerName, Timeout) -> Name = gen:get_proc_name(ServerName), Parent = gen:get_parent(), Debug = gen:debug_options(Name, Options), - loop(Parent, Name, State, Mod, Timeout, Debug). + HibernateAfterTimeout = gen:hibernate_after(Options), + loop(Parent, Name, State, Mod, Timeout, HibernateAfterTimeout, Debug). %%%======================================================================== %%% Gen-callback functions @@ -325,13 +326,14 @@ init_it(Starter, self, Name, Mod, Args, Options) -> init_it(Starter, Parent, Name0, Mod, Args, Options) -> Name = gen:name(Name0), Debug = gen:debug_options(Name, Options), + HibernateAfterTimeout = gen:hibernate_after(Options), case catch Mod:init(Args) of {ok, State} -> proc_lib:init_ack(Starter, {ok, self()}), - loop(Parent, Name, State, Mod, infinity, Debug); + loop(Parent, Name, State, Mod, infinity, HibernateAfterTimeout, Debug); {ok, State, Timeout} -> proc_lib:init_ack(Starter, {ok, self()}), - loop(Parent, Name, State, Mod, Timeout, Debug); + loop(Parent, Name, State, Mod, Timeout, HibernateAfterTimeout, Debug); {stop, Reason} -> %% For consistency, we must make sure that the %% registered name (if any) is unregistered before @@ -362,37 +364,46 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) -> %%% --------------------------------------------------- %%% The MAIN loop. %%% --------------------------------------------------- -loop(Parent, Name, State, Mod, hibernate, Debug) -> - proc_lib:hibernate(?MODULE,wake_hib,[Parent, Name, State, Mod, Debug]); -loop(Parent, Name, State, Mod, Time, Debug) -> +loop(Parent, Name, State, Mod, hibernate, HibernateAfterTimeout, Debug) -> + proc_lib:hibernate(?MODULE,wake_hib,[Parent, Name, State, Mod, HibernateAfterTimeout, Debug]); + +loop(Parent, Name, State, Mod, infinity, HibernateAfterTimeout, Debug) -> + receive + Msg -> + decode_msg(Msg, Parent, Name, State, Mod, infinity, HibernateAfterTimeout, Debug, false) + after HibernateAfterTimeout -> + loop(Parent, Name, State, Mod, hibernate, HibernateAfterTimeout, Debug) + end; + +loop(Parent, Name, State, Mod, Time, HibernateAfterTimeout, Debug) -> Msg = receive Input -> Input after Time -> timeout end, - decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, false). + decode_msg(Msg, Parent, Name, State, Mod, Time, HibernateAfterTimeout, Debug, false). -wake_hib(Parent, Name, State, Mod, Debug) -> +wake_hib(Parent, Name, State, Mod, HibernateAfterTimeout, Debug) -> Msg = receive Input -> Input end, - decode_msg(Msg, Parent, Name, State, Mod, hibernate, Debug, true). + decode_msg(Msg, Parent, Name, State, Mod, hibernate, HibernateAfterTimeout, Debug, true). -decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) -> +decode_msg(Msg, Parent, Name, State, Mod, Time, HibernateAfterTimeout, Debug, Hib) -> case Msg of {system, From, Req} -> sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug, - [Name, State, Mod, Time], Hib); + [Name, State, Mod, Time, HibernateAfterTimeout], Hib); {'EXIT', Parent, Reason} -> terminate(Reason, Name, undefined, Msg, Mod, State, Debug); _Msg when Debug =:= [] -> - handle_msg(Msg, Parent, Name, State, Mod); + handle_msg(Msg, Parent, Name, State, Mod, HibernateAfterTimeout); _Msg -> Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {in, Msg}), - handle_msg(Msg, Parent, Name, State, Mod, Debug1) + handle_msg(Msg, Parent, Name, State, Mod, HibernateAfterTimeout, Debug1) end. %%% --------------------------------------------------- @@ -659,65 +670,65 @@ try_terminate(Mod, Reason, State) -> %%% Message handling functions %%% --------------------------------------------------- -handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod) -> +handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, HibernateAfterTimeout) -> Result = try_handle_call(Mod, Msg, From, State), case Result of {ok, {reply, Reply, NState}} -> reply(From, Reply), - loop(Parent, Name, NState, Mod, infinity, []); + loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, []); {ok, {reply, Reply, NState, Time1}} -> reply(From, Reply), - loop(Parent, Name, NState, Mod, Time1, []); + loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, []); {ok, {noreply, NState}} -> - loop(Parent, Name, NState, Mod, infinity, []); + loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, []); {ok, {noreply, NState, Time1}} -> - loop(Parent, Name, NState, Mod, Time1, []); + loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, []); {ok, {stop, Reason, Reply, NState}} -> {'EXIT', R} = (catch terminate(Reason, Name, From, Msg, Mod, NState, [])), reply(From, Reply), exit(R); - Other -> handle_common_reply(Other, Parent, Name, From, Msg, Mod, State) + Other -> handle_common_reply(Other, Parent, Name, From, Msg, Mod, HibernateAfterTimeout, State) end; -handle_msg(Msg, Parent, Name, State, Mod) -> +handle_msg(Msg, Parent, Name, State, Mod, HibernateAfterTimeout) -> Reply = try_dispatch(Msg, Mod, State), - handle_common_reply(Reply, Parent, Name, undefined, Msg, Mod, State). + handle_common_reply(Reply, Parent, Name, undefined, Msg, Mod, HibernateAfterTimeout, State). -handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, Debug) -> +handle_msg({'$gen_call', From, Msg}, Parent, Name, State, Mod, HibernateAfterTimeout, Debug) -> Result = try_handle_call(Mod, Msg, From, State), case Result of {ok, {reply, Reply, NState}} -> Debug1 = reply(Name, From, Reply, NState, Debug), - loop(Parent, Name, NState, Mod, infinity, Debug1); + loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, Debug1); {ok, {reply, Reply, NState, Time1}} -> Debug1 = reply(Name, From, Reply, NState, Debug), - loop(Parent, Name, NState, Mod, Time1, Debug1); + loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, Debug1); {ok, {noreply, NState}} -> Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {noreply, NState}), - loop(Parent, Name, NState, Mod, infinity, Debug1); + loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, Debug1); {ok, {noreply, NState, Time1}} -> Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {noreply, NState}), - loop(Parent, Name, NState, Mod, Time1, Debug1); + loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, Debug1); {ok, {stop, Reason, Reply, NState}} -> {'EXIT', R} = (catch terminate(Reason, Name, From, Msg, Mod, NState, Debug)), _ = reply(Name, From, Reply, NState, Debug), exit(R); Other -> - handle_common_reply(Other, Parent, Name, From, Msg, Mod, State, Debug) + handle_common_reply(Other, Parent, Name, From, Msg, Mod, HibernateAfterTimeout, State, Debug) end; -handle_msg(Msg, Parent, Name, State, Mod, Debug) -> +handle_msg(Msg, Parent, Name, State, Mod, HibernateAfterTimeout, Debug) -> Reply = try_dispatch(Msg, Mod, State), - handle_common_reply(Reply, Parent, Name, undefined, Msg, Mod, State, Debug). + handle_common_reply(Reply, Parent, Name, undefined, Msg, Mod, HibernateAfterTimeout, State, Debug). -handle_common_reply(Reply, Parent, Name, From, Msg, Mod, State) -> +handle_common_reply(Reply, Parent, Name, From, Msg, Mod, HibernateAfterTimeout, State) -> case Reply of {ok, {noreply, NState}} -> - loop(Parent, Name, NState, Mod, infinity, []); + loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, []); {ok, {noreply, NState, Time1}} -> - loop(Parent, Name, NState, Mod, Time1, []); + loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, []); {ok, {stop, Reason, NState}} -> terminate(Reason, Name, From, Msg, Mod, NState, []); {'EXIT', ExitReason, ReportReason} -> @@ -726,16 +737,16 @@ handle_common_reply(Reply, Parent, Name, From, Msg, Mod, State) -> terminate({bad_return_value, BadReply}, Name, From, Msg, Mod, State, []) end. -handle_common_reply(Reply, Parent, Name, From, Msg, Mod, State, Debug) -> +handle_common_reply(Reply, Parent, Name, From, Msg, Mod, HibernateAfterTimeout, State, Debug) -> case Reply of {ok, {noreply, NState}} -> Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {noreply, NState}), - loop(Parent, Name, NState, Mod, infinity, Debug1); + loop(Parent, Name, NState, Mod, infinity, HibernateAfterTimeout, Debug1); {ok, {noreply, NState, Time1}} -> Debug1 = sys:handle_debug(Debug, fun print_event/3, Name, {noreply, NState}), - loop(Parent, Name, NState, Mod, Time1, Debug1); + loop(Parent, Name, NState, Mod, Time1, HibernateAfterTimeout, Debug1); {ok, {stop, Reason, NState}} -> terminate(Reason, Name, From, Msg, Mod, NState, Debug); {'EXIT', ExitReason, ReportReason} -> @@ -753,26 +764,26 @@ reply(Name, {To, Tag}, Reply, State, Debug) -> %%----------------------------------------------------------------- %% Callback functions for system messages handling. %%----------------------------------------------------------------- -system_continue(Parent, Debug, [Name, State, Mod, Time]) -> - loop(Parent, Name, State, Mod, Time, Debug). +system_continue(Parent, Debug, [Name, State, Mod, Time, HibernateAfterTimeout]) -> + loop(Parent, Name, State, Mod, Time, HibernateAfterTimeout, Debug). -spec system_terminate(_, _, _, [_]) -> no_return(). -system_terminate(Reason, _Parent, Debug, [Name, State, Mod, _Time]) -> +system_terminate(Reason, _Parent, Debug, [Name, State, Mod, _Time, _HibernateAfterTimeout]) -> terminate(Reason, Name, undefined, [], Mod, State, Debug). -system_code_change([Name, State, Mod, Time], _Module, OldVsn, Extra) -> +system_code_change([Name, State, Mod, Time, HibernateAfterTimeout], _Module, OldVsn, Extra) -> case catch Mod:code_change(OldVsn, State, Extra) of - {ok, NewState} -> {ok, [Name, NewState, Mod, Time]}; + {ok, NewState} -> {ok, [Name, NewState, Mod, Time, HibernateAfterTimeout]}; Else -> Else end. -system_get_state([_Name, State, _Mod, _Time]) -> +system_get_state([_Name, State, _Mod, _Time, _HibernateAfterTimeout]) -> {ok, State}. -system_replace_state(StateFun, [Name, State, Mod, Time]) -> +system_replace_state(StateFun, [Name, State, Mod, Time, HibernateAfterTimeout]) -> NState = StateFun(State), - {ok, NState, [Name, NState, Mod, Time]}. + {ok, NState, [Name, NState, Mod, Time, HibernateAfterTimeout]}. %%----------------------------------------------------------------- %% Format debug messages. Print them as the call-back module sees @@ -802,10 +813,10 @@ print_event(Dev, Event, Name) -> %%% Terminate the server. %%% --------------------------------------------------- + -spec terminate(_, _, _, _, _, _, _) -> no_return(). terminate(Reason, Name, From, Msg, Mod, State, Debug) -> terminate(Reason, Reason, Name, From, Msg, Mod, State, Debug). - -spec terminate(_, _, _, _, _, _, _, _) -> no_return(). terminate(ExitReason, ReportReason, Name, From, Msg, Mod, State, Debug) -> Reply = try_terminate(Mod, ExitReason, State), @@ -851,7 +862,7 @@ error_info(Reason, Name, From, Msg, State, Debug) -> end; _ -> Reason - end, + end, {ClientFmt, ClientArgs} = client_stacktrace(From), format("** Generic server ~p terminating \n" "** Last message in was ~p~n" @@ -860,7 +871,6 @@ error_info(Reason, Name, From, Msg, State, Debug) -> [Name, Msg, State, Reason1] ++ ClientArgs), sys:print_log(Debug), ok. - client_stacktrace(undefined) -> {"", []}; client_stacktrace({From, _Tag}) -> @@ -885,7 +895,7 @@ client_stacktrace(From) when is_pid(From) -> %% Status information %%----------------------------------------------------------------- format_status(Opt, StatusData) -> - [PDict, SysState, Parent, Debug, [Name, State, Mod, _Time]] = StatusData, + [PDict, SysState, Parent, Debug, [Name, State, Mod, _Time, _HibernateAfterTimeout]] = StatusData, Header = gen:format_status_header("Status for generic server", Name), Log = sys:get_debug(log, Debug, []), Specfic = case format_status(Opt, Mod, PDict, State) of diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl index 6f566b8beb..86109f04b4 100644 --- a/lib/stdlib/src/gen_statem.erl +++ b/lib/stdlib/src/gen_statem.erl @@ -369,9 +369,12 @@ event_type(Type) -> Dbgs :: ['trace' | 'log' | 'statistics' | 'debug' | {'logfile', string()}]}. +-type hibernate_after_opt() :: + {'hibernate_after', HibernateAfterTimeout :: timeout()}. -type start_opt() :: debug_opt() | {'timeout', Time :: timeout()} + | hibernate_after_opt() | {'spawn_opt', [proc_lib:spawn_option()]}. -type start_ret() :: {'ok', pid()} | 'ignore' | {'error', term()}. @@ -544,14 +547,14 @@ reply({To,Tag}, Reply) when is_pid(To) -> %% started by proc_lib into a state machine using %% the same arguments as you would have returned from init/1 -spec enter_loop( - Module :: module(), Opts :: [debug_opt()], + Module :: module(), Opts :: [debug_opt() | hibernate_after_opt()], State :: state(), Data :: data()) -> no_return(). enter_loop(Module, Opts, State, Data) -> enter_loop(Module, Opts, State, Data, self()). %% -spec enter_loop( - Module :: module(), Opts :: [debug_opt()], + Module :: module(), Opts :: [debug_opt() | hibernate_after_opt()], State :: state(), Data :: data(), Server_or_Actions :: server_name() | pid() | [action()]) -> @@ -565,7 +568,7 @@ enter_loop(Module, Opts, State, Data, Server_or_Actions) -> end. %% -spec enter_loop( - Module :: module(), Opts :: [debug_opt()], + Module :: module(), Opts :: [debug_opt() | hibernate_after_opt()], State :: state(), Data :: data(), Server :: server_name() | pid(), Actions :: [action()] | action()) -> @@ -605,7 +608,8 @@ enter(Module, Opts, State, Data, Server, Actions, Parent) -> %% The values should already have been type checked Name = gen:get_proc_name(Server), Debug = gen:debug_options(Name, Opts), - Events = [], + HibernateAfterTimeout = gen:hibernate_after(Opts), + Events = [], P = [], Event = {internal,init_state}, %% We enforce {postpone,false} to ensure that @@ -648,6 +652,7 @@ enter(Module, Opts, State, Data, Server, Actions, Parent) -> timer_refs => TimerRefs, timer_types => TimerTypes, hibernate => Hibernate, + hibernate_after => HibernateAfterTimeout, cancel_timers => CancelTimers }, NewDebug = sys_debug(Debug, S, State, {enter,Event,State}), @@ -854,7 +859,7 @@ loop_hibernate(Parent, Debug, S) -> {wakeup_from_hibernate,3}}). %% Entry point for wakeup_from_hibernate/3 -loop_receive(Parent, Debug, S) -> +loop_receive(Parent, Debug, #{hibernate_after := HibernateAfterTimeout} = S) -> receive Msg -> case Msg of @@ -956,6 +961,9 @@ loop_receive(Parent, Debug, S) -> loop_receive_result( Parent, Debug, S, Hibernate, Event) end + after + HibernateAfterTimeout -> + loop_hibernate(Parent, Debug, S) end. loop_receive_result( diff --git a/lib/stdlib/src/proc_lib.erl b/lib/stdlib/src/proc_lib.erl index 363705b0f4..2219467a8d 100644 --- a/lib/stdlib/src/proc_lib.erl +++ b/lib/stdlib/src/proc_lib.erl @@ -264,12 +264,12 @@ exit_p(Class, Reason, Stacktrace) -> case get('$initial_call') of {M,F,A} when is_atom(M), is_atom(F), is_integer(A) -> MFA = {M,F,make_dummy_args(A, [])}, - crash_report(Class, Reason, MFA), + crash_report(Class, Reason, MFA, Stacktrace), erlang:raise(exit, exit_reason(Class, Reason, Stacktrace), Stacktrace); _ -> %% The process dictionary has been cleared or %% possibly modified. - crash_report(Class, Reason, []), + crash_report(Class, Reason, [], Stacktrace), erlang:raise(exit, exit_reason(Class, Reason, Stacktrace), Stacktrace) end. @@ -499,24 +499,25 @@ trans_init(M, F, A) when is_atom(M), is_atom(F) -> %% Generate a crash report. %% ----------------------------------------------------- -crash_report(exit, normal, _) -> ok; -crash_report(exit, shutdown, _) -> ok; -crash_report(exit, {shutdown,_}, _) -> ok; -crash_report(Class, Reason, StartF) -> - OwnReport = my_info(Class, Reason, StartF), +crash_report(exit, normal, _, _) -> ok; +crash_report(exit, shutdown, _, _) -> ok; +crash_report(exit, {shutdown,_}, _, _) -> ok; +crash_report(Class, Reason, StartF, Stacktrace) -> + OwnReport = my_info(Class, Reason, StartF, Stacktrace), LinkReport = linked_info(self()), Rep = [OwnReport,LinkReport], error_logger:error_report(crash_report, Rep). -my_info(Class, Reason, []) -> - my_info_1(Class, Reason); -my_info(Class, Reason, StartF) -> - [{initial_call, StartF}|my_info_1(Class, Reason)]. +my_info(Class, Reason, [], Stacktrace) -> + my_info_1(Class, Reason, Stacktrace); +my_info(Class, Reason, StartF, Stacktrace) -> + [{initial_call, StartF}| + my_info_1(Class, Reason, Stacktrace)]. -my_info_1(Class, Reason) -> +my_info_1(Class, Reason, Stacktrace) -> [{pid, self()}, get_process_info(self(), registered_name), - {error_info, {Class,Reason,erlang:get_stacktrace()}}, + {error_info, {Class,Reason,Stacktrace}}, get_ancestors(self()), get_process_info(self(), messages), get_process_info(self(), links), diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl index 20aaa2638c..535ca57a6b 100644 --- a/lib/stdlib/src/qlc.erl +++ b/lib/stdlib/src/qlc.erl @@ -1392,8 +1392,10 @@ next_loop(Pid, L, N) when N =/= 0 -> {caught, throw, Error, [?THROWN_ERROR | _]} -> Error; {caught, Class, Reason, Stacktrace} -> - _ = (catch erlang:error(foo)), - erlang:raise(Class, Reason, Stacktrace ++ erlang:get_stacktrace()); + CurrentStacktrace = try erlang:error(foo) + catch error:_ -> erlang:get_stacktrace() + end, + erlang:raise(Class, Reason, Stacktrace ++ CurrentStacktrace); error -> erlang:error({qlc_cursor_pid_no_longer_exists, Pid}) end; diff --git a/lib/stdlib/test/dummy_h.erl b/lib/stdlib/test/dummy_h.erl index 45ccfee240..7a9eb11f98 100644 --- a/lib/stdlib/test/dummy_h.erl +++ b/lib/stdlib/test/dummy_h.erl @@ -43,6 +43,9 @@ handle_event(hibernate, _State) -> {ok,[],hibernate}; handle_event(wakeup, _State) -> {ok,[]}; +handle_event({From, handle_event}, _State) -> + From ! handled_event, + {ok,[]}; handle_event(Event, Parent) -> Parent ! {dummy_h, Event}, {ok, Parent}. @@ -75,6 +78,9 @@ handle_info(wake, _State) -> {ok, []}; handle_info(gnurf, _State) -> {ok, []}; +handle_info({From, handle_info}, _State) -> + From ! handled_info, + {ok, []}; handle_info(Info, Parent) -> Parent ! {dummy_h, Info}, {ok, Parent}. diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 03cad2c093..02524679fa 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -65,7 +65,8 @@ maps/1,maps_type/1,maps_parallel_match/1, otp_11851/1,otp_11879/1,otp_13230/1, record_errors/1, otp_11879_cont/1, - non_latin1_module/1, otp_14323/1]). + non_latin1_module/1, otp_14323/1, + get_stacktrace/1]). suite() -> [{ct_hooks,[ts_install_cth]}, @@ -85,7 +86,8 @@ all() -> too_many_arguments, basic_errors, bin_syntax_errors, predef, maps, maps_type, maps_parallel_match, otp_11851, otp_11879, otp_13230, - record_errors, otp_11879_cont, non_latin1_module, otp_14323]. + record_errors, otp_11879_cont, non_latin1_module, otp_14323, + get_stacktrace]. groups() -> [{unused_vars_warn, [], @@ -3980,6 +3982,63 @@ otp_14323(Config) -> [] = run(Config, Ts), ok. +get_stacktrace(Config) -> + Ts = [{old_catch, + <<"t1() -> + catch error(foo), + erlang:get_stacktrace(). + ">>, + [], + {warnings,[{3,erl_lint,{get_stacktrace,after_old_catch}}]}}, + {nowarn_get_stacktrace, + <<"t1() -> + catch error(foo), + erlang:get_stacktrace(). + ">>, + [nowarn_get_stacktrace], + []}, + {try_catch, + <<"t1(X) -> + try abs(X) of + _ -> + erlang:get_stacktrace() + catch + _:_ -> ok + end. + + t2() -> + try error(foo) + catch _:_ -> ok + end, + erlang:get_stacktrace(). + + t3() -> + try error(foo) + catch _:_ -> + try error(bar) + catch _:_ -> + ok + end, + erlang:get_stacktrace() + end. + + no_warning(X) -> + try + abs(X) + catch + _:_ -> + erlang:get_stacktrace() + end. + ">>, + [], + {warnings,[{4,erl_lint,{get_stacktrace,wrong_part_of_try}}, + {13,erl_lint,{get_stacktrace,after_try}}, + {22,erl_lint,{get_stacktrace,after_try}}]}}], + + run(Config, Ts), + ok. + + run(Config, Tests) -> F = fun({N,P,Ws,E}, BadL) -> case catch run_test(Config, P, Ws) of diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl index 749920f843..880b10117c 100644 --- a/lib/stdlib/test/gen_event_SUITE.erl +++ b/lib/stdlib/test/gen_event_SUITE.erl @@ -26,7 +26,7 @@ end_per_testcase/2]). -export([start/1, add_handler/1, add_sup_handler/1, delete_handler/1, swap_handler/1, swap_sup_handler/1, - notify/1, sync_notify/1, call/1, info/1, hibernate/1, + notify/1, sync_notify/1, call/1, info/1, hibernate/1, auto_hibernate/1, call_format_status/1, call_format_status_anon/1, error_format_status/1, get_state/1, replace_state/1, start_opt/1, @@ -37,7 +37,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [start, {group, test_all}, hibernate, + [start, {group, test_all}, hibernate, auto_hibernate, call_format_status, call_format_status_anon, error_format_status, get_state, replace_state, start_opt, {group, undef_callbacks}, undef_in_terminate]. @@ -306,6 +306,48 @@ hibernate(Config) when is_list(Config) -> ok. +auto_hibernate(Config) when is_list(Config) -> + HibernateAfterTimeout = 100, + State = {auto_hibernate_state}, + {ok,Pid} = gen_event:start({local, auto_hibernate_handler}, [{hibernate_after, HibernateAfterTimeout}]), + %% After init test + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + ok = gen_event:add_handler(auto_hibernate_handler, dummy_h, [State]), + %% Get state test + [{dummy_h,false,State}] = sys:get_state(Pid), + is_in_erlang_hibernate(Pid), + %% Call test + {ok, hejhopp} = gen_event:call(auto_hibernate_handler, dummy_h, hejsan), + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + %% Event test + ok = gen_event:notify(auto_hibernate_handler, {self(), handle_event}), + receive + handled_event -> + ok + after 1000 -> + ct:fail(event) + end, + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + %% Info test + Pid ! {self(), handle_info}, + receive + handled_info -> + ok + after 1000 -> + ct:fail(info) + end, + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + ok = gen_event:stop(auto_hibernate_handler), + ok. + is_in_erlang_hibernate(Pid) -> receive after 1 -> ok end, is_in_erlang_hibernate_1(200, Pid). diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl index 7f98526d35..86cf58566b 100644 --- a/lib/stdlib/test/gen_fsm_SUITE.erl +++ b/lib/stdlib/test/gen_fsm_SUITE.erl @@ -44,7 +44,7 @@ -export([undef_in_handle_info/1, undef_in_terminate/1]). --export([hibernate/1,hiber_idle/3,hiber_wakeup/3,hiber_idle/2,hiber_wakeup/2]). +-export([hibernate/1,auto_hibernate/1,hiber_idle/3,hiber_wakeup/3,hiber_idle/2,hiber_wakeup/2]). -export([enter_loop/1]). @@ -68,7 +68,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [{group, start}, {group, abnormal}, shutdown, - {group, sys}, hibernate, enter_loop, {group, undef_callbacks}, + {group, sys}, hibernate, auto_hibernate, enter_loop, {group, undef_callbacks}, undef_in_handle_info, undef_in_terminate]. groups() -> @@ -700,6 +700,43 @@ hibernate(Config) when is_list(Config) -> process_flag(trap_exit, OldFl), ok. +%% Auto hibernation +auto_hibernate(Config) when is_list(Config) -> + OldFl = process_flag(trap_exit, true), + HibernateAfterTimeout = 100, + State = {auto_hibernate_state}, + {ok, Pid} = gen_fsm:start_link({local, my_test_name_auto_hibernate}, ?MODULE, {state_data, State}, [{hibernate_after, HibernateAfterTimeout}]), + %% After init test + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + %% Get state test + {_, State} = sys:get_state(my_test_name_auto_hibernate), + is_in_erlang_hibernate(Pid), + %% Sync send event test + 'alive!' = gen_fsm:sync_send_event(Pid,'alive?'), + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + %% Send event test + ok = gen_fsm:send_all_state_event(Pid,{'alive?', self()}), + wfor(yes), + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + %% Info test + Pid ! {self(), handle_info}, + wfor({Pid, handled_info}), + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + stop_it(Pid), + receive + {'EXIT',Pid,normal} -> ok + end, + process_flag(trap_exit, OldFl), + ok. + is_in_erlang_hibernate(Pid) -> receive after 1 -> ok end, is_in_erlang_hibernate_1(200, Pid). @@ -1151,6 +1188,8 @@ idle(badreturn, _From, _Data) -> idle({timeout,Time}, From, _Data) -> gen_fsm:send_event_after(Time, {timeout,Time}), {next_state, timeout, From}; +idle('alive?', _From, Data) -> + {reply, 'alive!', idle, Data}; idle(_, _From, Data) -> {reply, 'eh?', idle, Data}. @@ -1226,6 +1265,9 @@ handle_info(hibernate_later, _SName, _State) -> handle_info({call_undef_fun, {Mod, Fun}}, State, Data) -> Mod:Fun(), {next_state, State, Data}; +handle_info({From, handle_info}, SName, State) -> + From ! {self(), handled_info}, + {next_state, SName, State}; handle_info(Info, _State, Data) -> {stop, {unexpected,Info}, Data}. diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl index 6f72034b09..7e3c71715e 100644 --- a/lib/stdlib/test/gen_server_SUITE.erl +++ b/lib/stdlib/test/gen_server_SUITE.erl @@ -32,7 +32,7 @@ call_remote_n1/1, call_remote_n2/1, call_remote_n3/1, spec_init/1, spec_init_local_registered_parent/1, spec_init_global_registered_parent/1, - otp_5854/1, hibernate/1, otp_7669/1, call_format_status/1, + otp_5854/1, hibernate/1, auto_hibernate/1, otp_7669/1, call_format_status/1, error_format_status/1, terminate_crash_format/1, get_state/1, replace_state/1, call_with_huge_message_queue/1, undef_handle_call/1, undef_handle_cast/1, undef_handle_info/1, @@ -65,7 +65,7 @@ all() -> call_remote3, call_remote_n1, call_remote_n2, call_remote_n3, spec_init, spec_init_local_registered_parent, - spec_init_global_registered_parent, otp_5854, hibernate, + spec_init_global_registered_parent, otp_5854, hibernate, auto_hibernate, otp_7669, call_format_status, error_format_status, terminate_crash_format, get_state, replace_state, @@ -730,6 +730,58 @@ hibernate(Config) when is_list(Config) -> process_flag(trap_exit, OldFl), ok. +auto_hibernate(Config) when is_list(Config) -> + OldFl = process_flag(trap_exit, true), + HibernateAfterTimeout = 100, + State = {auto_hibernate_state}, + {ok, Pid} = + gen_server:start_link({local, my_test_name_auto_hibernate}, + gen_server_SUITE, {state,State}, [{hibernate_after, HibernateAfterTimeout}]), + %% After init test + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + %% Get state test + State = sys:get_state(my_test_name_auto_hibernate), + is_in_erlang_hibernate(Pid), + %% Call test + ok = gen_server:call(my_test_name_auto_hibernate, started_p), + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + %% Cast test + ok = gen_server:cast(my_test_name_auto_hibernate, {self(),handle_cast}), + receive + {Pid, handled_cast} -> + ok + after 1000 -> + ct:fail(cast) + end, + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + %% Info test + Pid ! {self(),handle_info}, + receive + {Pid, handled_info} -> + ok + after 1000 -> + ct:fail(info) + end, + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + + ok = gen_server:call(my_test_name_auto_hibernate, stop), + receive + {'EXIT', Pid, stopped} -> + ok + after 5000 -> + ct:fail(gen_server_did_not_die) + end, + process_flag(trap_exit, OldFl), + ok. + is_in_erlang_hibernate(Pid) -> receive after 1 -> ok end, is_in_erlang_hibernate_1(200, Pid). @@ -747,6 +799,23 @@ is_in_erlang_hibernate_1(N, Pid) -> is_in_erlang_hibernate_1(N-1, Pid) end. +is_not_in_erlang_hibernate(Pid) -> + receive after 1 -> ok end, + is_not_in_erlang_hibernate_1(200, Pid). + +is_not_in_erlang_hibernate_1(0, Pid) -> + io:format("~p\n", [erlang:process_info(Pid, current_function)]), + ct:fail(not_in_erlang_hibernate_3); +is_not_in_erlang_hibernate_1(N, Pid) -> + {current_function,MFA} = erlang:process_info(Pid, current_function), + case MFA of + {erlang,hibernate,3} -> + receive after 10 -> ok end, + is_not_in_erlang_hibernate_1(N-1, Pid); + _ -> + ok + end. + %% -------------------------------------- %% Test gen_server:abcast and handle_cast. %% Test all different return values from diff --git a/lib/stdlib/test/gen_statem_SUITE.erl b/lib/stdlib/test/gen_statem_SUITE.erl index 05934b3953..5b9daecfd3 100644 --- a/lib/stdlib/test/gen_statem_SUITE.erl +++ b/lib/stdlib/test/gen_statem_SUITE.erl @@ -40,7 +40,7 @@ all() -> shutdown, stop_and_reply, state_enter, event_order, state_timeout, event_types, generic_timers, code_change, {group, sys}, - hibernate, enter_loop, {group, undef_callbacks}, + hibernate, auto_hibernate, enter_loop, {group, undef_callbacks}, undef_in_terminate]. groups() -> @@ -1284,6 +1284,84 @@ hibernate(Config) -> end, ok = verify_empty_msgq(). +%% Auto-hibernation timeout +auto_hibernate(Config) -> + OldFl = process_flag(trap_exit, true), + HibernateAfterTimeout = 100, + + {ok,Pid} = + gen_statem:start_link( + ?MODULE, start_arg(Config, []), [{hibernate_after, HibernateAfterTimeout}]), + %% After init test + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + %% After info test + Pid ! {hping, self()}, + receive + {Pid, hpong} -> + ok + after 1000 -> + ct:fail(info) + end, + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + %% After cast test + ok = gen_statem:cast(Pid, {hping, self()}), + receive + {Pid, hpong} -> + ok + after 1000 -> + ct:fail(cast) + end, + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + %% After call test + hpong = gen_statem:call(Pid, hping), + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + %% Timer test 1 + TimerTimeout1 = 50, + ok = gen_statem:call(Pid, {arm_htimer, self(), TimerTimeout1}), + is_not_in_erlang_hibernate(Pid), + timer:sleep(TimerTimeout1), + is_not_in_erlang_hibernate(Pid), + receive + {Pid, htimer_armed} -> + ok + after 1000 -> + ct:fail(timer1) + end, + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + %% Timer test 2 + TimerTimeout2 = 150, + ok = gen_statem:call(Pid, {arm_htimer, self(), TimerTimeout2}), + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + receive + {Pid, htimer_armed} -> + ok + after 1000 -> + ct:fail(timer2) + end, + is_not_in_erlang_hibernate(Pid), + timer:sleep(HibernateAfterTimeout), + is_in_erlang_hibernate(Pid), + stop_it(Pid), + process_flag(trap_exit, OldFl), + receive + {'EXIT',Pid,normal} -> ok + after 5000 -> + ct:fail(gen_statem_did_not_die) + end, + ok = verify_empty_msgq(). + is_in_erlang_hibernate(Pid) -> receive after 1 -> ok end, is_in_erlang_hibernate_1(200, Pid). @@ -1704,6 +1782,19 @@ terminate(_Reason, _State, _Data) -> %% State functions +idle(info, {hping,Pid}, _Data) -> + Pid ! {self(), hpong}, + keep_state_and_data; +idle(cast, {hping,Pid}, Data) -> + Pid ! {self(), hpong}, + {keep_state, Data}; +idle({call, From}, hping, _Data) -> + {keep_state_and_data, [{reply, From, hpong}]}; +idle({call, From}, {arm_htimer, Pid, Timeout}, _Data) -> + {keep_state_and_data, [{reply, From, ok}, {timeout, Timeout, {arm_htimer, Pid}}]}; +idle(timeout, {arm_htimer, Pid}, _Data) -> + Pid ! {self(), htimer_armed}, + keep_state_and_data; idle(cast, {connect,Pid}, Data) -> Pid ! accept, {next_state,wfor_conf,Data,infinity}; % NoOp timeout just to test API |