1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 1996-2018. 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_fsm).
21
22%%%-----------------------------------------------------------------
23%%%
24%%% This state machine is somewhat more pure than state_lib.  It is
25%%% still based on State dispatching (one function per state), but
26%%% allows a function handle_event to take care of events in all states.
27%%% It's not that pure anymore :(  We also allow synchronized event sending.
28%%%
29%%% If the Parent process terminates the Module:terminate/2
30%%% function is called.
31%%%
32%%% The user module should export:
33%%%
34%%%   init(Args)
35%%%     ==> {ok, StateName, StateData}
36%%%         {ok, StateName, StateData, Timeout}
37%%%         ignore
38%%%         {stop, Reason}
39%%%
40%%%   StateName(Msg, StateData)
41%%%
42%%%    ==> {next_state, NewStateName, NewStateData}
43%%%        {next_state, NewStateName, NewStateData, Timeout}
44%%%        {stop, Reason, NewStateData}
45%%%              Reason = normal | shutdown | Term terminate(State) is called
46%%%
47%%%   StateName(Msg, From, StateData)
48%%%
49%%%    ==> {next_state, NewStateName, NewStateData}
50%%%        {next_state, NewStateName, NewStateData, Timeout}
51%%%        {reply, Reply, NewStateName, NewStateData}
52%%%        {reply, Reply, NewStateName, NewStateData, Timeout}
53%%%        {stop, Reason, NewStateData}
54%%%              Reason = normal | shutdown | Term terminate(State) is called
55%%%
56%%%   handle_event(Msg, StateName, StateData)
57%%%
58%%%    ==> {next_state, NewStateName, NewStateData}
59%%%        {next_state, NewStateName, NewStateData, Timeout}
60%%%        {stop, Reason, Reply, NewStateData}
61%%%        {stop, Reason, NewStateData}
62%%%              Reason = normal | shutdown | Term terminate(State) is called
63%%%
64%%%   handle_sync_event(Msg, From, StateName, StateData)
65%%%
66%%%    ==> {next_state, NewStateName, NewStateData}
67%%%        {next_state, NewStateName, NewStateData, Timeout}
68%%%        {reply, Reply, NewStateName, NewStateData}
69%%%        {reply, Reply, NewStateName, NewStateData, Timeout}
70%%%        {stop, Reason, Reply, NewStateData}
71%%%        {stop, Reason, NewStateData}
72%%%              Reason = normal | shutdown | Term terminate(State) is called
73%%%
74%%%   handle_info(Info, StateName) (e.g. {'EXIT', P, R}, {nodedown, N}, ...
75%%%
76%%%    ==> {next_state, NewStateName, NewStateData}
77%%%        {next_state, NewStateName, NewStateData, Timeout}
78%%%        {stop, Reason, NewStateData}
79%%%              Reason = normal | shutdown | Term terminate(State) is called
80%%%
81%%%   terminate(Reason, StateName, StateData) Let the user module clean up
82%%%        always called when server terminates
83%%%
84%%%    ==> the return value is ignored
85%%%
86%%%
87%%% The work flow (of the fsm) can be described as follows:
88%%%
89%%%   User module                           fsm
90%%%   -----------                          -------
91%%%     start              ----->             start
92%%%     init               <-----              .
93%%%
94%%%                                           loop
95%%%     StateName          <-----              .
96%%%
97%%%     handle_event       <-----              .
98%%%
99%%%     handle__sunc_event <-----              .
100%%%
101%%%     handle_info        <-----              .
102%%%
103%%%     terminate          <-----              .
104%%%
105%%%
106%%% ---------------------------------------------------
107
108-include("logger.hrl").
109
110-export([start/3, start/4,
111	 start_link/3, start_link/4,
112	 stop/1, stop/3,
113	 send_event/2, sync_send_event/2, sync_send_event/3,
114	 send_all_state_event/2,
115	 sync_send_all_state_event/2, sync_send_all_state_event/3,
116	 reply/2,
117	 start_timer/2,send_event_after/2,cancel_timer/1,
118	 enter_loop/4, enter_loop/5, enter_loop/6, wake_hib/7]).
119
120%% Internal exports
121-export([init_it/6,
122	 system_continue/3,
123	 system_terminate/4,
124	 system_code_change/4,
125	 system_get_state/1,
126	 system_replace_state/2,
127	 format_status/2]).
128
129%% logger callback
130-export([format_log/1]).
131
132-deprecated({start, 3, eventually}).
133-deprecated({start, 4, eventually}).
134-deprecated({start_link, 3, eventually}).
135-deprecated({start_link, 4, eventually}).
136-deprecated({stop, 1, eventually}).
137-deprecated({stop, 3, eventually}).
138-deprecated({send_event, 2, eventually}).
139-deprecated({sync_send_event, 2, eventually}).
140-deprecated({sync_send_event, 3, eventually}).
141-deprecated({send_all_state_event, 2, eventually}).
142-deprecated({sync_send_all_state_event, 2, eventually}).
143-deprecated({sync_send_all_state_event, 3, eventually}).
144-deprecated({reply, 2, eventually}).
145-deprecated({start_timer, 2, eventually}).
146-deprecated({send_event_after, 2, eventually}).
147-deprecated({cancel_timer, 1, eventually}).
148-deprecated({enter_loop, 4, eventually}).
149-deprecated({enter_loop, 5, eventually}).
150-deprecated({enter_loop, 6, eventually}).
151
152%%% ---------------------------------------------------
153%%% Interface functions.
154%%% ---------------------------------------------------
155
156-callback init(Args :: term()) ->
157    {ok, StateName :: atom(), StateData :: term()} |
158    {ok, StateName :: atom(), StateData :: term(), timeout() | hibernate} |
159    {stop, Reason :: term()} | ignore.
160-callback handle_event(Event :: term(), StateName :: atom(),
161                       StateData :: term()) ->
162    {next_state, NextStateName :: atom(), NewStateData :: term()} |
163    {next_state, NextStateName :: atom(), NewStateData :: term(),
164     timeout() | hibernate} |
165    {stop, Reason :: term(), NewStateData :: term()}.
166-callback handle_sync_event(Event :: term(), From :: {pid(), Tag :: term()},
167                            StateName :: atom(), StateData :: term()) ->
168    {reply, Reply :: term(), NextStateName :: atom(), NewStateData :: term()} |
169    {reply, Reply :: term(), NextStateName :: atom(), NewStateData :: term(),
170     timeout() | hibernate} |
171    {next_state, NextStateName :: atom(), NewStateData :: term()} |
172    {next_state, NextStateName :: atom(), NewStateData :: term(),
173     timeout() | hibernate} |
174    {stop, Reason :: term(), Reply :: term(), NewStateData :: term()} |
175    {stop, Reason :: term(), NewStateData :: term()}.
176-callback handle_info(Info :: term(), StateName :: atom(),
177                      StateData :: term()) ->
178    {next_state, NextStateName :: atom(), NewStateData :: term()} |
179    {next_state, NextStateName :: atom(), NewStateData :: term(),
180     timeout() | hibernate} |
181    {stop, Reason :: normal | term(), NewStateData :: term()}.
182-callback terminate(Reason :: normal | shutdown | {shutdown, term()}
183		    | term(), StateName :: atom(), StateData :: term()) ->
184    term().
185-callback code_change(OldVsn :: term() | {down, term()}, StateName :: atom(),
186		      StateData :: term(), Extra :: term()) ->
187    {ok, NextStateName :: atom(), NewStateData :: term()}.
188-callback format_status(Opt, StatusData) -> Status when
189      Opt :: 'normal' | 'terminate',
190      StatusData :: [PDict | State],
191      PDict :: [{Key :: term(), Value :: term()}],
192      State :: term(),
193      Status :: term().
194
195-optional_callbacks(
196    [handle_info/3, terminate/3, code_change/4, format_status/2]).
197
198%%% ---------------------------------------------------
199%%% Starts a generic state machine.
200%%% start(Mod, Args, Options)
201%%% start(Name, Mod, Args, Options)
202%%% start_link(Mod, Args, Options)
203%%% start_link(Name, Mod, Args, Options) where:
204%%%    Name ::= {local, atom()} | {global, term()} | {via, atom(), term()}
205%%%    Mod  ::= atom(), callback module implementing the 'real' fsm
206%%%    Args ::= term(), init arguments (to Mod:init/1)
207%%%    Options ::= [{debug, [Flag]}]
208%%%      Flag ::= trace | log | {logfile, File} | statistics | debug
209%%%          (debug == log && statistics)
210%%% Returns: {ok, Pid} |
211%%%          {error, {already_started, Pid}} |
212%%%          {error, Reason}
213%%% ---------------------------------------------------
214start(Mod, Args, Options) ->
215    gen:start(?MODULE, nolink, Mod, Args, Options).
216
217start(Name, Mod, Args, Options) ->
218    gen:start(?MODULE, nolink, Name, Mod, Args, Options).
219
220start_link(Mod, Args, Options) ->
221    gen:start(?MODULE, link, Mod, Args, Options).
222
223start_link(Name, Mod, Args, Options) ->
224    gen:start(?MODULE, link, Name, Mod, Args, Options).
225
226stop(Name) ->
227    gen:stop(Name).
228
229stop(Name, Reason, Timeout) ->
230    gen:stop(Name, Reason, Timeout).
231
232send_event({global, Name}, Event) ->
233    catch global:send(Name, {'$gen_event', Event}),
234    ok;
235send_event({via, Mod, Name}, Event) ->
236    catch Mod:send(Name, {'$gen_event', Event}),
237    ok;
238send_event(Name, Event) ->
239    Name ! {'$gen_event', Event},
240    ok.
241
242sync_send_event(Name, Event) ->
243    case catch gen:call(Name, '$gen_sync_event', Event) of
244	{ok,Res} ->
245	    Res;
246	{'EXIT',Reason} ->
247	    exit({Reason, {?MODULE, sync_send_event, [Name, Event]}})
248    end.
249
250sync_send_event(Name, Event, Timeout) ->
251    case catch gen:call(Name, '$gen_sync_event', Event, Timeout) of
252	{ok,Res} ->
253	    Res;
254	{'EXIT',Reason} ->
255	    exit({Reason, {?MODULE, sync_send_event, [Name, Event, Timeout]}})
256    end.
257
258send_all_state_event({global, Name}, Event) ->
259    catch global:send(Name, {'$gen_all_state_event', Event}),
260    ok;
261send_all_state_event({via, Mod, Name}, Event) ->
262    catch Mod:send(Name, {'$gen_all_state_event', Event}),
263    ok;
264send_all_state_event(Name, Event) ->
265    Name ! {'$gen_all_state_event', Event},
266    ok.
267
268sync_send_all_state_event(Name, Event) ->
269    case catch gen:call(Name, '$gen_sync_all_state_event', Event) of
270	{ok,Res} ->
271	    Res;
272	{'EXIT',Reason} ->
273	    exit({Reason, {?MODULE, sync_send_all_state_event, [Name, Event]}})
274    end.
275
276sync_send_all_state_event(Name, Event, Timeout) ->
277    case catch gen:call(Name, '$gen_sync_all_state_event', Event, Timeout) of
278	{ok,Res} ->
279	    Res;
280	{'EXIT',Reason} ->
281	    exit({Reason, {?MODULE, sync_send_all_state_event,
282			   [Name, Event, Timeout]}})
283    end.
284
285%% Designed to be only callable within one of the callbacks
286%% hence using the self() of this instance of the process.
287%% This is to ensure that timers don't go astray in global
288%% e.g. when straddling a failover, or turn up in a restarted
289%% instance of the process.
290
291%% Returns Ref, sends event {timeout,Ref,Msg} after Time
292%% to the (then) current state.
293start_timer(Time, Msg) ->
294    erlang:start_timer(Time, self(), {'$gen_timer', Msg}).
295
296%% Returns Ref, sends Event after Time to the (then) current state.
297send_event_after(Time, Event) ->
298    erlang:start_timer(Time, self(), {'$gen_event', Event}).
299
300%% Returns the remaining time for the timer if Ref referred to
301%% an active timer/send_event_after, false otherwise.
302cancel_timer(Ref) ->
303    case erlang:cancel_timer(Ref) of
304	false ->
305	    receive {timeout, Ref, _} -> 0
306	    after 0 -> false
307	    end;
308	RemainingTime ->
309	    RemainingTime
310    end.
311
312%% enter_loop/4,5,6
313%% Makes an existing process into a gen_fsm.
314%% The calling process will enter the gen_fsm receive loop and become a
315%% gen_fsm process.
316%% The process *must* have been started using one of the start functions
317%% in proc_lib, see proc_lib(3).
318%% The user is responsible for any initialization of the process,
319%% including registering a name for it.
320enter_loop(Mod, Options, StateName, StateData) ->
321    enter_loop(Mod, Options, StateName, StateData, self(), infinity).
322
323enter_loop(Mod, Options, StateName, StateData, {Scope,_} = ServerName)
324  when Scope == local; Scope == global ->
325    enter_loop(Mod, Options, StateName, StateData, ServerName,infinity);
326enter_loop(Mod, Options, StateName, StateData, {via,_,_} = ServerName) ->
327    enter_loop(Mod, Options, StateName, StateData, ServerName,infinity);
328enter_loop(Mod, Options, StateName, StateData, Timeout) ->
329    enter_loop(Mod, Options, StateName, StateData, self(), Timeout).
330
331enter_loop(Mod, Options, StateName, StateData, ServerName, Timeout) ->
332    Name = gen:get_proc_name(ServerName),
333    Parent = gen:get_parent(),
334    Debug = gen:debug_options(Name, Options),
335	HibernateAfterTimeout = gen:hibernate_after(Options),
336    loop(Parent, Name, StateName, StateData, Mod, Timeout, HibernateAfterTimeout, Debug).
337
338%%% ---------------------------------------------------
339%%% Initiate the new process.
340%%% Register the name using the Rfunc function
341%%% Calls the Mod:init/Args function.
342%%% Finally an acknowledge is sent to Parent and the main
343%%% loop is entered.
344%%% ---------------------------------------------------
345init_it(Starter, self, Name, Mod, Args, Options) ->
346    init_it(Starter, self(), Name, Mod, Args, Options);
347init_it(Starter, Parent, Name0, Mod, Args, Options) ->
348    Name = gen:name(Name0),
349    Debug = gen:debug_options(Name, Options),
350	HibernateAfterTimeout = gen:hibernate_after(Options),
351	case catch Mod:init(Args) of
352	{ok, StateName, StateData} ->
353	    proc_lib:init_ack(Starter, {ok, self()}),
354	    loop(Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, Debug);
355	{ok, StateName, StateData, Timeout} ->
356	    proc_lib:init_ack(Starter, {ok, self()}),
357	    loop(Parent, Name, StateName, StateData, Mod, Timeout, HibernateAfterTimeout, Debug);
358	{stop, Reason} ->
359	    gen:unregister_name(Name0),
360	    proc_lib:init_ack(Starter, {error, Reason}),
361	    exit(Reason);
362	ignore ->
363	    gen:unregister_name(Name0),
364	    proc_lib:init_ack(Starter, ignore),
365	    exit(normal);
366	{'EXIT', Reason} ->
367	    gen:unregister_name(Name0),
368	    proc_lib:init_ack(Starter, {error, Reason}),
369	    exit(Reason);
370	Else ->
371	    Error = {bad_return_value, Else},
372	    proc_lib:init_ack(Starter, {error, Error}),
373	    exit(Error)
374    end.
375
376%%-----------------------------------------------------------------
377%% The MAIN loop
378%%-----------------------------------------------------------------
379loop(Parent, Name, StateName, StateData, Mod, hibernate, HibernateAfterTimeout, Debug) ->
380    proc_lib:hibernate(?MODULE,wake_hib,
381		       [Parent, Name, StateName, StateData, Mod, HibernateAfterTimeout,
382			Debug]);
383
384loop(Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, Debug) ->
385	receive
386		Msg ->
387			decode_msg(Msg,Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, Debug, false)
388	after HibernateAfterTimeout ->
389		loop(Parent, Name, StateName, StateData, Mod, hibernate, HibernateAfterTimeout, Debug)
390	end;
391
392loop(Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout, Debug) ->
393    Msg = receive
394	      Input ->
395		    Input
396	  after Time ->
397		  {'$gen_event', timeout}
398	  end,
399    decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout, Debug, false).
400
401wake_hib(Parent, Name, StateName, StateData, Mod, HibernateAfterTimeout, Debug) ->
402    Msg = receive
403	      Input ->
404		  Input
405	  end,
406    decode_msg(Msg, Parent, Name, StateName, StateData, Mod, hibernate, HibernateAfterTimeout, Debug, true).
407
408decode_msg(Msg,Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout, Debug, Hib) ->
409    case Msg of
410        {system, From, Req} ->
411	    sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
412				  [Name, StateName, StateData, Mod, Time, HibernateAfterTimeout], Hib);
413	{'EXIT', Parent, Reason} ->
414	    terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug);
415	_Msg when Debug =:= [] ->
416	    handle_msg(Msg, Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout);
417	_Msg ->
418	    Debug1 = sys:handle_debug(Debug, fun print_event/3,
419				      {Name, StateName}, {in, Msg}),
420	    handle_msg(Msg, Parent, Name, StateName, StateData,
421		       Mod, Time, HibernateAfterTimeout, Debug1)
422    end.
423
424%%-----------------------------------------------------------------
425%% Callback functions for system messages handling.
426%%-----------------------------------------------------------------
427system_continue(Parent, Debug, [Name, StateName, StateData, Mod, Time, HibernateAfterTimeout]) ->
428    loop(Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout, Debug).
429
430-spec system_terminate(term(), _, _, [term(),...]) -> no_return().
431
432system_terminate(Reason, _Parent, Debug,
433		 [Name, StateName, StateData, Mod, _Time, _HibernateAfterTimeout]) ->
434    terminate(Reason, Name, [], Mod, StateName, StateData, Debug).
435
436system_code_change([Name, StateName, StateData, Mod, Time, HibernateAfterTimeout],
437		   _Module, OldVsn, Extra) ->
438    case catch Mod:code_change(OldVsn, StateName, StateData, Extra) of
439	{ok, NewStateName, NewStateData} ->
440	    {ok, [Name, NewStateName, NewStateData, Mod, Time, HibernateAfterTimeout]};
441	Else -> Else
442    end.
443
444system_get_state([_Name, StateName, StateData, _Mod, _Time, _HibernateAfterTimeout]) ->
445    {ok, {StateName, StateData}}.
446
447system_replace_state(StateFun, [Name, StateName, StateData, Mod, Time, HibernateAfterTimeout]) ->
448    Result = {NStateName, NStateData} = StateFun({StateName, StateData}),
449    {ok, Result, [Name, NStateName, NStateData, Mod, Time, HibernateAfterTimeout]}.
450
451%%-----------------------------------------------------------------
452%% Format debug messages.  Print them as the call-back module sees
453%% them, not as the real erlang messages.  Use trace for that.
454%%-----------------------------------------------------------------
455print_event(Dev, {in, Msg}, {Name, StateName}) ->
456    case Msg of
457	{'$gen_event', Event} ->
458	    io:format(Dev, "*DBG* ~tp got event ~tp in state ~tw~n",
459		      [Name, Event, StateName]);
460	{'$gen_all_state_event', Event} ->
461	    io:format(Dev,
462		      "*DBG* ~tp got all_state_event ~tp in state ~tw~n",
463		      [Name, Event, StateName]);
464	{timeout, Ref, {'$gen_timer', Message}} ->
465	    io:format(Dev,
466		      "*DBG* ~tp got timer ~tp in state ~tw~n",
467		      [Name, {timeout, Ref, Message}, StateName]);
468	{timeout, _Ref, {'$gen_event', Event}} ->
469	    io:format(Dev,
470		      "*DBG* ~tp got timer ~tp in state ~tw~n",
471		      [Name, Event, StateName]);
472	_ ->
473	    io:format(Dev, "*DBG* ~tp got ~tp in state ~tw~n",
474		      [Name, Msg, StateName])
475    end;
476print_event(Dev, {out, Msg, To, StateName}, Name) ->
477    io:format(Dev, "*DBG* ~tp sent ~tp to ~tw~n"
478	           "      and switched to state ~tw~n",
479	      [Name, Msg, To, StateName]);
480print_event(Dev, return, {Name, StateName}) ->
481    io:format(Dev, "*DBG* ~tp switched to state ~tw~n",
482	      [Name, StateName]).
483
484handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, HibernateAfterTimeout) -> %No debug here
485    From = from(Msg),
486    case catch dispatch(Msg, Mod, StateName, StateData) of
487	{next_state, NStateName, NStateData} ->
488	    loop(Parent, Name, NStateName, NStateData, Mod, infinity, HibernateAfterTimeout, []);
489	{next_state, NStateName, NStateData, Time1} ->
490	    loop(Parent, Name, NStateName, NStateData, Mod, Time1, HibernateAfterTimeout, []);
491        {reply, Reply, NStateName, NStateData} when From =/= undefined ->
492	    reply(From, Reply),
493	    loop(Parent, Name, NStateName, NStateData, Mod, infinity, HibernateAfterTimeout, []);
494        {reply, Reply, NStateName, NStateData, Time1} when From =/= undefined ->
495	    reply(From, Reply),
496	    loop(Parent, Name, NStateName, NStateData, Mod, Time1, HibernateAfterTimeout, []);
497	{stop, Reason, NStateData} ->
498	    terminate(Reason, Name, Msg, Mod, StateName, NStateData, []);
499	{stop, Reason, Reply, NStateData} when From =/= undefined ->
500	    {'EXIT', R} = (catch terminate(Reason, Name, Msg, Mod,
501					   StateName, NStateData, [])),
502	    reply(From, Reply),
503	    exit(R);
504        {'EXIT', {undef, [{Mod, handle_info, [_,_,_], _}|_]}} ->
505            ?LOG_WARNING(#{label=>{gen_fsm,no_handle_info},
506                           module=>Mod,
507                           message=>Msg},
508                         #{domain=>[otp],
509                           report_cb=>fun gen_fsm:format_log/1,
510                           error_logger=>#{tag=>warning_msg}}),
511            loop(Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, []);
512	{'EXIT', What} ->
513	    terminate(What, Name, Msg, Mod, StateName, StateData, []);
514	Reply ->
515	    terminate({bad_return_value, Reply},
516		      Name, Msg, Mod, StateName, StateData, [])
517    end.
518
519handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, HibernateAfterTimeout, Debug) ->
520    From = from(Msg),
521    case catch dispatch(Msg, Mod, StateName, StateData) of
522	{next_state, NStateName, NStateData} ->
523	    Debug1 = sys:handle_debug(Debug, fun print_event/3,
524				      {Name, NStateName}, return),
525	    loop(Parent, Name, NStateName, NStateData, Mod, infinity, HibernateAfterTimeout, Debug1);
526	{next_state, NStateName, NStateData, Time1} ->
527	    Debug1 = sys:handle_debug(Debug, fun print_event/3,
528				      {Name, NStateName}, return),
529	    loop(Parent, Name, NStateName, NStateData, Mod, Time1, HibernateAfterTimeout, Debug1);
530        {reply, Reply, NStateName, NStateData} when From =/= undefined ->
531	    Debug1 = reply(Name, From, Reply, Debug, NStateName),
532	    loop(Parent, Name, NStateName, NStateData, Mod, infinity, HibernateAfterTimeout, Debug1);
533        {reply, Reply, NStateName, NStateData, Time1} when From =/= undefined ->
534	    Debug1 = reply(Name, From, Reply, Debug, NStateName),
535	    loop(Parent, Name, NStateName, NStateData, Mod, Time1, HibernateAfterTimeout, Debug1);
536	{stop, Reason, NStateData} ->
537	    terminate(Reason, Name, Msg, Mod, StateName, NStateData, Debug);
538	{stop, Reason, Reply, NStateData} when From =/= undefined ->
539	    {'EXIT', R} = (catch terminate(Reason, Name, Msg, Mod,
540					   StateName, NStateData, Debug)),
541	    _ = reply(Name, From, Reply, Debug, StateName),
542	    exit(R);
543	{'EXIT', What} ->
544	    terminate(What, Name, Msg, Mod, StateName, StateData, Debug);
545	Reply ->
546	    terminate({bad_return_value, Reply},
547		      Name, Msg, Mod, StateName, StateData, Debug)
548    end.
549
550dispatch({'$gen_event', Event}, Mod, StateName, StateData) ->
551    Mod:StateName(Event, StateData);
552dispatch({'$gen_all_state_event', Event}, Mod, StateName, StateData) ->
553    Mod:handle_event(Event, StateName, StateData);
554dispatch({'$gen_sync_event', From, Event}, Mod, StateName, StateData) ->
555    Mod:StateName(Event, From, StateData);
556dispatch({'$gen_sync_all_state_event', From, Event},
557	 Mod, StateName, StateData) ->
558    Mod:handle_sync_event(Event, From, StateName, StateData);
559dispatch({timeout, Ref, {'$gen_timer', Msg}}, Mod, StateName, StateData) ->
560    Mod:StateName({timeout, Ref, Msg}, StateData);
561dispatch({timeout, _Ref, {'$gen_event', Event}}, Mod, StateName, StateData) ->
562    Mod:StateName(Event, StateData);
563dispatch(Info, Mod, StateName, StateData) ->
564    Mod:handle_info(Info, StateName, StateData).
565
566from({'$gen_sync_event', From, _Event}) -> From;
567from({'$gen_sync_all_state_event', From, _Event}) -> From;
568from(_) -> undefined.
569
570%% Send a reply to the client.
571reply({To, Tag}, Reply) ->
572    catch To ! {Tag, Reply}.
573
574reply(Name, {To, Tag}, Reply, Debug, StateName) ->
575    reply({To, Tag}, Reply),
576    sys:handle_debug(Debug, fun print_event/3, Name,
577		     {out, Reply, To, StateName}).
578
579%%% ---------------------------------------------------
580%%% Terminate the server.
581%%% ---------------------------------------------------
582
583-spec terminate(term(), _, _, atom(), _, _, _) -> no_return().
584
585terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug) ->
586    case erlang:function_exported(Mod, terminate, 3) of
587	true ->
588	    case catch Mod:terminate(Reason, StateName, StateData) of
589		{'EXIT', R} ->
590		    FmtStateData = format_status(terminate, Mod, get(), StateData),
591		    error_info(R, Name, Msg, StateName, FmtStateData, Debug),
592		    exit(R);
593		_ ->
594		    ok
595	    end;
596	false ->
597	    ok
598    end,
599    case Reason of
600	normal ->
601	    exit(normal);
602	shutdown ->
603	    exit(shutdown);
604 	{shutdown,_}=Shutdown ->
605 	    exit(Shutdown);
606	_ ->
607	    FmtStateData1 = format_status(terminate, Mod, get(), StateData),
608	    error_info(Reason,Name,Msg,StateName,FmtStateData1,Debug),
609	    exit(Reason)
610    end.
611
612error_info(Reason, Name, Msg, StateName, StateData, Debug) ->
613    ?LOG_ERROR(#{label=>{gen_fsm,terminate},
614                 name=>Name,
615                 last_message=>Msg,
616                 state_name=>StateName,
617                 state_data=>StateData,
618                 reason=>Reason},
619               #{domain=>[otp],
620                 report_cb=>fun gen_fsm:format_log/1,
621                 error_logger=>#{tag=>error}}),
622    sys:print_log(Debug),
623    ok.
624
625format_log(#{label:={gen_fsm,terminate},
626             name:=Name,
627             last_message:=Msg,
628             state_name:=StateName,
629             state_data:=StateData,
630             reason:=Reason}) ->
631    Reason1 =
632	case Reason of
633	    {undef,[{M,F,A,L}|MFAs]} ->
634		case code:is_loaded(M) of
635		    false ->
636			{'module could not be loaded',[{M,F,A,L}|MFAs]};
637		    _ ->
638			case erlang:function_exported(M, F, length(A)) of
639			    true ->
640				Reason;
641			    false ->
642				{'function not exported',[{M,F,A,L}|MFAs]}
643			end
644		end;
645	    _ ->
646		Reason
647	end,
648    {"** State machine ~tp terminating \n" ++
649         get_msg_str(Msg) ++
650     "** When State == ~tp~n"
651     "**      Data  == ~tp~n"
652     "** Reason for termination = ~n** ~tp~n",
653     [Name, get_msg(Msg), StateName, StateData, Reason1]};
654format_log(#{label:={gen_fsm,no_handle_info},
655             module:=Mod,
656             message:=Msg}) ->
657    {"** Undefined handle_info in ~p~n"
658     "** Unhandled message: ~tp~n",
659     [Mod, Msg]}.
660
661get_msg_str({'$gen_event', _Event}) ->
662    "** Last event in was ~tp~n";
663get_msg_str({'$gen_sync_event', _Event}) ->
664    "** Last sync event in was ~tp~n";
665get_msg_str({'$gen_all_state_event', _Event}) ->
666    "** Last event in was ~tp (for all states)~n";
667get_msg_str({'$gen_sync_all_state_event', _Event}) ->
668    "** Last sync event in was ~tp (for all states)~n";
669get_msg_str({timeout, _Ref, {'$gen_timer', _Msg}}) ->
670    "** Last timer event in was ~tp~n";
671get_msg_str({timeout, _Ref, {'$gen_event', _Msg}}) ->
672    "** Last timer event in was ~tp~n";
673get_msg_str(_Msg) ->
674    "** Last message in was ~tp~n".
675
676get_msg({'$gen_event', Event}) -> Event;
677get_msg({'$gen_sync_event', Event}) -> Event;
678get_msg({'$gen_all_state_event', Event}) -> Event;
679get_msg({'$gen_sync_all_state_event', Event}) -> Event;
680get_msg({timeout, Ref, {'$gen_timer', Msg}}) -> {timeout, Ref, Msg};
681get_msg({timeout, _Ref, {'$gen_event', Event}}) -> Event;
682get_msg(Msg) -> Msg.
683
684%%-----------------------------------------------------------------
685%% Status information
686%%-----------------------------------------------------------------
687format_status(Opt, StatusData) ->
688    [PDict, SysState, Parent, Debug, [Name, StateName, StateData, Mod, _Time, _HibernateAfterTimeout]] =
689	StatusData,
690    Header = gen:format_status_header("Status for state machine",
691                                      Name),
692    Log = sys:get_debug(log, Debug, []),
693    Specfic = format_status(Opt, Mod, PDict, StateData),
694    Specfic = case format_status(Opt, Mod, PDict, StateData) of
695		  S when is_list(S) -> S;
696		  S -> [S]
697	      end,
698    [{header, Header},
699     {data, [{"Status", SysState},
700	     {"Parent", Parent},
701	     {"Logged events", Log},
702	     {"StateName", StateName}]} |
703     Specfic].
704
705format_status(Opt, Mod, PDict, State) ->
706    DefStatus = case Opt of
707		    terminate -> State;
708		    _ -> [{data, [{"StateData", State}]}]
709		end,
710    case erlang:function_exported(Mod, format_status, 2) of
711	true ->
712	    case catch Mod:format_status(Opt, [PDict, State]) of
713		{'EXIT', _} -> DefStatus;
714		Else -> Else
715	    end;
716	_ ->
717	    DefStatus
718    end.
719