diff options
author | Raimo Niskanen <[email protected]> | 2016-04-26 16:49:47 +0200 |
---|---|---|
committer | Raimo Niskanen <[email protected]> | 2016-04-27 09:01:00 +0200 |
commit | 471a50ef1391f6399664f8b992da7a67c32c8b86 (patch) | |
tree | d68e1a3ab621ee15b39422dd1452a3e5270c2edc /lib | |
parent | 7d000278728f5f8940d02fe9798735820baa79e5 (diff) | |
download | otp-471a50ef1391f6399664f8b992da7a67c32c8b86.tar.gz otp-471a50ef1391f6399664f8b992da7a67c32c8b86.tar.bz2 otp-471a50ef1391f6399664f8b992da7a67c32c8b86.zip |
Fix hibernation subtlety
Diffstat (limited to 'lib')
-rw-r--r-- | lib/stdlib/doc/src/gen_statem.xml | 35 | ||||
-rw-r--r-- | lib/stdlib/src/gen_statem.erl | 30 |
2 files changed, 42 insertions, 23 deletions
diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml index 91332fbdde..ec7f267c64 100644 --- a/lib/stdlib/doc/src/gen_statem.xml +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -216,19 +216,23 @@ erlang:'!' -----> Module:StateName/3 if bad arguments are given. </p> <p> - The <c>gen_statem</c> process can go into hibernation (see - <seealso marker="erts:erlang#hibernate/3"> - <c>erlang:hibernate/3</c> - </seealso>) if a + The <c>gen_statem</c> process can go into hibernation; see + <seealso marker="proc_lib#hibernate/3"> + <c>proc_lib:hibernate/3</c>. + </seealso> + It is done when a <seealso marker="#state_function">state function</seealso> or <seealso marker="#Module:init/1"><c>Module:init/1</c></seealso> specifies <c>hibernate</c> in the returned <seealso marker="#type-action"><c>Actions</c></seealso> - list. This might be useful if the server is expected to be idle - for a long time. However use this feature with care - since hibernation implies at least two garbage collections - (when hibernating and shortly after waking up) and that is not - something you would want to do between each event on a busy server. + list. This feature might be useful to reclaim process heap memory + while the server is expected to be idle for a long time. + However, use this feature with care + since hibernation can be too costly + to use after every event; see + <seealso marker="erts:erlang#hibernate/3"> + <c>erlang:hibernate/3</c>. + </seealso> </p> </description> @@ -619,7 +623,8 @@ handle_event(_, _, State, Data) -> to wait for the next message. In hibernation the next non-system event awakens the <c>gen_statem</c>, or rather the next incoming message awakens the <c>gen_statem</c> - but if it is a system event it goes back into hibernation. + but if it is a system event + it goes right back into hibernation. </item> </list> </desc> @@ -645,9 +650,13 @@ handle_event(_, _, State, Data) -> </seealso> before going into <c>receive</c> to wait for a new external event. - If there are enqueued events the <c>hibernate</c> - is ignored as if an event just arrived and awakened - the <c>gen_statem</c>. + If there are enqueued events, + to prevent receiving any new event; a + <seealso marker="erts:erlang#garbage_collect/0"> + <c>garbage_collect/0</c> + </seealso> is done instead to simulate + that the <c>gen_statem</c> entered hibernation + and immediately got awakened by the oldest enqueued event. </p> </desc> </datatype> diff --git a/lib/stdlib/src/gen_statem.erl b/lib/stdlib/src/gen_statem.erl index c85c521d8e..f9e2e5f7d2 100644 --- a/lib/stdlib/src/gen_statem.erl +++ b/lib/stdlib/src/gen_statem.erl @@ -723,8 +723,8 @@ wakeup_from_hibernate(Parent, Debug, S) -> %% and some detours through sys and proc_lib %% Entry point for system_continue/3 -loop(Parent, Debug, #{hibernate := Hib} = S) -> - case Hib of +loop(Parent, Debug, #{hibernate := Hibernate} = S) -> + case Hibernate of true -> %% Does not return but restarts process at %% wakeup_from_hibernate/3 that jumps to loop_receive/3 @@ -754,8 +754,7 @@ loop_receive(Parent, Debug, #{timer := Timer} = S) -> %% but this will stand out in the crash report... ?TERMINATE(exit, Reason, Debug, S, [EXIT]); {timeout,Timer,Content} when Timer =/= undefined -> - loop_event( - Parent, Debug, S, {timeout,Content}); + loop_event(Parent, Debug, S, {timeout,Content}); _ -> %% Cancel Timer if running case Timer of @@ -788,15 +787,15 @@ loop_receive(Parent, Debug, #{timer := Timer} = S) -> end. loop_event(Parent, Debug, S, Event) -> - %% The timer field in S is now invalid and ignored - %% until we get back to loop/3 + %% The timer field and the hibernate flag in S + %% are now invalid and ignored until we get back to loop/3 NewDebug = sys_debug(Debug, S, {in,Event}), %% Here the queue of not yet processed events is created - loop_events(Parent, NewDebug, S, [Event]). + loop_events(Parent, NewDebug, S, [Event], false). %% Process first the event queue, or if it is empty %% loop back to receive a new event -loop_events(Parent, Debug, S, []) -> +loop_events(Parent, Debug, S, [], _Hibernate) -> loop(Parent, Debug, S); loop_events( Parent, Debug, @@ -804,7 +803,16 @@ loop_events( module := Module, state := State, data := Data} = S, - [{Type,Content} = Event|Events] = Q) -> + [{Type,Content} = Event|Events] = Q, + Hibernate) -> + %% If the Hibernate flag is true here it can only be + %% because it was set from an event action + %% and we did not go into hibernation since there + %% were events in queue, so we do what the user + %% might depend on i.e collect garbage which + %% would have happened if we actually hibernated + %% and immediately was awakened + Hibernate andalso garbage_collect(), try case CallbackMode of state_functions -> @@ -869,6 +877,8 @@ loop_event_result( Parent, Debug, #{state := State, data := Data} = S, Events, Event, Result) -> + %% From now until we loop back to the loop_events/4 + %% the state and data fields in S are old case Result of stop -> ?TERMINATE(exit, normal, Debug, S, [Event|Events]); @@ -1094,7 +1104,7 @@ loop_event_actions( timer := Timer, postponed := P, hibernate := Hibernate}, - Q). + Q, Hibernate). %%--------------------------------------------------------------------------- %% Server helpers |