1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2016-2019. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20-module(gen_statem).
21
22-include("logger.hrl").
23
24%% API
25-export(
26   [start/3,start/4,start_link/3,start_link/4,
27    stop/1,stop/3,
28    cast/2,call/2,call/3,
29    enter_loop/4,enter_loop/5,enter_loop/6,
30    reply/1,reply/2]).
31
32%% gen callbacks
33-export(
34   [init_it/6]).
35
36%% sys callbacks
37-export(
38   [system_continue/3,
39    system_terminate/4,
40    system_code_change/4,
41    system_get_state/1,
42    system_replace_state/2,
43    format_status/2]).
44
45%% Internal callbacks
46-export(
47   [wakeup_from_hibernate/3]).
48
49%% logger callback
50-export([format_log/1]).
51
52%% Type exports for templates and callback modules
53-export_type(
54   [event_type/0,
55    callback_mode_result/0,
56    init_result/1,
57    state_enter_result/1,
58    event_handler_result/1,
59    reply_action/0,
60    enter_action/0,
61    action/0]).
62%% Old types, not advertised
63-export_type(
64   [state_function_result/0,
65    handle_event_result/0]).
66
67%% Type that is exported just to be documented
68-export_type([transition_option/0]).
69
70%%%==========================================================================
71%%% Interface functions.
72%%%==========================================================================
73
74-type from() ::
75	{To :: pid(), Tag :: term()}. % Reply-to specifier for call
76
77-type state() ::
78	state_name() | % For StateName/3 callback functions
79	term(). % For handle_event/4 callback function
80
81-type state_name() :: atom().
82
83-type data() :: term().
84
85-type event_type() ::
86        external_event_type() | timeout_event_type() | 'internal'.
87-type external_event_type() ::
88        {'call',From :: from()} | 'cast' | 'info'.
89-type timeout_event_type() ::
90        'timeout' | {'timeout', Name :: term()} | 'state_timeout'.
91
92-type callback_mode_result() ::
93	callback_mode() | [callback_mode() | state_enter()].
94-type callback_mode() :: 'state_functions' | 'handle_event_function'.
95-type state_enter() :: 'state_enter'.
96
97-type transition_option() ::
98	postpone() | hibernate() |
99	event_timeout() | generic_timeout() | state_timeout().
100-type postpone() ::
101	%% If 'true' postpone the current event
102	%% and retry it when the state changes (=/=)
103	boolean().
104-type hibernate() ::
105	%% If 'true' hibernate the server instead of going into receive
106	boolean().
107-type event_timeout() ::
108	%% Generate a ('timeout', EventContent, ...) event
109	%% unless some other event is delivered
110	Time :: timeout() | integer().
111-type generic_timeout() ::
112	%% Generate a ({'timeout',Name}, EventContent, ...) event
113	Time :: timeout() | integer().
114-type state_timeout() ::
115	%% Generate a ('state_timeout', EventContent, ...) event
116	%% unless the state is changed
117	Time :: timeout() | integer().
118-type timeout_option() :: {abs,Abs :: boolean()}.
119
120-type action() ::
121	%% During a state change:
122	%% * NextState and NewData are set.
123	%% * All action()s are executed in order of apperance.
124	%% * Postponing the current event is performed
125	%%   iff 'postpone' is 'true'.
126	%% * A state timeout is started iff 'timeout' is set.
127	%% * Pending events are handled or if there are
128	%%   no pending events the server goes into receive
129	%%   or hibernate (iff 'hibernate' is 'true')
130	%%
131	%% These action()s are executed in order of appearence
132	%% in the containing list. The ones that set options
133	%% will override any previous so the last of each kind wins.
134	%%
135	'postpone' |  % Set the postpone option
136	{'postpone', Postpone :: postpone()} |
137	%%
138	%% All 'next_event' events are kept in a list and then
139	%% inserted at state changes so the first in the
140	%% action() list is the first to be delivered.
141	{'next_event', % Insert event as the next to handle
142	 EventType :: event_type(),
143	 EventContent :: term()} |
144	enter_action().
145-type enter_action() ::
146	'hibernate' | % Set the hibernate option
147	{'hibernate', Hibernate :: hibernate()} |
148        timeout_action() |
149	reply_action().
150-type timeout_action() ::
151	(Time :: event_timeout()) | % {timeout,Time,Time}
152	{'timeout', % Set the event_timeout option
153	 Time :: event_timeout(), EventContent :: term()} |
154	{'timeout', % Set the event_timeout option
155	 Time :: event_timeout(),
156	 EventContent :: term(),
157	 Options :: (timeout_option() | [timeout_option()])} |
158	%%
159	{{'timeout', Name :: term()}, % Set the generic_timeout option
160	 Time :: generic_timeout(), EventContent :: term()} |
161	{{'timeout', Name :: term()}, % Set the generic_timeout option
162	 Time :: generic_timeout(),
163	 EventContent :: term(),
164	 Options :: (timeout_option() | [timeout_option()])} |
165	%%
166	{'state_timeout', % Set the state_timeout option
167	 Time :: state_timeout(), EventContent :: term()} |
168	{'state_timeout', % Set the state_timeout option
169	 Time :: state_timeout(),
170	 EventContent :: term(),
171	 Options :: (timeout_option() | [timeout_option()])}.
172-type reply_action() ::
173	{'reply', % Reply to a caller
174	 From :: from(), Reply :: term()}.
175
176-type init_result(StateType) ::
177    {ok, State :: StateType, Data :: data()} |
178    {ok, State :: StateType, Data :: data(),
179     Actions :: [action()] | action()} |
180    'ignore' |
181    {'stop', Reason :: term()}.
182
183%% Old, not advertised
184-type state_function_result() ::
185	event_handler_result(state_name()).
186-type handle_event_result() ::
187	event_handler_result(state()).
188%%
189-type state_enter_result(State) ::
190	{'next_state', % {next_state,NextState,NewData,[]}
191	 State,
192	 NewData :: data()} |
193	{'next_state', % State transition, maybe to the same state
194	 State,
195	 NewData :: data(),
196	 Actions :: [enter_action()] | enter_action()} |
197	state_callback_result(enter_action()).
198-type event_handler_result(StateType) ::
199	{'next_state', % {next_state,NextState,NewData,[]}
200	 NextState :: StateType,
201	 NewData :: data()} |
202	{'next_state', % State transition, maybe to the same state
203	 NextState :: StateType,
204	 NewData :: data(),
205	 Actions :: [action()] | action()} |
206	state_callback_result(action()).
207-type state_callback_result(ActionType) ::
208	{'keep_state', % {keep_state,NewData,[]}
209	 NewData :: data()} |
210	{'keep_state', % Keep state, change data
211	 NewData :: data(),
212	 Actions :: [ActionType] | ActionType} |
213	'keep_state_and_data' | % {keep_state_and_data,[]}
214	{'keep_state_and_data', % Keep state and data -> only actions
215	 Actions :: [ActionType] | ActionType} |
216	%%
217	{'repeat_state', % {repeat_state,NewData,[]}
218	 NewData :: data()} |
219	{'repeat_state', % Repeat state, change data
220	 NewData :: data(),
221	 Actions :: [ActionType] | ActionType} |
222	'repeat_state_and_data' | % {repeat_state_and_data,[]}
223	{'repeat_state_and_data', % Repeat state and data -> only actions
224	 Actions :: [ActionType] | ActionType} |
225	%%
226	'stop' | % {stop,normal}
227	{'stop', % Stop the server
228	 Reason :: term()} |
229	{'stop', % Stop the server
230	 Reason :: term(),
231	 NewData :: data()} |
232	%%
233	{'stop_and_reply', % Reply then stop the server
234	 Reason :: term(),
235	 Replies :: [reply_action()] | reply_action()} |
236	{'stop_and_reply', % Reply then stop the server
237	 Reason :: term(),
238	 Replies :: [reply_action()] | reply_action(),
239	 NewData :: data()}.
240
241
242%% The state machine init function.  It is called only once and
243%% the server is not running until this function has returned
244%% an {ok, ...} tuple.  Thereafter the state callbacks are called
245%% for all events to this server.
246-callback init(Args :: term()) -> init_result(state()).
247
248%% This callback shall return the callback mode of the callback module.
249%%
250%% It is called once after init/0 and code_change/4 but before
251%% the first state callback StateName/3 or handle_event/4.
252-callback callback_mode() -> callback_mode_result().
253
254%% Example state callback for StateName = 'state_name'
255%% when callback_mode() =:= state_functions.
256%%
257%% In this mode all states has to be of type state_name() i.e atom().
258%%
259%% Note that the only callbacks that have arity 3 are these
260%% StateName/3 callbacks and terminate/3, so the state name
261%% 'terminate' is unusable in this mode.
262-callback state_name(
263	    'enter',
264	    OldStateName :: state_name(),
265	    Data :: data()) ->
266    state_enter_result('state_name');
267           (event_type(),
268	    EventContent :: term(),
269	    Data :: data()) ->
270    event_handler_result(state_name()).
271%%
272%% State callback for all states
273%% when callback_mode() =:= handle_event_function.
274-callback handle_event(
275	    'enter',
276	    OldState :: state(),
277	    State, % Current state
278	    Data :: data()) ->
279    state_enter_result(State);
280           (event_type(),
281	    EventContent :: term(),
282	    State :: state(), % Current state
283	    Data :: data()) ->
284    event_handler_result(state()).
285
286%% Clean up before the server terminates.
287-callback terminate(
288	    Reason :: 'normal' | 'shutdown' | {'shutdown', term()}
289		    | term(),
290	    State :: state(),
291	    Data :: data()) ->
292    any().
293
294%% Note that the new code can expect to get an OldState from
295%% the old code version not only in code_change/4 but in the first
296%% state callback function called thereafter
297-callback code_change(
298	    OldVsn :: term() | {'down', term()},
299	    OldState :: state(),
300	    OldData :: data(),
301	    Extra :: term()) ->
302    {ok, NewState :: state(), NewData :: data()} |
303    (Reason :: term()).
304
305%% Format the callback module state in some sensible that is
306%% often condensed way.  For StatusOption =:= 'normal' the preferred
307%% return term is [{data,[{"State",FormattedState}]}], and for
308%% StatusOption =:= 'terminate' it is just FormattedState.
309-callback format_status(
310	    StatusOption,
311	    [ [{Key :: term(), Value :: term()}] |
312	      state() |
313	      data()]) ->
314    Status :: term() when
315      StatusOption :: 'normal' | 'terminate'.
316
317-optional_callbacks(
318   [format_status/2, % Has got a default implementation
319    terminate/3, % Has got a default implementation
320    code_change/4, % Only needed by advanced soft upgrade
321    %%
322    state_name/3, % Example for callback_mode() =:= state_functions:
323    %% there has to be a StateName/3 callback function
324    %% for every StateName in your state machine but the state name
325    %% 'state_name' does of course not have to be used.
326    %%
327    handle_event/4 % For callback_mode() =:= handle_event_function
328   ]).
329
330
331
332%% Type validation functions
333-compile(
334   {inline,
335    [callback_mode/1, state_enter/1,
336     event_type/1, from/1, timeout_event_type/1]}).
337%%
338callback_mode(CallbackMode) ->
339    case CallbackMode of
340	state_functions -> true;
341	handle_event_function -> true;
342	_ -> false
343    end.
344%%
345state_enter(StateEnter) ->
346    case StateEnter of
347        state_enter ->
348            true;
349        _ ->
350            false
351    end.
352%%
353event_type(Type) ->
354    case Type of
355	{call,From} -> from(From);
356        %%
357	cast -> true;
358	info -> true;
359	internal -> true;
360        _ -> timeout_event_type(Type)
361    end.
362%%
363from({Pid,_}) when is_pid(Pid) -> true;
364from(_) -> false.
365%%
366timeout_event_type(Type) ->
367    case Type of
368        timeout -> true;
369        state_timeout -> true;
370        {timeout,_Name} -> true;
371        _ -> false
372    end.
373
374
375-define(
376   STACKTRACE(),
377   element(2, erlang:process_info(self(), current_stacktrace))).
378
379-define(not_sys_debug, []).
380%%
381%% This is a macro to only evaluate arguments if Debug =/= [].
382%% Debug is evaluated multiple times.
383-define(
384   sys_debug(Debug, NameState, Entry),
385   case begin Debug end of
386       ?not_sys_debug ->
387           begin Debug end;
388       _ ->
389           sys_debug(begin Debug end, begin NameState end, begin Entry end)
390    end).
391
392-record(state,
393        {callback_mode = undefined :: callback_mode() | undefined,
394         state_enter = false :: boolean(),
395         module :: atom(),
396         name :: atom(),
397         state :: term(),
398         data :: term(),
399         postponed = [] :: [{event_type(),term()}],
400         %%
401         timers = {#{},#{}} ::
402           {%% timer ref => the timer's event type
403            TimerRefs :: #{reference() => timeout_event_type()},
404            %% timer's event type => timer ref
405            TimerTypes :: #{timeout_event_type() => reference()}},
406         hibernate = false :: boolean(),
407         hibernate_after = infinity :: timeout()}).
408
409-record(trans_opts,
410        {hibernate = false,
411         postpone = false,
412         timeouts_r = [],
413         next_events_r = []}).
414
415%%%==========================================================================
416%%% API
417
418-type server_name() ::
419      {'global', GlobalName :: term()}
420      | {'via', RegMod :: module(), Name :: term()}
421      | {'local', atom()}.
422-type server_ref() ::
423      pid()
424      | (LocalName :: atom())
425      | {Name :: atom(), Node :: atom()}
426      | {'global', GlobalName :: term()}
427      | {'via', RegMod :: module(), ViaName :: term()}.
428-type debug_opt() ::
429	{'debug',
430	 Dbgs ::
431	   ['trace' | 'log' | 'statistics' | 'debug'
432	    | {'logfile', string()}]}.
433-type hibernate_after_opt() ::
434	{'hibernate_after', HibernateAfterTimeout :: timeout()}.
435-type start_opt() ::
436	debug_opt()
437      | {'timeout', Time :: timeout()}
438	  | hibernate_after_opt()
439      | {'spawn_opt', [proc_lib:spawn_option()]}.
440-type start_ret() ::  {'ok', pid()} | 'ignore' | {'error', term()}.
441
442
443
444%% Start a state machine
445-spec start(
446	Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
447		   start_ret().
448start(Module, Args, Opts) ->
449    gen:start(?MODULE, nolink, Module, Args, Opts).
450%%
451-spec start(
452	ServerName :: server_name(),
453	Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
454		   start_ret().
455start(ServerName, Module, Args, Opts) ->
456    gen:start(?MODULE, nolink, ServerName, Module, Args, Opts).
457
458%% Start and link to a state machine
459-spec start_link(
460	Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
461		   start_ret().
462start_link(Module, Args, Opts) ->
463    gen:start(?MODULE, link, Module, Args, Opts).
464%%
465-spec start_link(
466	ServerName :: server_name(),
467	Module :: module(), Args :: term(), Opts :: [start_opt()]) ->
468		   start_ret().
469start_link(ServerName, Module, Args, Opts) ->
470    gen:start(?MODULE, link, ServerName, Module, Args, Opts).
471
472%% Stop a state machine
473-spec stop(ServerRef :: server_ref()) -> ok.
474stop(ServerRef) ->
475    gen:stop(ServerRef).
476%%
477-spec stop(
478	ServerRef :: server_ref(),
479	Reason :: term(),
480	Timeout :: timeout()) -> ok.
481stop(ServerRef, Reason, Timeout) ->
482    gen:stop(ServerRef, Reason, Timeout).
483
484%% Send an event to a state machine that arrives with type 'event'
485-spec cast(ServerRef :: server_ref(), Msg :: term()) -> ok.
486cast(ServerRef, Msg) when is_pid(ServerRef) ->
487    send(ServerRef, wrap_cast(Msg));
488cast(ServerRef, Msg) when is_atom(ServerRef) ->
489    send(ServerRef, wrap_cast(Msg));
490cast({global,Name}, Msg) ->
491    try	global:send(Name, wrap_cast(Msg)) of
492	_ -> ok
493    catch
494	_:_ -> ok
495    end;
496cast({via,RegMod,Name}, Msg) ->
497    try	RegMod:send(Name, wrap_cast(Msg)) of
498	_ -> ok
499    catch
500	_:_ -> ok
501    end;
502cast({Name,Node} = ServerRef, Msg) when is_atom(Name), is_atom(Node) ->
503    send(ServerRef, wrap_cast(Msg)).
504
505%% Call a state machine (synchronous; a reply is expected) that
506%% arrives with type {call,From}
507-spec call(ServerRef :: server_ref(), Request :: term()) -> Reply :: term().
508call(ServerRef, Request) ->
509    call(ServerRef, Request, infinity).
510%%
511-spec call(
512	ServerRef :: server_ref(),
513	Request :: term(),
514	Timeout ::
515	  timeout() |
516	  {'clean_timeout',T :: timeout()} |
517	  {'dirty_timeout',T :: timeout()}) ->
518		  Reply :: term().
519call(ServerRef, Request, infinity = T = Timeout) ->
520    call_dirty(ServerRef, Request, Timeout, T);
521call(ServerRef, Request, {dirty_timeout, T} = Timeout) ->
522    call_dirty(ServerRef, Request, Timeout, T);
523call(ServerRef, Request, {clean_timeout, T} = Timeout) ->
524    call_clean(ServerRef, Request, Timeout, T);
525call(ServerRef, Request, {_, _} = Timeout) ->
526    erlang:error(badarg, [ServerRef,Request,Timeout]);
527call(ServerRef, Request, Timeout) ->
528    call_clean(ServerRef, Request, Timeout, Timeout).
529
530%% Reply from a state machine callback to whom awaits in call/2
531-spec reply([reply_action()] | reply_action()) -> ok.
532reply({reply,From,Reply}) ->
533    reply(From, Reply);
534reply(Replies) when is_list(Replies) ->
535    replies(Replies).
536%%
537-compile({inline, [reply/2]}).
538-spec reply(From :: from(), Reply :: term()) -> ok.
539reply({To,Tag}, Reply) when is_pid(To) ->
540    Msg = {Tag,Reply},
541    try To ! Msg of
542	_ ->
543	    ok
544    catch
545	_:_ -> ok
546    end.
547
548%% Instead of starting the state machine through start/3,4
549%% or start_link/3,4 turn the current process presumably
550%% started by proc_lib into a state machine using
551%% the same arguments as you would have returned from init/1
552-spec enter_loop(
553	Module :: module(), Opts :: [debug_opt() | hibernate_after_opt()],
554	State :: state(), Data :: data()) ->
555			no_return().
556enter_loop(Module, Opts, State, Data) ->
557    enter_loop(Module, Opts, State, Data, self()).
558%%
559-spec enter_loop(
560	Module :: module(), Opts :: [debug_opt() | hibernate_after_opt()],
561	State :: state(), Data :: data(),
562	Server_or_Actions ::
563	  server_name() | pid() | [action()]) ->
564			no_return().
565enter_loop(Module, Opts, State, Data, Server_or_Actions) ->
566    if
567	is_list(Server_or_Actions) ->
568	    enter_loop(Module, Opts, State, Data, self(), Server_or_Actions);
569	true ->
570	    enter_loop(Module, Opts, State, Data, Server_or_Actions, [])
571    end.
572%%
573-spec enter_loop(
574	Module :: module(), Opts :: [debug_opt() | hibernate_after_opt()],
575	State :: state(), Data :: data(),
576	Server :: server_name() | pid(),
577	Actions :: [action()] | action()) ->
578			no_return().
579enter_loop(Module, Opts, State, Data, Server, Actions) ->
580    is_atom(Module) orelse error({atom,Module}),
581    Parent = gen:get_parent(),
582    enter(Module, Opts, State, Data, Server, Actions, Parent).
583
584%%---------------------------------------------------------------------------
585%% API helpers
586
587-compile({inline, [wrap_cast/1]}).
588wrap_cast(Event) ->
589    {'$gen_cast',Event}.
590
591call_dirty(ServerRef, Request, Timeout, T) ->
592    try gen:call(ServerRef, '$gen_call', Request, T) of
593        {ok,Reply} ->
594            Reply
595    catch
596        Class:Reason:Stacktrace ->
597            erlang:raise(
598              Class,
599              {Reason,{?MODULE,call,[ServerRef,Request,Timeout]}},
600              Stacktrace)
601    end.
602
603call_clean(ServerRef, Request, Timeout, T) ->
604    %% Call server through proxy process to dodge any late reply
605    Ref = make_ref(),
606    Self = self(),
607    Pid = spawn(
608            fun () ->
609                    Self !
610                        try gen:call(
611                              ServerRef, '$gen_call', Request, T) of
612                            Result ->
613                                {Ref,Result}
614                        catch Class:Reason:Stacktrace ->
615                                {Ref,Class,Reason,Stacktrace}
616                        end
617            end),
618    Mref = monitor(process, Pid),
619    receive
620        {Ref,Result} ->
621            demonitor(Mref, [flush]),
622            case Result of
623                {ok,Reply} ->
624                    Reply
625            end;
626        {Ref,Class,Reason,Stacktrace} ->
627            demonitor(Mref, [flush]),
628            erlang:raise(
629              Class,
630              {Reason,{?MODULE,call,[ServerRef,Request,Timeout]}},
631              Stacktrace);
632        {'DOWN',Mref,_,_,Reason} ->
633            %% There is a theoretical possibility that the
634            %% proxy process gets killed between try--of and !
635            %% so this clause is in case of that
636            exit(Reason)
637    end.
638
639replies([{reply,From,Reply}|Replies]) ->
640    reply(From, Reply),
641    replies(Replies);
642replies([]) ->
643    ok.
644
645%% Might actually not send the message in case of caught exception
646send(Proc, Msg) ->
647    try erlang:send(Proc, Msg)
648    catch
649        error:_ -> ok
650    end,
651    ok.
652
653%% Here the init_it/6 and enter_loop/5,6,7 functions converge
654enter(Module, Opts, State, Data, Server, Actions, Parent) ->
655    %% The values should already have been type checked
656    Name = gen:get_proc_name(Server),
657    Debug = gen:debug_options(Name, Opts),
658    HibernateAfterTimeout = gen:hibernate_after(Opts),
659    Events = [],
660    Event = {internal,init_state},
661    %% We enforce {postpone,false} to ensure that
662    %% our fake Event gets discarded, thought it might get logged
663    NewActions = listify(Actions) ++ [{postpone,false}],
664    S =
665        #state{
666           module = Module,
667           name = Name,
668           state = State,
669           data = Data,
670           hibernate_after = HibernateAfterTimeout},
671    CallEnter = true,
672    NewDebug = ?sys_debug(Debug, {Name,State}, {enter,Event,State}),
673    case call_callback_mode(S) of
674	#state{} = NewS ->
675	    loop_event_actions_list(
676	      Parent, NewDebug, NewS,
677	      Events, Event, State, Data, false,
678              NewActions, CallEnter);
679	[Class,Reason,Stacktrace] ->
680	    terminate(
681	      Class, Reason, Stacktrace, NewDebug,
682	      S, [Event|Events])
683    end.
684
685%%%==========================================================================
686%%%  gen callbacks
687
688init_it(Starter, self, ServerRef, Module, Args, Opts) ->
689    init_it(Starter, self(), ServerRef, Module, Args, Opts);
690init_it(Starter, Parent, ServerRef, Module, Args, Opts) ->
691    try Module:init(Args) of
692	Result ->
693	    init_result(Starter, Parent, ServerRef, Module, Result, Opts)
694    catch
695	Result ->
696	    init_result(Starter, Parent, ServerRef, Module, Result, Opts);
697	Class:Reason:Stacktrace ->
698	    Name = gen:get_proc_name(ServerRef),
699	    gen:unregister_name(ServerRef),
700	    proc_lib:init_ack(Starter, {error,Reason}),
701	    error_info(
702	      Class, Reason, Stacktrace,
703	      #state{name = Name},
704	      []),
705	    erlang:raise(Class, Reason, Stacktrace)
706    end.
707
708%%---------------------------------------------------------------------------
709%% gen callbacks helpers
710
711init_result(Starter, Parent, ServerRef, Module, Result, Opts) ->
712    case Result of
713	{ok,State,Data} ->
714	    proc_lib:init_ack(Starter, {ok,self()}),
715	    enter(Module, Opts, State, Data, ServerRef, [], Parent);
716	{ok,State,Data,Actions} ->
717	    proc_lib:init_ack(Starter, {ok,self()}),
718	    enter(Module, Opts, State, Data, ServerRef, Actions, Parent);
719	{stop,Reason} ->
720	    gen:unregister_name(ServerRef),
721	    proc_lib:init_ack(Starter, {error,Reason}),
722	    exit(Reason);
723	ignore ->
724	    gen:unregister_name(ServerRef),
725	    proc_lib:init_ack(Starter, ignore),
726	    exit(normal);
727	_ ->
728	    Name = gen:get_proc_name(ServerRef),
729	    gen:unregister_name(ServerRef),
730	    Error = {bad_return_from_init,Result},
731	    proc_lib:init_ack(Starter, {error,Error}),
732	    error_info(
733	      error, Error, ?STACKTRACE(),
734	      #state{name = Name},
735	      []),
736	    exit(Error)
737    end.
738
739%%%==========================================================================
740%%% sys callbacks
741
742system_continue(Parent, Debug, S) ->
743    loop(Parent, Debug, S).
744
745system_terminate(Reason, _Parent, Debug, S) ->
746    terminate(exit, Reason, ?STACKTRACE(), Debug, S, []).
747
748system_code_change(
749  #state{
750     module = Module,
751     state = State,
752     data = Data} = S,
753  _Mod, OldVsn, Extra) ->
754    case
755	try Module:code_change(OldVsn, State, Data, Extra)
756	catch
757	    Result -> Result
758	end
759    of
760	{ok,NewState,NewData} ->
761	    {ok,
762	     S#state{
763               callback_mode = undefined,
764               state = NewState,
765               data = NewData}};
766	{ok,_} = Error ->
767	    error({case_clause,Error});
768	Error ->
769	    Error
770    end.
771
772system_get_state(#state{state = State, data = Data}) ->
773    {ok,{State,Data}}.
774
775system_replace_state(
776  StateFun,
777  #state{
778     state = State,
779     data = Data} = S) ->
780    {NewState,NewData} = Result = StateFun({State,Data}),
781    {ok,Result,S#state{state = NewState, data = NewData}}.
782
783format_status(
784  Opt,
785  [PDict,SysState,Parent,Debug,
786   #state{name = Name, postponed = P} = S]) ->
787    Header = gen:format_status_header("Status for state machine", Name),
788    Log = sys:get_debug(log, Debug, []),
789    [{header,Header},
790     {data,
791      [{"Status",SysState},
792       {"Parent",Parent},
793       {"Logged Events",Log},
794       {"Postponed",P}]} |
795     case format_status(Opt, PDict, S) of
796	 L when is_list(L) -> L;
797	 T -> [T]
798     end].
799
800%%---------------------------------------------------------------------------
801%% Format debug messages.  Print them as the call-back module sees
802%% them, not as the real erlang messages.  Use trace for that.
803%%---------------------------------------------------------------------------
804
805sys_debug(Debug, NameState, Entry) ->
806  sys:handle_debug(Debug, fun print_event/3, NameState, Entry).
807
808print_event(Dev, {in,Event}, {Name,State}) ->
809    io:format(
810      Dev, "*DBG* ~tp receive ~ts in state ~tp~n",
811      [Name,event_string(Event),State]);
812print_event(Dev, {out,Reply,{To,_Tag}}, {Name,State}) ->
813    io:format(
814      Dev, "*DBG* ~tp send ~tp to ~p from state ~tp~n",
815      [Name,Reply,To,State]);
816print_event(Dev, {terminate,Reason}, {Name,State}) ->
817    io:format(
818      Dev, "*DBG* ~tp terminate ~tp in state ~tp~n",
819      [Name,Reason,State]);
820print_event(Dev, {Tag,Event,NextState}, {Name,State}) ->
821    StateString =
822	case NextState of
823	    State ->
824		io_lib:format("~tp", [State]);
825	    _ ->
826		io_lib:format("~tp => ~tp", [State,NextState])
827	end,
828    io:format(
829      Dev, "*DBG* ~tp ~tw ~ts in state ~ts~n",
830      [Name,Tag,event_string(Event),StateString]).
831
832event_string(Event) ->
833    case Event of
834	{{call,{Pid,_Tag}},Request} ->
835	    io_lib:format("call ~tp from ~w", [Request,Pid]);
836	{EventType,EventContent} ->
837	    io_lib:format("~tw ~tp", [EventType,EventContent])
838    end.
839
840%%%==========================================================================
841%%% Internal callbacks
842
843wakeup_from_hibernate(Parent, Debug, S) ->
844    %% It is a new message that woke us up so we have to receive it now
845    loop_receive(Parent, Debug, S).
846
847%%%==========================================================================
848%%% State Machine engine implementation of proc_lib/gen server
849
850%% Server loop, consists of all loop* functions
851%% and detours through sys:handle_system_message/7 and proc_lib:hibernate/3
852
853%% Entry point for system_continue/3
854loop(Parent, Debug, #state{hibernate = true} = S) ->
855    loop_hibernate(Parent, Debug, S);
856loop(Parent, Debug, S) ->
857    loop_receive(Parent, Debug, S).
858
859loop_hibernate(Parent, Debug, S) ->
860    %%
861    %% Does not return but restarts process at
862    %% wakeup_from_hibernate/3 that jumps to loop_receive/3
863    %%
864    proc_lib:hibernate(
865      ?MODULE, wakeup_from_hibernate, [Parent,Debug,S]),
866    error(
867      {should_not_have_arrived_here_but_instead_in,
868       {wakeup_from_hibernate,3}}).
869
870%% Entry point for wakeup_from_hibernate/3
871loop_receive(
872  Parent, Debug, #state{hibernate_after = HibernateAfterTimeout} = S) ->
873    %%
874    receive
875	Msg ->
876	    case Msg of
877		{system,Pid,Req} ->
878		    %% Does not return but tail recursively calls
879		    %% system_continue/3 that jumps to loop/3
880		    sys:handle_system_msg(
881		      Req, Pid, Parent, ?MODULE, Debug, S,
882		      S#state.hibernate);
883		{'EXIT',Parent,Reason} = EXIT ->
884		    %% EXIT is not a 2-tuple therefore
885		    %% not an event but this will stand out
886		    %% in the crash report...
887		    Q = [EXIT],
888		    terminate(exit, Reason, ?STACKTRACE(), Debug, S, Q);
889		{timeout,TimerRef,TimerMsg} ->
890                    case S#state.timers of
891			{#{TimerRef := TimerType} = TimerRefs,TimerTypes} ->
892                            %% Our timer
893                            NewTimers =
894                                {maps:remove(TimerRef, TimerRefs),
895                                 maps:remove(TimerType, TimerTypes)},
896                            loop_receive_result(
897                              Parent, Debug,
898                              S#state{timers = NewTimers},
899                              TimerType, TimerMsg);
900			{#{},_} ->
901			    %% Not our timer; present it as an event
902			    loop_receive_result(Parent, Debug, S, info, Msg)
903		    end;
904		_ ->
905		    %% External msg
906                    case Msg of
907                        {'$gen_call',From,Request} ->
908                            loop_receive_result(
909                              Parent, Debug, S, {call,From}, Request);
910                        {'$gen_cast',Cast} ->
911                            loop_receive_result(Parent, Debug, S, cast, Cast);
912                        _ ->
913                            loop_receive_result(Parent, Debug, S, info, Msg)
914                    end
915	    end
916    after
917	    HibernateAfterTimeout ->
918		    loop_hibernate(Parent, Debug, S)
919    end.
920
921loop_receive_result(Parent, ?not_sys_debug, S, Type, Content) ->
922    %% Here is the queue of not yet handled events created
923    Events = [],
924    loop_event(Parent, ?not_sys_debug, S, Events, Type, Content);
925loop_receive_result(
926  Parent, Debug, #state{name = Name, state = State} = S, Type, Content) ->
927    NewDebug = sys_debug(Debug, {Name,State}, {in,{Type,Content}}),
928    %% Here is the queue of not yet handled events created
929    Events = [],
930    loop_event(Parent, NewDebug, S, Events, Type, Content).
931
932%% Entry point for handling an event, received or enqueued
933loop_event(
934  Parent, Debug, #state{hibernate = Hibernate} = S,
935  Events, Type, Content) ->
936    %%
937    case Hibernate of
938        true ->
939            %%
940            %% If (this old) Hibernate is true here it can only be
941            %% because it was set from an event action
942            %% and we did not go into hibernation since there were
943            %% events in queue, so we do what the user
944            %% might rely on i.e collect garbage which
945            %% would have happened if we actually hibernated
946            %% and immediately was awakened.
947            %%
948            _ = garbage_collect(),
949            loop_event_state_function(
950              Parent, Debug, S, Events, Type, Content);
951        false ->
952            loop_event_state_function(
953              Parent, Debug, S, Events, Type, Content)
954    end.
955
956%% Call the state function
957loop_event_state_function(
958  Parent, Debug,
959  #state{state = State, data = Data} = S,
960  Events, Type, Content) ->
961    %%
962    %% The field 'hibernate' in S is now invalid and will be
963    %% restored when looping back to loop/3 or loop_event/6.
964    %%
965    Event = {Type,Content},
966    TransOpts = false,
967    case call_state_function(S, Type, Content, State, Data) of
968        {Result, NewS} ->
969            loop_event_result(
970              Parent, Debug, NewS,
971              Events, Event, State, Data, TransOpts, Result);
972	[Class,Reason,Stacktrace] ->
973	    terminate(
974	      Class, Reason, Stacktrace, Debug, S, [Event|Events])
975    end.
976
977%% Make a state enter call to the state function
978loop_event_state_enter(
979  Parent, Debug, #state{state = PrevState} = S,
980  Events, Event, NextState, NewData, TransOpts) ->
981    %%
982    case call_state_function(S, enter, PrevState, NextState, NewData) of
983        {Result, NewS} ->
984            loop_event_result(
985              Parent, Debug, NewS,
986              Events, Event, NextState, NewData, TransOpts, Result);
987	[Class,Reason,Stacktrace] ->
988	    terminate(
989	      Class, Reason, Stacktrace, Debug, S, [Event|Events])
990    end.
991
992%% Process the result from the state function.
993%% When TransOpts =:= false it was a state function call,
994%% otherwise it is an option tuple and it was a state enter call.
995%%
996loop_event_result(
997  Parent, Debug, S,
998  Events, Event, State, Data, TransOpts, Result) ->
999    %%
1000    case Result of
1001	{next_state,State,NewData} ->
1002            loop_event_actions(
1003              Parent, Debug, S,
1004              Events, Event, State, NewData, TransOpts,
1005              [], false);
1006	{next_state,NextState,NewData}
1007          when TransOpts =:= false ->
1008            loop_event_actions(
1009              Parent, Debug, S,
1010              Events, Event, NextState, NewData, TransOpts,
1011              [], true);
1012	{next_state,_NextState,_NewData} ->
1013            terminate(
1014              error,
1015              {bad_state_enter_return_from_state_function,Result},
1016              ?STACKTRACE(), Debug,
1017              S#state{
1018                state = State, data = Data,
1019                hibernate = hibernate_in_trans_opts(TransOpts)},
1020              [Event|Events]);
1021	{next_state,State,NewData,Actions} ->
1022            loop_event_actions(
1023              Parent, Debug, S,
1024              Events, Event, State, NewData, TransOpts,
1025              Actions, false);
1026	{next_state,NextState,NewData,Actions}
1027          when TransOpts =:= false ->
1028            loop_event_actions(
1029              Parent, Debug, S,
1030              Events, Event, NextState, NewData, TransOpts,
1031              Actions, true);
1032	{next_state,_NextState,_NewData,_Actions} ->
1033            terminate(
1034              error,
1035              {bad_state_enter_return_from_state_function,Result},
1036              ?STACKTRACE(), Debug,
1037              S#state{
1038                state = State, data = Data,
1039                hibernate = hibernate_in_trans_opts(TransOpts)},
1040              [Event|Events]);
1041        %%
1042        {keep_state,NewData} ->
1043            loop_event_actions(
1044              Parent, Debug, S,
1045              Events, Event, State, NewData, TransOpts,
1046              [], false);
1047        {keep_state,NewData,Actions} ->
1048            loop_event_actions(
1049              Parent, Debug, S,
1050              Events, Event, State, NewData, TransOpts,
1051              Actions, false);
1052        %%
1053        keep_state_and_data ->
1054            loop_event_actions(
1055              Parent, Debug, S,
1056              Events, Event, State, Data, TransOpts,
1057              [], false);
1058        {keep_state_and_data,Actions} ->
1059            loop_event_actions(
1060              Parent, Debug, S,
1061              Events, Event, State, Data, TransOpts,
1062              Actions, false);
1063        %%
1064        {repeat_state,NewData} ->
1065            loop_event_actions(
1066              Parent, Debug, S,
1067              Events, Event, State, NewData, TransOpts,
1068              [], true);
1069        {repeat_state,NewData,Actions} ->
1070            loop_event_actions(
1071              Parent, Debug, S,
1072              Events, Event, State, NewData, TransOpts,
1073              Actions, true);
1074        %%
1075        repeat_state_and_data ->
1076            loop_event_actions(
1077              Parent, Debug, S,
1078              Events, Event, State, Data, TransOpts,
1079              [], true);
1080        {repeat_state_and_data,Actions} ->
1081            loop_event_actions(
1082              Parent, Debug, S,
1083              Events, Event, State, Data, TransOpts,
1084              Actions, true);
1085        %%
1086	stop ->
1087            terminate(
1088              exit, normal, ?STACKTRACE(), Debug,
1089              S#state{
1090                state = State, data = Data,
1091                hibernate = hibernate_in_trans_opts(TransOpts)},
1092              [Event|Events]);
1093	{stop,Reason} ->
1094            terminate(
1095              exit, Reason, ?STACKTRACE(), Debug,
1096              S#state{
1097                state = State, data = Data,
1098                hibernate = hibernate_in_trans_opts(TransOpts)},
1099              [Event|Events]);
1100	{stop,Reason,NewData} ->
1101            terminate(
1102              exit, Reason, ?STACKTRACE(), Debug,
1103              S#state{
1104                state = State, data = NewData,
1105                hibernate = hibernate_in_trans_opts(TransOpts)},
1106              [Event|Events]);
1107	%%
1108	{stop_and_reply,Reason,Replies} ->
1109            reply_then_terminate(
1110              exit, Reason, ?STACKTRACE(), Debug,
1111              S#state{
1112                state = State, data = Data,
1113                hibernate = hibernate_in_trans_opts(TransOpts)},
1114              [Event|Events], Replies);
1115	{stop_and_reply,Reason,Replies,NewData} ->
1116            reply_then_terminate(
1117              exit, Reason, ?STACKTRACE(), Debug,
1118              S#state{
1119                state = State, data = NewData,
1120                hibernate = hibernate_in_trans_opts(TransOpts)},
1121              [Event|Events], Replies);
1122	%%
1123	_ ->
1124            terminate(
1125              error,
1126              {bad_return_from_state_function,Result},
1127              ?STACKTRACE(), Debug,
1128              S#state{
1129                state = State, data = Data,
1130                hibernate = hibernate_in_trans_opts(TransOpts)},
1131              [Event|Events])
1132    end.
1133
1134%% Ensure that Actions are a list
1135loop_event_actions(
1136  Parent, Debug, S,
1137  Events, Event, NextState, NewerData, TransOpts,
1138  Actions, CallEnter) ->
1139    loop_event_actions_list(
1140      Parent, Debug, S,
1141      Events, Event, NextState, NewerData, TransOpts,
1142      listify(Actions), CallEnter).
1143
1144%% Process actions from the state function
1145loop_event_actions_list(
1146  Parent, Debug, #state{state_enter = StateEnter} = S,
1147  Events, Event, NextState, NewerData, TransOpts,
1148  Actions, CallEnter) ->
1149    %%
1150    case parse_actions(TransOpts, Debug, S, Actions) of
1151        {NewDebug,NewTransOpts}
1152          when StateEnter, CallEnter ->
1153            loop_event_state_enter(
1154              Parent, NewDebug, S,
1155              Events, Event, NextState, NewerData, NewTransOpts);
1156        {NewDebug,NewTransOpts} ->
1157            loop_event_done(
1158              Parent, NewDebug, S,
1159              Events, Event, NextState, NewerData, NewTransOpts);
1160        [Class,Reason,Stacktrace,NewDebug] ->
1161            terminate(
1162              Class, Reason, Stacktrace, NewDebug,
1163              S#state{
1164                state = NextState,
1165                data = NewerData,
1166                hibernate = hibernate_in_trans_opts(TransOpts)},
1167              [Event|Events])
1168    end.
1169
1170-compile({inline, [hibernate_in_trans_opts/1]}).
1171hibernate_in_trans_opts(false) ->
1172    (#trans_opts{})#trans_opts.hibernate;
1173hibernate_in_trans_opts(#trans_opts{hibernate = Hibernate}) ->
1174    Hibernate.
1175
1176parse_actions(false, Debug, S, Actions) ->
1177    parse_actions(true, Debug, S, Actions, #trans_opts{});
1178parse_actions(TransOpts, Debug, S, Actions) ->
1179    parse_actions(false, Debug, S, Actions, TransOpts).
1180%%
1181parse_actions(_StateCall, Debug, _S, [], TransOpts) ->
1182    {Debug,TransOpts};
1183parse_actions(StateCall, Debug, S, [Action|Actions], TransOpts) ->
1184    case Action of
1185	%% Actual actions
1186	{reply,From,Reply} ->
1187            parse_actions_reply(
1188              StateCall, Debug, S, Actions, TransOpts, From, Reply);
1189	%%
1190	%% Actions that set options
1191	{hibernate,NewHibernate} when is_boolean(NewHibernate) ->
1192            parse_actions(
1193              StateCall, Debug, S, Actions,
1194              TransOpts#trans_opts{hibernate = NewHibernate});
1195	hibernate ->
1196            parse_actions(
1197              StateCall, Debug, S, Actions,
1198              TransOpts#trans_opts{hibernate = true});
1199	%%
1200	{postpone,NewPostpone} when not NewPostpone orelse StateCall ->
1201            parse_actions(
1202              StateCall, Debug, S, Actions,
1203              TransOpts#trans_opts{postpone = NewPostpone});
1204	postpone when StateCall ->
1205            parse_actions(
1206              StateCall, Debug, S, Actions,
1207              TransOpts#trans_opts{postpone = true});
1208	postpone ->
1209            [error,
1210             {bad_state_enter_action_from_state_function,Action},
1211             ?STACKTRACE(),
1212             Debug];
1213	%%
1214	{next_event,Type,Content} ->
1215            parse_actions_next_event(
1216              StateCall, Debug, S, Actions, TransOpts, Type, Content);
1217	%%
1218        _ ->
1219            parse_actions_timeout(
1220              StateCall, Debug, S, Actions, TransOpts, Action)
1221    end.
1222
1223parse_actions_reply(
1224  StateCall, ?not_sys_debug, S, Actions, TransOpts,
1225  From, Reply) ->
1226    %%
1227    case from(From) of
1228        true ->
1229            reply(From, Reply),
1230            parse_actions(StateCall, ?not_sys_debug, S, Actions, TransOpts);
1231        false ->
1232            [error,
1233             {bad_action_from_state_function,{reply,From,Reply}},
1234             ?STACKTRACE(),
1235             ?not_sys_debug]
1236    end;
1237parse_actions_reply(
1238  StateCall, Debug, #state{name = Name, state = State} = S,
1239  Actions, TransOpts, From, Reply) ->
1240    %%
1241    case from(From) of
1242        true ->
1243            reply(From, Reply),
1244            NewDebug = sys_debug(Debug, {Name,State}, {out,Reply,From}),
1245            parse_actions(StateCall, NewDebug, S, Actions, TransOpts);
1246        false ->
1247            [error,
1248             {bad_action_from_state_function,{reply,From,Reply}},
1249             ?STACKTRACE(),
1250             Debug]
1251    end.
1252
1253parse_actions_next_event(
1254  StateCall, ?not_sys_debug, S,
1255  Actions, TransOpts, Type, Content) ->
1256    case event_type(Type) of
1257        true when StateCall ->
1258            NextEventsR = TransOpts#trans_opts.next_events_r,
1259            parse_actions(
1260              StateCall, ?not_sys_debug, S, Actions,
1261              TransOpts#trans_opts{
1262                next_events_r = [{Type,Content}|NextEventsR]});
1263        _ ->
1264            [error,
1265             {bad_state_enter_action_from_state_function,
1266	      {next_event,Type,Content}},
1267             ?STACKTRACE(),
1268             ?not_sys_debug]
1269    end;
1270parse_actions_next_event(
1271  StateCall, Debug, #state{name = Name, state = State} = S,
1272  Actions, TransOpts, Type, Content) ->
1273    case event_type(Type) of
1274        true when StateCall ->
1275            NewDebug = sys_debug(Debug, {Name,State}, {in,{Type,Content}}),
1276            NextEventsR = TransOpts#trans_opts.next_events_r,
1277            parse_actions(
1278              StateCall, NewDebug, S, Actions,
1279              TransOpts#trans_opts{
1280                next_events_r = [{Type,Content}|NextEventsR]});
1281        _ ->
1282            [error,
1283             {bad_state_enter_action_from_state_function,
1284	      {next_event,Type,Content}},
1285             ?STACKTRACE(),
1286             Debug]
1287    end.
1288
1289parse_actions_timeout(
1290  StateCall, Debug, S, Actions, TransOpts,
1291  {TimeoutType,Time,TimerMsg,TimerOpts} = AbsoluteTimeout) ->
1292    %%
1293    case classify_timeout(TimeoutType, Time, listify(TimerOpts)) of
1294        absolute ->
1295            parse_actions_timeout_add(
1296              StateCall, Debug, S, Actions,
1297              TransOpts, AbsoluteTimeout);
1298        relative ->
1299            RelativeTimeout = {TimeoutType,Time,TimerMsg},
1300            parse_actions_timeout_add(
1301              StateCall, Debug, S, Actions,
1302              TransOpts, RelativeTimeout);
1303        badarg ->
1304            [error,
1305             {bad_action_from_state_function,AbsoluteTimeout},
1306             ?STACKTRACE(),
1307             Debug]
1308    end;
1309parse_actions_timeout(
1310  StateCall, Debug, S, Actions, TransOpts,
1311  {TimeoutType,Time,_} = RelativeTimeout) ->
1312    case classify_timeout(TimeoutType, Time, []) of
1313        relative ->
1314            parse_actions_timeout_add(
1315              StateCall, Debug, S, Actions,
1316              TransOpts, RelativeTimeout);
1317        badarg ->
1318            [error,
1319             {bad_action_from_state_function,RelativeTimeout},
1320             ?STACKTRACE(),
1321             Debug]
1322    end;
1323parse_actions_timeout(
1324  StateCall, Debug, S, Actions, TransOpts,
1325  Time) ->
1326    case classify_timeout(timeout, Time, []) of
1327        relative ->
1328            RelativeTimeout = {timeout,Time,Time},
1329            parse_actions_timeout_add(
1330              StateCall, Debug, S, Actions,
1331              TransOpts, RelativeTimeout);
1332        badarg ->
1333            [error,
1334             {bad_action_from_state_function,Time},
1335             ?STACKTRACE(),
1336             Debug]
1337    end.
1338
1339parse_actions_timeout_add(
1340  StateCall, Debug, S, Actions,
1341  #trans_opts{timeouts_r = TimeoutsR} = TransOpts, Timeout) ->
1342    parse_actions(
1343      StateCall, Debug, S, Actions,
1344      TransOpts#trans_opts{timeouts_r = [Timeout|TimeoutsR]}).
1345
1346%% Do the state transition
1347loop_event_done(
1348  Parent, ?not_sys_debug,
1349  #state{postponed = P} = S,
1350  Events, Event, NextState, NewData,
1351  #trans_opts{
1352     postpone = Postpone, hibernate = Hibernate,
1353     timeouts_r = [], next_events_r = []}) ->
1354    %%
1355    %% Optimize the simple cases
1356    %% i.e no timer changes, no inserted events and no debug,
1357    %% by duplicate stripped down code
1358    %%
1359    %% Fast path
1360    %%
1361    case Postpone of
1362        true ->
1363            loop_event_done_fast(
1364              Parent, Hibernate,
1365              S,
1366              Events, [Event|P], NextState, NewData);
1367        false ->
1368            loop_event_done_fast(
1369              Parent, Hibernate,
1370              S,
1371              Events, P, NextState, NewData)
1372    end;
1373loop_event_done(
1374  Parent, Debug_0,
1375  #state{
1376     state = State, postponed = P_0, timers = Timers_0} = S,
1377  Events_0, Event_0, NextState, NewData,
1378  #trans_opts{
1379     hibernate = Hibernate, timeouts_r = TimeoutsR,
1380     postpone = Postpone, next_events_r = NextEventsR}) ->
1381    %%
1382    %% All options have been collected and next_events are buffered.
1383    %% Do the actual state transition.
1384    %%
1385    %% Full feature path
1386    %%
1387    [Debug_1|P_1] = % Move current event to postponed if Postpone
1388	case Postpone of
1389	    true ->
1390		[?sys_debug(
1391                    Debug_0,
1392                    {S#state.name,State},
1393                    {postpone,Event_0,NextState}),
1394		 Event_0|P_0];
1395	    false ->
1396		[?sys_debug(
1397                    Debug_0,
1398                    {S#state.name,State},
1399                    {consume,Event_0,NextState})|P_0]
1400	end,
1401    {Events_2,P_2,Timers_2} =
1402	%% Move all postponed events to queue,
1403        %% cancel the event timer,
1404        %% and cancel the state timeout if the state changes
1405	if
1406	    NextState =:= State ->
1407		{Events_0,P_1,
1408                 cancel_timer_by_type(timeout, Timers_0)};
1409	    true ->
1410		{lists:reverse(P_1, Events_0),
1411		 [],
1412		 cancel_timer_by_type(
1413		   state_timeout,
1414                   cancel_timer_by_type(timeout, Timers_0))}
1415	end,
1416    {Timers_3,TimeoutEvents} =
1417	%% Stop and start timers
1418	parse_timers(Timers_2, TimeoutsR),
1419    %% Place next events last in reversed queue
1420    Events_3R = lists:reverse(Events_2, NextEventsR),
1421    %% Enqueue immediate timeout events
1422    Events_4R =	prepend_timeout_events(TimeoutEvents, Events_3R),
1423    loop_event_done(
1424      Parent, Debug_1,
1425      S#state{
1426        state = NextState,
1427        data = NewData,
1428        postponed = P_2,
1429        timers = Timers_3,
1430        hibernate = Hibernate},
1431      lists:reverse(Events_4R)).
1432
1433%% Fast path
1434%%
1435loop_event_done_fast(
1436  Parent, Hibernate,
1437  #state{
1438     state = NextState,
1439     timers = {_,#{timeout := _}} = Timers} = S,
1440  Events, P, NextState, NewData) ->
1441    %%
1442    %% Same state, event timeout active
1443    %%
1444    loop_event_done_fast(
1445      Parent, Hibernate, S,
1446      Events, P, NextState, NewData,
1447      cancel_timer_by_type(timeout, Timers));
1448loop_event_done_fast(
1449  Parent, Hibernate,
1450  #state{state = NextState} = S,
1451  Events, P, NextState, NewData) ->
1452    %%
1453    %% Same state
1454    %%
1455    loop_event_done(
1456      Parent, ?not_sys_debug,
1457      S#state{
1458        data = NewData,
1459        postponed = P,
1460        hibernate = Hibernate},
1461      Events);
1462loop_event_done_fast(
1463  Parent, Hibernate,
1464  #state{
1465     timers = {_,#{timeout := _}} = Timers} = S,
1466  Events, P, NextState, NewData) ->
1467    %%
1468    %% State change, event timeout active
1469    %%
1470    loop_event_done_fast(
1471      Parent, Hibernate, S,
1472      lists:reverse(P, Events), [], NextState, NewData,
1473        cancel_timer_by_type(
1474          state_timeout,
1475          cancel_timer_by_type(timeout, Timers)));
1476loop_event_done_fast(
1477  Parent, Hibernate,
1478  #state{
1479     timers = {_,#{state_timeout := _}} = Timers} = S,
1480  Events, P, NextState, NewData) ->
1481    %%
1482    %% State change, state timeout active
1483    %%
1484    loop_event_done_fast(
1485      Parent, Hibernate, S,
1486      lists:reverse(P, Events), [], NextState, NewData,
1487        cancel_timer_by_type(
1488          state_timeout,
1489          cancel_timer_by_type(timeout, Timers)));
1490loop_event_done_fast(
1491  Parent, Hibernate,
1492  #state{} = S,
1493  Events, P, NextState, NewData) ->
1494    %%
1495    %% State change, no timeout to automatically cancel
1496    %%
1497    loop_event_done(
1498      Parent, ?not_sys_debug,
1499      S#state{
1500        state = NextState,
1501        data = NewData,
1502        postponed = [],
1503        hibernate = Hibernate},
1504      lists:reverse(P, Events)).
1505%%
1506%% Fast path
1507%%
1508loop_event_done_fast(
1509  Parent, Hibernate, S, Events, P, NextState, NewData, Timers) ->
1510    %%
1511    loop_event_done(
1512      Parent, ?not_sys_debug,
1513      S#state{
1514        state = NextState,
1515        data = NewData,
1516        postponed = P,
1517        timers = Timers,
1518        hibernate = Hibernate},
1519      Events).
1520
1521loop_event_done(Parent, Debug, S, Q) ->
1522    case Q of
1523        [] ->
1524            %% Get a new event
1525            loop(Parent, Debug, S);
1526        [{Type,Content}|Events] ->
1527	    %% Loop until out of enqueued events
1528	    loop_event(Parent, Debug, S, Events, Type, Content)
1529    end.
1530
1531
1532%%---------------------------------------------------------------------------
1533%% Server loop helpers
1534
1535call_callback_mode(#state{module = Module} = S) ->
1536    try Module:callback_mode() of
1537	CallbackMode ->
1538	    callback_mode_result(S, CallbackMode)
1539    catch
1540	CallbackMode ->
1541	    callback_mode_result(S, CallbackMode);
1542	Class:Reason:Stacktrace ->
1543	    [Class,Reason,Stacktrace]
1544    end.
1545
1546callback_mode_result(S, CallbackMode) ->
1547    callback_mode_result(
1548      S, CallbackMode, listify(CallbackMode), undefined, false).
1549%%
1550callback_mode_result(_S, CallbackMode, [], undefined, _StateEnter) ->
1551    [error,
1552     {bad_return_from_callback_mode,CallbackMode},
1553     ?STACKTRACE()];
1554callback_mode_result(S, _CallbackMode, [], CBMode, StateEnter) ->
1555    S#state{callback_mode = CBMode, state_enter = StateEnter};
1556callback_mode_result(S, CallbackMode, [H|T], CBMode, StateEnter) ->
1557    case callback_mode(H) of
1558	true ->
1559            callback_mode_result(S, CallbackMode, T, H, StateEnter);
1560	false ->
1561	    case state_enter(H) of
1562		true ->
1563                    callback_mode_result(S, CallbackMode, T, CBMode, true);
1564		false ->
1565                    [error,
1566                     {bad_return_from_callback_mode,CallbackMode},
1567                     ?STACKTRACE()]
1568	    end
1569    end.
1570
1571
1572call_state_function(
1573  #state{callback_mode = undefined} = S, Type, Content, State, Data) ->
1574    case call_callback_mode(S) of
1575	#state{} = NewS ->
1576	    call_state_function(NewS, Type, Content, State, Data);
1577	Error ->
1578	    Error
1579    end;
1580call_state_function(
1581  #state{callback_mode = CallbackMode, module = Module} = S,
1582  Type, Content, State, Data) ->
1583    try
1584	case CallbackMode of
1585	    state_functions ->
1586		Module:State(Type, Content, Data);
1587	    handle_event_function ->
1588		Module:handle_event(Type, Content, State, Data)
1589	end
1590    of
1591	Result ->
1592	    {Result,S}
1593    catch
1594	Result ->
1595	    {Result,S};
1596	Class:Reason:Stacktrace ->
1597	    [Class,Reason,Stacktrace]
1598    end.
1599
1600
1601%% -> absolute | relative | badarg
1602classify_timeout(TimeoutType, Time, Opts) ->
1603    case timeout_event_type(TimeoutType) of
1604        true ->
1605            classify_time(false, Time, Opts);
1606        false ->
1607            badarg
1608    end.
1609
1610classify_time(Abs, Time, []) ->
1611    case Abs of
1612        true when
1613              is_integer(Time);
1614              Time =:= infinity ->
1615            absolute;
1616        false when
1617              is_integer(Time), 0 =< Time;
1618              Time =:= infinity ->
1619            relative;
1620        _ ->
1621            badarg
1622    end;
1623classify_time(_, Time, [{abs,Abs}|Opts]) when is_boolean(Abs) ->
1624    classify_time(Abs, Time, Opts);
1625classify_time(_, _, Opts) when is_list(Opts) ->
1626    badarg.
1627
1628%% Stop and start timers as well as create timeout zero events
1629%% and pending event timer
1630%%
1631%% Stop and start timers non-event timers
1632parse_timers(Timers, TimeoutsR) ->
1633    parse_timers(Timers, TimeoutsR, #{}, []).
1634%%
1635parse_timers(Timers, [], _Seen, TimeoutEvents) ->
1636    %%
1637    {Timers,TimeoutEvents};
1638parse_timers(
1639  Timers, [Timeout|TimeoutsR], Seen, TimeoutEvents) ->
1640    %%
1641    case Timeout of
1642	{TimerType,Time,TimerMsg,TimerOpts} ->
1643	    %% Absolute timer
1644	    parse_timers(
1645	      Timers, TimeoutsR, Seen, TimeoutEvents,
1646	      TimerType, Time, TimerMsg, listify(TimerOpts));
1647	%% Relative timers below
1648	{TimerType,0,TimerMsg} ->
1649	    parse_timers(
1650	      Timers, TimeoutsR, Seen, TimeoutEvents,
1651	      TimerType, zero, TimerMsg, []);
1652	{TimerType,Time,TimerMsg} ->
1653	    parse_timers(
1654	      Timers, TimeoutsR, Seen, TimeoutEvents,
1655	      TimerType, Time, TimerMsg, [])
1656    end.
1657
1658parse_timers(
1659  Timers, TimeoutsR, Seen, TimeoutEvents,
1660  TimerType, Time, TimerMsg, TimerOpts) ->
1661    case Seen of
1662	#{TimerType := _} ->
1663	    %% Type seen before - ignore
1664	    parse_timers(
1665              Timers, TimeoutsR, Seen, TimeoutEvents);
1666	#{} ->
1667	    %% Unseen type - handle
1668	    NewSeen = Seen#{TimerType => true},
1669	    case Time of
1670		infinity ->
1671		    %% Cancel any running timer
1672		    parse_timers(
1673		      cancel_timer_by_type(TimerType, Timers),
1674                      TimeoutsR, NewSeen, TimeoutEvents);
1675		zero ->
1676		    %% Cancel any running timer
1677		    %% Handle zero time timeouts later
1678		    parse_timers(
1679		      cancel_timer_by_type(TimerType, Timers),
1680                      TimeoutsR, NewSeen,
1681                      [{TimerType,TimerMsg}|TimeoutEvents]);
1682		_ ->
1683		    %% (Re)start the timer
1684		    TimerRef =
1685			erlang:start_timer(
1686			  Time, self(), TimerMsg, TimerOpts),
1687                    {TimerRefs,TimerTypes} = Timers,
1688		    case TimerTypes of
1689                         #{TimerType := OldTimerRef} ->
1690			    %% Cancel the running timer,
1691                            %% update the timeout type,
1692                            %% insert the new timer ref,
1693                            %% and remove the old timer ref
1694			    cancel_timer(OldTimerRef),
1695			    %% Insert the new timer into
1696			    %% both TimerRefs and TimerTypes
1697			    parse_timers(
1698			      {maps:remove(
1699                                 OldTimerRef,
1700                                 TimerRefs#{TimerRef => TimerType}),
1701                               TimerTypes#{TimerType := TimerRef}},
1702                              TimeoutsR, NewSeen, TimeoutEvents);
1703                        #{} ->
1704			    %% Insert the new timer type and ref
1705			    parse_timers(
1706			      {TimerRefs#{TimerRef => TimerType},
1707                               TimerTypes#{TimerType => TimerRef}},
1708                              TimeoutsR, NewSeen, TimeoutEvents)
1709		    end
1710	    end
1711    end.
1712
1713%% Enqueue immediate timeout events (timeout 0 events)
1714%%
1715%% Event timer timeout 0 events gets special treatment since
1716%% an event timer is cancelled by any received event,
1717%% so if there are enqueued events before the event timer
1718%% timeout 0 event - the event timer is cancelled hence no event.
1719%%
1720%% Other (state_timeout) timeout 0 events that are after
1721%% the event timer timeout 0 events are considered to
1722%% belong to timers that were started after the event timer
1723%% timeout 0 event fired, so they do not cancel the event timer.
1724%%
1725prepend_timeout_events([], EventsR) ->
1726    EventsR;
1727prepend_timeout_events([{timeout,_} = TimeoutEvent|TimeoutEvents], []) ->
1728    prepend_timeout_events(TimeoutEvents, [TimeoutEvent]);
1729prepend_timeout_events([{timeout,_}|TimeoutEvents], EventsR) ->
1730    %% Ignore since there are other events in queue
1731    %% so they have cancelled the event timeout 0.
1732    prepend_timeout_events(TimeoutEvents, EventsR);
1733prepend_timeout_events([TimeoutEvent|TimeoutEvents], EventsR) ->
1734    %% Just prepend all others
1735    prepend_timeout_events(TimeoutEvents, [TimeoutEvent|EventsR]).
1736
1737
1738
1739%%---------------------------------------------------------------------------
1740%% Server helpers
1741
1742reply_then_terminate(Class, Reason, Stacktrace, Debug, S, Q, Replies) ->
1743    do_reply_then_terminate(
1744      Class, Reason, Stacktrace, Debug, S, Q, listify(Replies)).
1745%%
1746do_reply_then_terminate(
1747  Class, Reason, Stacktrace, Debug, S, Q, []) ->
1748    terminate(Class, Reason, Stacktrace, Debug, S, Q);
1749do_reply_then_terminate(
1750  Class, Reason, Stacktrace, Debug, S, Q, [R|Rs]) ->
1751    case R of
1752	{reply,{_To,_Tag}=From,Reply} ->
1753            reply(From, Reply),
1754            NewDebug =
1755                ?sys_debug(
1756                   Debug,
1757                   begin
1758                       #state{name = Name, state = State} = S,
1759                       {Name,State}
1760                   end,
1761                   {out,Reply,From}),
1762	    do_reply_then_terminate(
1763	      Class, Reason, Stacktrace, NewDebug, S, Q, Rs);
1764	_ ->
1765	    terminate(
1766	      error,
1767	      {bad_reply_action_from_state_function,R},
1768	      ?STACKTRACE(),
1769	      Debug, S, Q)
1770    end.
1771
1772terminate(
1773  Class, Reason, Stacktrace, Debug,
1774  #state{module = Module, state = State, data = Data} = S,
1775  Q) ->
1776    case erlang:function_exported(Module, terminate, 3) of
1777	true ->
1778	    try Module:terminate(Reason, State, Data) of
1779		_ -> ok
1780	    catch
1781		_ -> ok;
1782		C:R:ST ->
1783		    error_info(C, R, ST, S, Q),
1784		    sys:print_log(Debug),
1785		    erlang:raise(C, R, ST)
1786	    end;
1787	false ->
1788	    ok
1789    end,
1790    _ =
1791	case Reason of
1792	    normal ->
1793                terminate_sys_debug(Debug, S, State, Reason);
1794	    shutdown ->
1795                terminate_sys_debug(Debug, S, State, Reason);
1796	    {shutdown,_} ->
1797                terminate_sys_debug(Debug, S, State, Reason);
1798	    _ ->
1799		error_info(Class, Reason, Stacktrace, S, Q),
1800		sys:print_log(Debug)
1801	end,
1802    case Stacktrace of
1803	[] ->
1804	    erlang:Class(Reason);
1805	_ ->
1806	    erlang:raise(Class, Reason, Stacktrace)
1807    end.
1808
1809terminate_sys_debug(Debug, S, State, Reason) ->
1810    ?sys_debug(Debug, {S#state.name,State}, {terminate,Reason}).
1811
1812
1813error_info(
1814  Class, Reason, Stacktrace,
1815  #state{
1816     name = Name,
1817     callback_mode = CallbackMode,
1818     state_enter = StateEnter,
1819     postponed = P} = S,
1820  Q) ->
1821    ?LOG_ERROR(#{label=>{gen_statem,terminate},
1822                 name=>Name,
1823                 queue=>Q,
1824                 postponed=>P,
1825                 callback_mode=>CallbackMode,
1826                 state_enter=>StateEnter,
1827                 state=>format_status(terminate, get(), S),
1828                 reason=>{Class,Reason,Stacktrace}},
1829               #{domain=>[otp],
1830                 report_cb=>fun gen_statem:format_log/1,
1831                 error_logger=>#{tag=>error}}).
1832
1833format_log(#{label:={gen_statem,terminate},
1834             name:=Name,
1835             queue:=Q,
1836             postponed:=P,
1837             callback_mode:=CallbackMode,
1838             state_enter:=StateEnter,
1839             state:=FmtData,
1840             reason:={Class,Reason,Stacktrace}}) ->
1841    {FixedReason,FixedStacktrace} =
1842	case Stacktrace of
1843	    [{M,F,Args,_}|ST]
1844	      when Class =:= error, Reason =:= undef ->
1845		case code:is_loaded(M) of
1846		    false ->
1847			{{'module could not be loaded',M},ST};
1848		    _ ->
1849			Arity =
1850			    if
1851				is_list(Args) ->
1852				    length(Args);
1853				is_integer(Args) ->
1854				    Args
1855			    end,
1856			case erlang:function_exported(M, F, Arity) of
1857			    true ->
1858				{Reason,Stacktrace};
1859			    false ->
1860				{{'function not exported',{M,F,Arity}},
1861				 ST}
1862			end
1863		end;
1864	    _ -> {Reason,Stacktrace}
1865	end,
1866    [LimitedP, LimitedFmtData, LimitedFixedReason] =
1867        [error_logger:limit_term(D) || D <- [P, FmtData, FixedReason]],
1868    CBMode =
1869	 case StateEnter of
1870	     true ->
1871		 [CallbackMode,state_enter];
1872	     false ->
1873		 CallbackMode
1874	 end,
1875    {"** State machine ~tp terminating~n" ++
1876         case Q of
1877             [] -> "";
1878             _ -> "** Last event = ~tp~n"
1879         end ++
1880         "** When server state  = ~tp~n" ++
1881         "** Reason for termination = ~w:~tp~n" ++
1882         "** Callback mode = ~p~n" ++
1883         case Q of
1884             [_,_|_] -> "** Queued = ~tp~n";
1885             _ -> ""
1886         end ++
1887         case P of
1888             [] -> "";
1889             _ -> "** Postponed = ~tp~n"
1890         end ++
1891         case FixedStacktrace of
1892             [] -> "";
1893             _ -> "** Stacktrace =~n**  ~tp~n"
1894         end,
1895     [Name |
1896      case Q of
1897          [] -> [];
1898          [Event|_] -> [Event]
1899      end] ++
1900         [LimitedFmtData,
1901          Class,LimitedFixedReason,
1902          CBMode] ++
1903         case Q of
1904             [_|[_|_] = Events] -> [Events];
1905             _ -> []
1906         end ++
1907         case P of
1908             [] -> [];
1909             _ -> [LimitedP]
1910         end ++
1911         case FixedStacktrace of
1912             [] -> [];
1913             _ -> [FixedStacktrace]
1914         end}.
1915
1916%% Call Module:format_status/2 or return a default value
1917format_status(
1918  Opt, PDict,
1919  #state{module = Module, state = State, data = Data}) ->
1920    case erlang:function_exported(Module, format_status, 2) of
1921	true ->
1922	    try Module:format_status(Opt, [PDict,State,Data])
1923	    catch
1924		Result -> Result;
1925		_:_ ->
1926		    format_status_default(
1927		      Opt, State,
1928		      atom_to_list(Module) ++ ":format_status/2 crashed")
1929	    end;
1930	false ->
1931	    format_status_default(Opt, State, Data)
1932    end.
1933
1934%% The default Module:format_status/2
1935format_status_default(Opt, State, Data) ->
1936    StateData = {State,Data},
1937    case Opt of
1938	terminate ->
1939	    StateData;
1940	_ ->
1941	    [{data,[{"State",StateData}]}]
1942    end.
1943
1944-compile({inline, [listify/1]}).
1945listify(Item) when is_list(Item) ->
1946    Item;
1947listify(Item) ->
1948    [Item].
1949
1950
1951-define(cancel_timer(TimerRef),
1952    case erlang:cancel_timer(TimerRef) of
1953        false ->
1954            %% No timer found and we have not seen the timeout message
1955            receive
1956                {timeout,(TimerRef),_} ->
1957                    ok
1958            end;
1959        _ ->
1960            %% Timer was running
1961            ok
1962    end).
1963
1964-compile({inline, [cancel_timer/1]}).
1965cancel_timer(TimerRef) ->
1966    ?cancel_timer(TimerRef).
1967
1968%% Cancel timer if running, otherwise no op
1969%%
1970%% Remove the timer from Timers.
1971-compile({inline, [cancel_timer_by_type/2]}).
1972cancel_timer_by_type(TimerType, {TimerRefs,TimerTypes} = Timers) ->
1973    case TimerTypes of
1974	#{TimerType := TimerRef} ->
1975            ?cancel_timer(TimerRef),
1976	    {maps:remove(TimerRef, TimerRefs),
1977             maps:remove(TimerType, TimerTypes)};
1978	#{} ->
1979	    Timers
1980    end.
1981