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(
415              Reason, Name, undefined, Msg, Mod, StateName, StateData, Debug);
416	_Msg when Debug =:= [] ->
417	    handle_msg(Msg, Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout);
418	_Msg ->
419	    Debug1 = sys:handle_debug(Debug, fun print_event/3,
420				      Name, {in, Msg, StateName}),
421	    handle_msg(Msg, Parent, Name, StateName, StateData,
422		       Mod, Time, HibernateAfterTimeout, Debug1)
423    end.
424
425%%-----------------------------------------------------------------
426%% Callback functions for system messages handling.
427%%-----------------------------------------------------------------
428system_continue(Parent, Debug, [Name, StateName, StateData, Mod, Time, HibernateAfterTimeout]) ->
429    loop(Parent, Name, StateName, StateData, Mod, Time, HibernateAfterTimeout, Debug).
430
431-spec system_terminate(term(), _, _, [term(),...]) -> no_return().
432
433system_terminate(Reason, _Parent, Debug,
434		 [Name, StateName, StateData, Mod, _Time, _HibernateAfterTimeout]) ->
435    terminate(Reason, Name, undefined, [], Mod, StateName, StateData, Debug).
436
437system_code_change([Name, StateName, StateData, Mod, Time, HibernateAfterTimeout],
438		   _Module, OldVsn, Extra) ->
439    case catch Mod:code_change(OldVsn, StateName, StateData, Extra) of
440	{ok, NewStateName, NewStateData} ->
441	    {ok, [Name, NewStateName, NewStateData, Mod, Time, HibernateAfterTimeout]};
442	Else -> Else
443    end.
444
445system_get_state([_Name, StateName, StateData, _Mod, _Time, _HibernateAfterTimeout]) ->
446    {ok, {StateName, StateData}}.
447
448system_replace_state(StateFun, [Name, StateName, StateData, Mod, Time, HibernateAfterTimeout]) ->
449    Result = {NStateName, NStateData} = StateFun({StateName, StateData}),
450    {ok, Result, [Name, NStateName, NStateData, Mod, Time, HibernateAfterTimeout]}.
451
452%%-----------------------------------------------------------------
453%% Format debug messages.  Print them as the call-back module sees
454%% them, not as the real erlang messages.  Use trace for that.
455%%-----------------------------------------------------------------
456print_event(Dev, {in, Msg, StateName}, Name) ->
457    case Msg of
458	{'$gen_event', Event} ->
459	    io:format(Dev, "*DBG* ~tp got event ~tp in state ~tw~n",
460		      [Name, Event, StateName]);
461	{'$gen_all_state_event', Event} ->
462	    io:format(Dev,
463		      "*DBG* ~tp got all_state_event ~tp in state ~tw~n",
464		      [Name, Event, StateName]);
465	{'$gen_sync_event', {From,_Tag}, Event} ->
466	    io:format(Dev,
467                      "*DBG* ~tp got sync_event ~tp "
468                      "from ~tw in state ~tw~n",
469		      [Name, Event, From, StateName]);
470	{'$gen_sync_all_state_event', {From,_Tag}, Event} ->
471	    io:format(Dev,
472		      "*DBG* ~tp got sync_all_state_event ~tp "
473                      "from ~tw in state ~tw~n",
474		      [Name, Event, From, StateName]);
475	{timeout, Ref, {'$gen_timer', Message}} ->
476	    io:format(Dev,
477		      "*DBG* ~tp got timer ~tp in state ~tw~n",
478		      [Name, {timeout, Ref, Message}, StateName]);
479	{timeout, _Ref, {'$gen_event', Event}} ->
480	    io:format(Dev,
481		      "*DBG* ~tp got timer ~tp in state ~tw~n",
482		      [Name, Event, StateName]);
483	_ ->
484	    io:format(Dev, "*DBG* ~tp got ~tp in state ~tw~n",
485		      [Name, Msg, StateName])
486    end;
487print_event(Dev, {out, Msg, {To,_Tag}, StateName}, Name) ->
488    io:format(Dev, "*DBG* ~tp sent ~tp to ~tw~n"
489	           "      and switched to state ~tw~n",
490	      [Name, Msg, To, StateName]);
491print_event(Dev, {noreply, StateName}, Name) ->
492    io:format(Dev, "*DBG* ~tp switched to state ~tw~n",
493	      [Name, StateName]).
494
495handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, HibernateAfterTimeout) -> %No debug here
496    From = from(Msg),
497    case catch dispatch(Msg, Mod, StateName, StateData) of
498	{next_state, NStateName, NStateData} ->
499	    loop(Parent, Name, NStateName, NStateData, Mod, infinity, HibernateAfterTimeout, []);
500	{next_state, NStateName, NStateData, Time1} ->
501	    loop(Parent, Name, NStateName, NStateData, Mod, Time1, HibernateAfterTimeout, []);
502        {reply, Reply, NStateName, NStateData} when From =/= undefined ->
503	    reply(From, Reply),
504	    loop(Parent, Name, NStateName, NStateData, Mod, infinity, HibernateAfterTimeout, []);
505        {reply, Reply, NStateName, NStateData, Time1} when From =/= undefined ->
506	    reply(From, Reply),
507	    loop(Parent, Name, NStateName, NStateData, Mod, Time1, HibernateAfterTimeout, []);
508	{stop, Reason, NStateData} ->
509	    terminate(Reason, Name, From, Msg, Mod, StateName, NStateData, []);
510	{stop, Reason, Reply, NStateData} when From =/= undefined ->
511	    {'EXIT', R} = (catch terminate(Reason, Name, From, Msg, Mod,
512					   StateName, NStateData, [])),
513	    reply(From, Reply),
514	    exit(R);
515        {'EXIT', {undef, [{Mod, handle_info, [_,_,_], _}|_]}} ->
516            ?LOG_WARNING(#{label=>{gen_fsm,no_handle_info},
517                           module=>Mod,
518                           message=>Msg},
519                         #{domain=>[otp],
520                           report_cb=>fun gen_fsm:format_log/1,
521                           error_logger=>#{tag=>warning_msg}}),
522            loop(Parent, Name, StateName, StateData, Mod, infinity, HibernateAfterTimeout, []);
523	{'EXIT', What} ->
524	    terminate(What, Name, From, Msg, Mod, StateName, StateData, []);
525	Reply ->
526	    terminate({bad_return_value, Reply},
527		      Name, From, Msg, Mod, StateName, StateData, [])
528    end.
529
530handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, HibernateAfterTimeout, Debug) ->
531    From = from(Msg),
532    case catch dispatch(Msg, Mod, StateName, StateData) of
533	{next_state, NStateName, NStateData} ->
534	    Debug1 = sys:handle_debug(Debug, fun print_event/3,
535				      Name, {noreply, NStateName}),
536	    loop(Parent, Name, NStateName, NStateData, Mod, infinity, HibernateAfterTimeout, Debug1);
537	{next_state, NStateName, NStateData, Time1} ->
538	    Debug1 = sys:handle_debug(Debug, fun print_event/3,
539				      Name, {noreply, NStateName}),
540	    loop(Parent, Name, NStateName, NStateData, Mod, Time1, HibernateAfterTimeout, Debug1);
541        {reply, Reply, NStateName, NStateData} when From =/= undefined ->
542	    Debug1 = reply(Name, From, Reply, Debug, NStateName),
543	    loop(Parent, Name, NStateName, NStateData, Mod, infinity, HibernateAfterTimeout, Debug1);
544        {reply, Reply, NStateName, NStateData, Time1} when From =/= undefined ->
545	    Debug1 = reply(Name, From, Reply, Debug, NStateName),
546	    loop(Parent, Name, NStateName, NStateData, Mod, Time1, HibernateAfterTimeout, Debug1);
547	{stop, Reason, NStateData} ->
548	    terminate(
549              Reason, Name, From, Msg, Mod, StateName, NStateData, Debug);
550	{stop, Reason, Reply, NStateData} when From =/= undefined ->
551	    {'EXIT', R} = (catch terminate(Reason, Name, From, Msg, Mod,
552					   StateName, NStateData, Debug)),
553	    _ = reply(Name, From, Reply, Debug, StateName),
554	    exit(R);
555	{'EXIT', What} ->
556	    terminate(What, Name, From, Msg, Mod, StateName, StateData, Debug);
557	Reply ->
558	    terminate({bad_return_value, Reply},
559		      Name, From, Msg, Mod, StateName, StateData, Debug)
560    end.
561
562dispatch({'$gen_event', Event}, Mod, StateName, StateData) ->
563    Mod:StateName(Event, StateData);
564dispatch({'$gen_all_state_event', Event}, Mod, StateName, StateData) ->
565    Mod:handle_event(Event, StateName, StateData);
566dispatch({'$gen_sync_event', From, Event}, Mod, StateName, StateData) ->
567    Mod:StateName(Event, From, StateData);
568dispatch({'$gen_sync_all_state_event', From, Event},
569	 Mod, StateName, StateData) ->
570    Mod:handle_sync_event(Event, From, StateName, StateData);
571dispatch({timeout, Ref, {'$gen_timer', Msg}}, Mod, StateName, StateData) ->
572    Mod:StateName({timeout, Ref, Msg}, StateData);
573dispatch({timeout, _Ref, {'$gen_event', Event}}, Mod, StateName, StateData) ->
574    Mod:StateName(Event, StateData);
575dispatch(Info, Mod, StateName, StateData) ->
576    Mod:handle_info(Info, StateName, StateData).
577
578from({'$gen_sync_event', From, _Event}) -> From;
579from({'$gen_sync_all_state_event', From, _Event}) -> From;
580from(_) -> undefined.
581
582%% Send a reply to the client.
583reply({To, Tag}, Reply) ->
584    catch To ! {Tag, Reply}.
585
586reply(Name, From, Reply, Debug, StateName) ->
587    reply(From, Reply),
588    sys:handle_debug(Debug, fun print_event/3, Name,
589		     {out, Reply, From, StateName}).
590
591%%% ---------------------------------------------------
592%%% Terminate the server.
593%%% ---------------------------------------------------
594
595-spec terminate(term(), _, _, _, atom(), _, _, _) -> no_return().
596
597terminate(Reason, Name, From, Msg, Mod, StateName, StateData, Debug) ->
598    case erlang:function_exported(Mod, terminate, 3) of
599	true ->
600	    case catch Mod:terminate(Reason, StateName, StateData) of
601		{'EXIT', R} ->
602		    FmtStateData = format_status(terminate, Mod, get(), StateData),
603		    error_info(
604                      R, Name, From, Msg, StateName, FmtStateData, Debug),
605		    exit(R);
606		_ ->
607		    ok
608	    end;
609	false ->
610	    ok
611    end,
612    case Reason of
613	normal ->
614	    exit(normal);
615	shutdown ->
616	    exit(shutdown);
617 	{shutdown,_}=Shutdown ->
618 	    exit(Shutdown);
619	_ ->
620	    FmtStateData1 = format_status(terminate, Mod, get(), StateData),
621	    error_info(
622              Reason, Name, From, Msg, StateName, FmtStateData1, Debug),
623	    exit(Reason)
624    end.
625
626error_info(Reason, Name, From, Msg, StateName, StateData, Debug) ->
627    Log = sys:get_log(Debug),
628    ?LOG_ERROR(#{label=>{gen_fsm,terminate},
629                 name=>Name,
630                 last_message=>Msg,
631                 state_name=>StateName,
632                 state_data=>StateData,
633                 log=>Log,
634                 reason=>Reason,
635                 client_info=>client_stacktrace(From)},
636               #{domain=>[otp],
637                 report_cb=>fun gen_fsm:format_log/1,
638                 error_logger=>#{tag=>error}}),
639    ok.
640
641client_stacktrace(undefined) ->
642    undefined;
643client_stacktrace({Pid,_Tag}) ->
644    client_stacktrace(Pid);
645client_stacktrace(Pid) when is_pid(Pid), node(Pid) =:= node() ->
646    case process_info(Pid, [current_stacktrace, registered_name]) of
647        undefined ->
648            {Pid,dead};
649        [{current_stacktrace, Stacktrace}, {registered_name, []}]  ->
650            {Pid,{Pid,Stacktrace}};
651        [{current_stacktrace, Stacktrace}, {registered_name, Name}]  ->
652            {Pid,{Name,Stacktrace}}
653    end;
654client_stacktrace(Pid) when is_pid(Pid) ->
655    {Pid,remote}.
656
657
658format_log(#{label:={gen_fsm,terminate},
659             name:=Name,
660             last_message:=Msg,
661             state_name:=StateName,
662             state_data:=StateData,
663             log:=Log,
664             reason:=Reason,
665             client_info:=ClientInfo}) ->
666    Reason1 =
667	case Reason of
668	    {undef,[{M,F,A,L}|MFAs]} ->
669		case code:is_loaded(M) of
670		    false ->
671			{'module could not be loaded',[{M,F,A,L}|MFAs]};
672		    _ ->
673			case erlang:function_exported(M, F, length(A)) of
674			    true ->
675				Reason;
676			    false ->
677				{'function not exported',[{M,F,A,L}|MFAs]}
678			end
679		end;
680	    _ ->
681		Reason
682	end,
683    {ClientFmt,ClientArgs} = format_client_log(ClientInfo),
684    {"** State machine ~tp terminating \n" ++
685         get_msg_str(Msg) ++
686     "** When State == ~tp~n"
687     "**      Data  == ~tp~n"
688     "** Reason for termination ==~n** ~tp~n" ++
689         case Log of
690             [] -> [];
691             _ -> "** Log ==~n** ~tp~n"
692         end ++ ClientFmt,
693     [Name|error_logger:limit_term(get_msg(Msg))] ++
694         [StateName,
695          error_logger:limit_term(StateData),
696          error_logger:limit_term(Reason1) |
697          case Log of
698              [] -> [];
699              _ -> [[error_logger:limit_term(D) || D <- Log]]
700          end] ++ ClientArgs};
701format_log(#{label:={gen_fsm,no_handle_info},
702             module:=Mod,
703             message:=Msg}) ->
704    {"** Undefined handle_info in ~p~n"
705     "** Unhandled message: ~tp~n",
706     [Mod, error_logger:limit_term(Msg)]}.
707
708get_msg_str({'$gen_event', _Event}) ->
709    "** Last event in was ~tp~n";
710get_msg_str({'$gen_sync_event', _From, _Event}) ->
711    "** Last sync event in was ~tp from ~tw~n";
712get_msg_str({'$gen_all_state_event', _Event}) ->
713    "** Last event in was ~tp (for all states)~n";
714get_msg_str({'$gen_sync_all_state_event', _From, _Event}) ->
715    "** Last sync event in was ~tp (for all states) from ~tw~n";
716get_msg_str({timeout, _Ref, {'$gen_timer', _Msg}}) ->
717    "** Last timer event in was ~tp~n";
718get_msg_str({timeout, _Ref, {'$gen_event', _Msg}}) ->
719    "** Last timer event in was ~tp~n";
720get_msg_str(_Msg) ->
721    "** Last message in was ~tp~n".
722
723get_msg({'$gen_event', Event}) -> [Event];
724get_msg({'$gen_sync_event', {From,_Tag}, Event}) -> [Event,From];
725get_msg({'$gen_all_state_event', Event}) -> [Event];
726get_msg({'$gen_sync_all_state_event', {From,_Tag}, Event}) -> [Event,From];
727get_msg({timeout, Ref, {'$gen_timer', Msg}}) -> [{timeout, Ref, Msg}];
728get_msg({timeout, _Ref, {'$gen_event', Event}}) -> [Event];
729get_msg(Msg) -> [Msg].
730
731format_client_log(undefined) ->
732    {"", []};
733format_client_log({From,dead}) ->
734    {"** Client ~p is dead~n", [From]};
735format_client_log({From,remote}) ->
736    {"** Client ~p is remote on node ~p~n", [From, node(From)]};
737format_client_log({_From,{Name,Stacktrace}}) ->
738    {"** Client ~tp stacktrace~n"
739     "** ~tp~n",
740     [Name, error_logger:limit_term(Stacktrace)]}.
741
742%%-----------------------------------------------------------------
743%% Status information
744%%-----------------------------------------------------------------
745format_status(Opt, StatusData) ->
746    [PDict, SysState, Parent, Debug, [Name, StateName, StateData, Mod, _Time, _HibernateAfterTimeout]] =
747	StatusData,
748    Header = gen:format_status_header("Status for state machine",
749                                      Name),
750    Log = sys:get_log(Debug),
751    Specific =
752        case format_status(Opt, Mod, PDict, StateData) of
753            S when is_list(S) -> S;
754            S -> [S]
755        end,
756    [{header, Header},
757     {data, [{"Status", SysState},
758	     {"Parent", Parent},
759	     {"Logged events", Log},
760	     {"StateName", StateName}]} |
761     Specific].
762
763format_status(Opt, Mod, PDict, State) ->
764    DefStatus = case Opt of
765		    terminate -> State;
766		    _ -> [{data, [{"StateData", State}]}]
767		end,
768    case erlang:function_exported(Mod, format_status, 2) of
769	true ->
770	    case catch Mod:format_status(Opt, [PDict, State]) of
771		{'EXIT', _} -> DefStatus;
772		Else -> Else
773	    end;
774	_ ->
775	    DefStatus
776    end.
777