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