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