gen_fsm.erl 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. %% ``The contents of this file are subject to the Erlang Public License,
  2. %% Version 1.1, (the "License"); you may not use this file except in
  3. %% compliance with the License. You should have received a copy of the
  4. %% Erlang Public License along with this software. If not, it can be
  5. %% retrieved via the world wide web at http://www.erlang.org/.
  6. %%
  7. %% Software distributed under the License is distributed on an "AS IS"
  8. %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  9. %% the License for the specific language governing rights and limitations
  10. %% under the License.
  11. %%
  12. %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
  13. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
  14. %% AB. All Rights Reserved.''
  15. %%
  16. %% $Id$
  17. %%
  18. -module(gen_fsm).
  19. %%%-----------------------------------------------------------------
  20. %%%
  21. %%% This state machine is somewhat more pure than state_lib. It is
  22. %%% still based on State dispatching (one function per state), but
  23. %%% allows a function handle_event to take care of events in all states.
  24. %%% It's not that pure anymore :( We also allow synchronized event sending.
  25. %%%
  26. %%% If the Parent process terminates the Module:terminate/2
  27. %%% function is called.
  28. %%%
  29. %%% The user module should export:
  30. %%%
  31. %%% init(Args)
  32. %%% ==> {ok, StateName, StateData}
  33. %%% {ok, StateName, StateData, Timeout}
  34. %%% ignore
  35. %%% {stop, Reason}
  36. %%%
  37. %%% StateName(Msg, StateData)
  38. %%%
  39. %%% ==> {next_state, NewStateName, NewStateData}
  40. %%% {next_state, NewStateName, NewStateData, Timeout}
  41. %%% {stop, Reason, NewStateData}
  42. %%% Reason = normal | shutdown | Term terminate(State) is called
  43. %%%
  44. %%% StateName(Msg, From, StateData)
  45. %%%
  46. %%% ==> {next_state, NewStateName, NewStateData}
  47. %%% {next_state, NewStateName, NewStateData, Timeout}
  48. %%% {reply, Reply, NewStateName, NewStateData}
  49. %%% {reply, Reply, NewStateName, NewStateData, Timeout}
  50. %%% {stop, Reason, NewStateData}
  51. %%% Reason = normal | shutdown | Term terminate(State) is called
  52. %%%
  53. %%% handle_event(Msg, StateName, StateData)
  54. %%%
  55. %%% ==> {next_state, NewStateName, NewStateData}
  56. %%% {next_state, NewStateName, NewStateData, Timeout}
  57. %%% {stop, Reason, Reply, NewStateData}
  58. %%% {stop, Reason, NewStateData}
  59. %%% Reason = normal | shutdown | Term terminate(State) is called
  60. %%%
  61. %%% handle_sync_event(Msg, From, StateName, StateData)
  62. %%%
  63. %%% ==> {next_state, NewStateName, NewStateData}
  64. %%% {next_state, NewStateName, NewStateData, Timeout}
  65. %%% {reply, Reply, NewStateName, NewStateData}
  66. %%% {reply, Reply, NewStateName, NewStateData, Timeout}
  67. %%% {stop, Reason, Reply, NewStateData}
  68. %%% {stop, Reason, NewStateData}
  69. %%% Reason = normal | shutdown | Term terminate(State) is called
  70. %%%
  71. %%% handle_info(Info, StateName) (e.g. {'EXIT', P, R}, {nodedown, N}, ...
  72. %%%
  73. %%% ==> {next_state, NewStateName, NewStateData}
  74. %%% {next_state, NewStateName, NewStateData, Timeout}
  75. %%% {stop, Reason, NewStateData}
  76. %%% Reason = normal | shutdown | Term terminate(State) is called
  77. %%%
  78. %%% terminate(Reason, StateName, StateData) Let the user module clean up
  79. %%% always called when server terminates
  80. %%%
  81. %%% ==> the return value is ignored
  82. %%%
  83. %%%
  84. %%% The work flow (of the fsm) can be described as follows:
  85. %%%
  86. %%% User module fsm
  87. %%% ----------- -------
  88. %%% start -----> start
  89. %%% init <----- .
  90. %%%
  91. %%% loop
  92. %%% StateName <----- .
  93. %%%
  94. %%% handle_event <----- .
  95. %%%
  96. %%% handle__sunc_event <----- .
  97. %%%
  98. %%% handle_info <----- .
  99. %%%
  100. %%% terminate <----- .
  101. %%%
  102. %%%
  103. %%% ---------------------------------------------------
  104. -export([start/3, start/4,
  105. start_link/3, start_link/4,
  106. send_event/2, sync_send_event/2, sync_send_event/3,
  107. send_all_state_event/2,
  108. sync_send_all_state_event/2, sync_send_all_state_event/3,
  109. reply/2,
  110. start_timer/2,send_event_after/2,cancel_timer/1,
  111. enter_loop/4, enter_loop/5, enter_loop/6]).
  112. -export([behaviour_info/1]).
  113. %% Internal exports
  114. -export([init_it/6, print_event/3,
  115. system_continue/3,
  116. system_terminate/4,
  117. system_code_change/4,
  118. format_status/2]).
  119. -import(error_logger , [format/2]).
  120. %%% ---------------------------------------------------
  121. %%% Interface functions.
  122. %%% ---------------------------------------------------
  123. behaviour_info(callbacks) ->
  124. [{init,1},{handle_event,3},{handle_sync_event,4},{handle_info,3},
  125. {terminate,3},{code_change,4}];
  126. behaviour_info(_Other) ->
  127. undefined.
  128. %%% ---------------------------------------------------
  129. %%% Starts a generic state machine.
  130. %%% start(Mod, Args, Options)
  131. %%% start(Name, Mod, Args, Options)
  132. %%% start_link(Mod, Args, Options)
  133. %%% start_link(Name, Mod, Args, Options) where:
  134. %%% Name ::= {local, atom()} | {global, atom()}
  135. %%% Mod ::= atom(), callback module implementing the 'real' fsm
  136. %%% Args ::= term(), init arguments (to Mod:init/1)
  137. %%% Options ::= [{debug, [Flag]}]
  138. %%% Flag ::= trace | log | {logfile, File} | statistics | debug
  139. %%% (debug == log && statistics)
  140. %%% Returns: {ok, Pid} |
  141. %%% {error, {already_started, Pid}} |
  142. %%% {error, Reason}
  143. %%% ---------------------------------------------------
  144. start(Mod, Args, Options) ->
  145. gen:start(?MODULE, nolink, Mod, Args, Options).
  146. start(Name, Mod, Args, Options) ->
  147. gen:start(?MODULE, nolink, Name, Mod, Args, Options).
  148. start_link(Mod, Args, Options) ->
  149. gen:start(?MODULE, link, Mod, Args, Options).
  150. start_link(Name, Mod, Args, Options) ->
  151. gen:start(?MODULE, link, Name, Mod, Args, Options).
  152. send_event({global, Name}, Event) ->
  153. catch global:send(Name, {'$gen_event', Event}),
  154. ok;
  155. send_event(Name, Event) ->
  156. Name ! {'$gen_event', Event},
  157. ok.
  158. sync_send_event(Name, Event) ->
  159. case catch gen:call(Name, '$gen_sync_event', Event) of
  160. {ok,Res} ->
  161. Res;
  162. {'EXIT',Reason} ->
  163. exit({Reason, {?MODULE, sync_send_event, [Name, Event]}})
  164. end.
  165. sync_send_event(Name, Event, Timeout) ->
  166. case catch gen:call(Name, '$gen_sync_event', Event, Timeout) of
  167. {ok,Res} ->
  168. Res;
  169. {'EXIT',Reason} ->
  170. exit({Reason, {?MODULE, sync_send_event, [Name, Event, Timeout]}})
  171. end.
  172. send_all_state_event({global, Name}, Event) ->
  173. catch global:send(Name, {'$gen_all_state_event', Event}),
  174. ok;
  175. send_all_state_event(Name, Event) ->
  176. Name ! {'$gen_all_state_event', Event},
  177. ok.
  178. sync_send_all_state_event(Name, Event) ->
  179. case catch gen:call(Name, '$gen_sync_all_state_event', Event) of
  180. {ok,Res} ->
  181. Res;
  182. {'EXIT',Reason} ->
  183. exit({Reason, {?MODULE, sync_send_all_state_event, [Name, Event]}})
  184. end.
  185. sync_send_all_state_event(Name, Event, Timeout) ->
  186. case catch gen:call(Name, '$gen_sync_all_state_event', Event, Timeout) of
  187. {ok,Res} ->
  188. Res;
  189. {'EXIT',Reason} ->
  190. exit({Reason, {?MODULE, sync_send_all_state_event,
  191. [Name, Event, Timeout]}})
  192. end.
  193. %% Designed to be only callable within one of the callbacks
  194. %% hence using the self() of this instance of the process.
  195. %% This is to ensure that timers don't go astray in global
  196. %% e.g. when straddling a failover, or turn up in a restarted
  197. %% instance of the process.
  198. %% Returns Ref, sends event {timeout,Ref,Msg} after Time
  199. %% to the (then) current state.
  200. start_timer(Time, Msg) ->
  201. erlang:start_timer(Time, self(), {'$gen_timer', Msg}).
  202. %% Returns Ref, sends Event after Time to the (then) current state.
  203. send_event_after(Time, Event) ->
  204. erlang:start_timer(Time, self(), {'$gen_event', Event}).
  205. %% Returns the remaing time for the timer if Ref referred to
  206. %% an active timer/send_event_after, false otherwise.
  207. cancel_timer(Ref) ->
  208. case erlang:cancel_timer(Ref) of
  209. false ->
  210. receive {timeout, Ref, _} -> 0
  211. after 0 -> false
  212. end;
  213. RemainingTime ->
  214. RemainingTime
  215. end.
  216. %% enter_loop/4,5,6
  217. %% Makes an existing process into a gen_fsm.
  218. %% The calling process will enter the gen_fsm receive loop and become a
  219. %% gen_fsm process.
  220. %% The process *must* have been started using one of the start functions
  221. %% in proc_lib, see proc_lib(3).
  222. %% The user is responsible for any initialization of the process,
  223. %% including registering a name for it.
  224. enter_loop(Mod, Options, StateName, StateData) ->
  225. enter_loop(Mod, Options, StateName, StateData, self(), infinity).
  226. enter_loop(Mod, Options, StateName, StateData, ServerName = {_,_}) ->
  227. enter_loop(Mod, Options, StateName, StateData, ServerName,infinity);
  228. enter_loop(Mod, Options, StateName, StateData, Timeout) ->
  229. enter_loop(Mod, Options, StateName, StateData, self(), Timeout).
  230. enter_loop(Mod, Options, StateName, StateData, ServerName, Timeout) ->
  231. Name = get_proc_name(ServerName),
  232. Parent = get_parent(),
  233. Debug = gen:debug_options(Options),
  234. loop(Parent, Name, StateName, StateData, Mod, Timeout, Debug).
  235. get_proc_name(Pid) when is_pid(Pid) ->
  236. Pid;
  237. get_proc_name({local, Name}) ->
  238. case process_info(self(), registered_name) of
  239. {registered_name, Name} ->
  240. Name;
  241. {registered_name, _Name} ->
  242. exit(process_not_registered);
  243. [] ->
  244. exit(process_not_registered)
  245. end;
  246. get_proc_name({global, Name}) ->
  247. case global:safe_whereis_name(Name) of
  248. undefined ->
  249. exit(process_not_registered_globally);
  250. Pid when Pid =:= self() ->
  251. Name;
  252. _Pid ->
  253. exit(process_not_registered_globally)
  254. end.
  255. get_parent() ->
  256. case get('$ancestors') of
  257. [Parent | _] when is_pid(Parent) ->
  258. Parent;
  259. [Parent | _] when is_atom(Parent) ->
  260. name_to_pid(Parent);
  261. _ ->
  262. exit(process_was_not_started_by_proc_lib)
  263. end.
  264. name_to_pid(Name) ->
  265. case whereis(Name) of
  266. undefined ->
  267. case global:safe_whereis_name(Name) of
  268. undefined ->
  269. exit(could_not_find_registerd_name);
  270. Pid ->
  271. Pid
  272. end;
  273. Pid ->
  274. Pid
  275. end.
  276. %%% ---------------------------------------------------
  277. %%% Initiate the new process.
  278. %%% Register the name using the Rfunc function
  279. %%% Calls the Mod:init/Args function.
  280. %%% Finally an acknowledge is sent to Parent and the main
  281. %%% loop is entered.
  282. %%% ---------------------------------------------------
  283. init_it(Starter, self, Name, Mod, Args, Options) ->
  284. init_it(Starter, self(), Name, Mod, Args, Options);
  285. init_it(Starter, Parent, Name, Mod, Args, Options) ->
  286. Debug = gen:debug_options(Options),
  287. gen:reg_behaviour(?MODULE),
  288. case catch Mod:init(Args) of
  289. {ok, StateName, StateData} ->
  290. proc_lib:init_ack(Starter, {ok, self()}),
  291. loop(Parent, Name, StateName, StateData, Mod, infinity, Debug);
  292. {ok, StateName, StateData, Timeout} ->
  293. proc_lib:init_ack(Starter, {ok, self()}),
  294. loop(Parent, Name, StateName, StateData, Mod, Timeout, Debug);
  295. {stop, Reason} ->
  296. proc_lib:init_ack(Starter, {error, Reason}),
  297. exit(Reason);
  298. ignore ->
  299. proc_lib:init_ack(Starter, ignore),
  300. exit(normal);
  301. {'EXIT', Reason} ->
  302. proc_lib:init_ack(Starter, {error, Reason}),
  303. exit(Reason);
  304. Else ->
  305. Error = {bad_return_value, Else},
  306. proc_lib:init_ack(Starter, {error, Error}),
  307. exit(Error)
  308. end.
  309. %%-----------------------------------------------------------------
  310. %% The MAIN loop
  311. %%-----------------------------------------------------------------
  312. loop(Parent, Name, StateName, StateData, Mod, Time, Debug) ->
  313. Msg = receive
  314. Input ->
  315. Input
  316. after Time ->
  317. {'$gen_event', timeout}
  318. end,
  319. case Msg of
  320. {system, From, Req} ->
  321. sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
  322. [Name, StateName, StateData, Mod, Time]);
  323. {'EXIT', Parent, Reason} ->
  324. terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug);
  325. _Msg when Debug =:= [] ->
  326. handle_msg(Msg, Parent, Name, StateName, StateData, Mod, Time);
  327. _Msg ->
  328. Debug1 = sys:handle_debug(Debug, {?MODULE, print_event},
  329. {Name, StateName}, {in, Msg}),
  330. handle_msg(Msg, Parent, Name, StateName, StateData,
  331. Mod, Time, Debug1)
  332. end.
  333. %%-----------------------------------------------------------------
  334. %% Callback functions for system messages handling.
  335. %%-----------------------------------------------------------------
  336. system_continue(Parent, Debug, [Name, StateName, StateData, Mod, Time]) ->
  337. loop(Parent, Name, StateName, StateData, Mod, Time, Debug).
  338. system_terminate(Reason, _Parent, Debug,
  339. [Name, StateName, StateData, Mod, _Time]) ->
  340. terminate(Reason, Name, [], Mod, StateName, StateData, Debug).
  341. system_code_change([Name, StateName, StateData, Mod, Time],
  342. _Module, OldVsn, Extra) ->
  343. case catch Mod:code_change(OldVsn, StateName, StateData, Extra) of
  344. {ok, NewStateName, NewStateData} ->
  345. {ok, [Name, NewStateName, NewStateData, Mod, Time]};
  346. Else -> Else
  347. end.
  348. %%-----------------------------------------------------------------
  349. %% Format debug messages. Print them as the call-back module sees
  350. %% them, not as the real erlang messages. Use trace for that.
  351. %%-----------------------------------------------------------------
  352. print_event(Dev, {in, Msg}, {Name, StateName}) ->
  353. case Msg of
  354. {'$gen_event', Event} ->
  355. io:format(Dev, "*DBG* ~p got event ~p in state ~w~n",
  356. [Name, Event, StateName]);
  357. {'$gen_all_state_event', Event} ->
  358. io:format(Dev,
  359. "*DBG* ~p got all_state_event ~p in state ~w~n",
  360. [Name, Event, StateName]);
  361. {timeout, Ref, {'$gen_timer', Message}} ->
  362. io:format(Dev,
  363. "*DBG* ~p got timer ~p in state ~w~n",
  364. [Name, {timeout, Ref, Message}, StateName]);
  365. {timeout, _Ref, {'$gen_event', Event}} ->
  366. io:format(Dev,
  367. "*DBG* ~p got timer ~p in state ~w~n",
  368. [Name, Event, StateName]);
  369. _ ->
  370. io:format(Dev, "*DBG* ~p got ~p in state ~w~n",
  371. [Name, Msg, StateName])
  372. end;
  373. print_event(Dev, {out, Msg, To, StateName}, Name) ->
  374. io:format(Dev, "*DBG* ~p sent ~p to ~w~n"
  375. " and switched to state ~w~n",
  376. [Name, Msg, To, StateName]);
  377. print_event(Dev, return, {Name, StateName}) ->
  378. io:format(Dev, "*DBG* ~p switched to state ~w~n",
  379. [Name, StateName]).
  380. handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time) -> %No debug here
  381. From = from(Msg),
  382. case catch dispatch(Msg, Mod, StateName, StateData) of
  383. {next_state, NStateName, NStateData} ->
  384. loop(Parent, Name, NStateName, NStateData, Mod, infinity, []);
  385. {next_state, NStateName, NStateData, Time1} ->
  386. loop(Parent, Name, NStateName, NStateData, Mod, Time1, []);
  387. {reply, Reply, NStateName, NStateData} when From =/= undefined ->
  388. reply(From, Reply),
  389. loop(Parent, Name, NStateName, NStateData, Mod, infinity, []);
  390. {reply, Reply, NStateName, NStateData, Time1} when From =/= undefined ->
  391. reply(From, Reply),
  392. loop(Parent, Name, NStateName, NStateData, Mod, Time1, []);
  393. {stop, Reason, NStateData} ->
  394. terminate(Reason, Name, Msg, Mod, StateName, NStateData, []);
  395. {stop, Reason, Reply, NStateData} when From =/= undefined ->
  396. {'EXIT', R} = (catch terminate(Reason, Name, Msg, Mod,
  397. StateName, NStateData, [])),
  398. reply(From, Reply),
  399. exit(R);
  400. {'EXIT', What} ->
  401. terminate(What, Name, Msg, Mod, StateName, StateData, []);
  402. Reply ->
  403. terminate({bad_return_value, Reply},
  404. Name, Msg, Mod, StateName, StateData, [])
  405. end.
  406. handle_msg(Msg, Parent, Name, StateName, StateData, Mod, _Time, Debug) ->
  407. From = from(Msg),
  408. case catch dispatch(Msg, Mod, StateName, StateData) of
  409. {next_state, NStateName, NStateData} ->
  410. Debug1 = sys:handle_debug(Debug, {?MODULE, print_event},
  411. {Name, NStateName}, return),
  412. loop(Parent, Name, NStateName, NStateData, Mod, infinity, Debug1);
  413. {next_state, NStateName, NStateData, Time1} ->
  414. Debug1 = sys:handle_debug(Debug, {?MODULE, print_event},
  415. {Name, NStateName}, return),
  416. loop(Parent, Name, NStateName, NStateData, Mod, Time1, Debug1);
  417. {reply, Reply, NStateName, NStateData} when From =/= undefined ->
  418. Debug1 = reply(Name, From, Reply, Debug, NStateName),
  419. loop(Parent, Name, NStateName, NStateData, Mod, infinity, Debug1);
  420. {reply, Reply, NStateName, NStateData, Time1} when From =/= undefined ->
  421. Debug1 = reply(Name, From, Reply, Debug, NStateName),
  422. loop(Parent, Name, NStateName, NStateData, Mod, Time1, Debug1);
  423. {stop, Reason, NStateData} ->
  424. terminate(Reason, Name, Msg, Mod, StateName, NStateData, Debug);
  425. {stop, Reason, Reply, NStateData} when From =/= undefined ->
  426. {'EXIT', R} = (catch terminate(Reason, Name, Msg, Mod,
  427. StateName, NStateData, Debug)),
  428. reply(Name, From, Reply, Debug, StateName),
  429. exit(R);
  430. {'EXIT', What} ->
  431. terminate(What, Name, Msg, Mod, StateName, StateData, Debug);
  432. Reply ->
  433. terminate({bad_return_value, Reply},
  434. Name, Msg, Mod, StateName, StateData, Debug)
  435. end.
  436. dispatch({'$gen_event', Event}, Mod, StateName, StateData) ->
  437. Mod:StateName(Event, StateData);
  438. dispatch({'$gen_all_state_event', Event}, Mod, StateName, StateData) ->
  439. Mod:handle_event(Event, StateName, StateData);
  440. dispatch({'$gen_sync_event', From, Event}, Mod, StateName, StateData) ->
  441. Mod:StateName(Event, From, StateData);
  442. dispatch({'$gen_sync_all_state_event', From, Event},
  443. Mod, StateName, StateData) ->
  444. Mod:handle_sync_event(Event, From, StateName, StateData);
  445. dispatch({timeout, Ref, {'$gen_timer', Msg}}, Mod, StateName, StateData) ->
  446. Mod:StateName({timeout, Ref, Msg}, StateData);
  447. dispatch({timeout, _Ref, {'$gen_event', Event}}, Mod, StateName, StateData) ->
  448. Mod:StateName(Event, StateData);
  449. dispatch(Info, Mod, StateName, StateData) ->
  450. Mod:handle_info(Info, StateName, StateData).
  451. from({'$gen_sync_event', From, _Event}) -> From;
  452. from({'$gen_sync_all_state_event', From, _Event}) -> From;
  453. from(_) -> undefined.
  454. %% Send a reply to the client.
  455. reply({To, Tag}, Reply) ->
  456. catch To ! {Tag, Reply}.
  457. reply(Name, {To, Tag}, Reply, Debug, StateName) ->
  458. reply({To, Tag}, Reply),
  459. sys:handle_debug(Debug, {?MODULE, print_event}, Name,
  460. {out, Reply, To, StateName}).
  461. %%% ---------------------------------------------------
  462. %%% Terminate the server.
  463. %%% ---------------------------------------------------
  464. terminate(Reason, Name, Msg, Mod, StateName, StateData, Debug) ->
  465. case catch Mod:terminate(Reason, StateName, StateData) of
  466. {'EXIT', R} ->
  467. error_info(R, Name, Msg, StateName, StateData, Debug),
  468. exit(R);
  469. _ ->
  470. case Reason of
  471. normal ->
  472. exit(normal);
  473. shutdown ->
  474. exit(shutdown);
  475. _ ->
  476. error_info(Reason, Name, Msg, StateName, StateData, Debug),
  477. exit(Reason)
  478. end
  479. end.
  480. error_info(Reason, Name, Msg, StateName, StateData, Debug) ->
  481. Reason1 =
  482. case Reason of
  483. {undef,[{M,F,A}|MFAs]} ->
  484. case code:is_loaded(M) of
  485. false ->
  486. {'module could not be loaded',[{M,F,A}|MFAs]};
  487. _ ->
  488. case erlang:function_exported(M, F, length(A)) of
  489. true ->
  490. Reason;
  491. false ->
  492. {'function not exported',[{M,F,A}|MFAs]}
  493. end
  494. end;
  495. _ ->
  496. Reason
  497. end,
  498. Str = "** State machine ~p terminating \n" ++
  499. get_msg_str(Msg) ++
  500. "** When State == ~p~n"
  501. "** Data == ~p~n"
  502. "** Reason for termination = ~n** ~p~n",
  503. format(Str, [Name, get_msg(Msg), StateName, StateData, Reason1]),
  504. sys:print_log(Debug),
  505. ok.
  506. get_msg_str({'$gen_event', _Event}) ->
  507. "** Last event in was ~p~n";
  508. get_msg_str({'$gen_sync_event', _Event}) ->
  509. "** Last sync event in was ~p~n";
  510. get_msg_str({'$gen_all_state_event', _Event}) ->
  511. "** Last event in was ~p (for all states)~n";
  512. get_msg_str({'$gen_sync_all_state_event', _Event}) ->
  513. "** Last sync event in was ~p (for all states)~n";
  514. get_msg_str({timeout, _Ref, {'$gen_timer', _Msg}}) ->
  515. "** Last timer event in was ~p~n";
  516. get_msg_str({timeout, _Ref, {'$gen_event', _Msg}}) ->
  517. "** Last timer event in was ~p~n";
  518. get_msg_str(_Msg) ->
  519. "** Last message in was ~p~n".
  520. get_msg({'$gen_event', Event}) -> Event;
  521. get_msg({'$gen_sync_event', Event}) -> Event;
  522. get_msg({'$gen_all_state_event', Event}) -> Event;
  523. get_msg({'$gen_sync_all_state_event', Event}) -> Event;
  524. get_msg({timeout, Ref, {'$gen_timer', Msg}}) -> {timeout, Ref, Msg};
  525. get_msg({timeout, _Ref, {'$gen_event', Event}}) -> Event;
  526. get_msg(Msg) -> Msg.
  527. %%-----------------------------------------------------------------
  528. %% Status information
  529. %%-----------------------------------------------------------------
  530. format_status(Opt, StatusData) ->
  531. [PDict, SysState, Parent, Debug, [Name, StateName, StateData, Mod, _Time]] =
  532. StatusData,
  533. Header = lists:concat(["Status for state machine ", Name]),
  534. Log = sys:get_debug(log, Debug, []),
  535. Specfic =
  536. case erlang:function_exported(Mod, format_status, 2) of
  537. true ->
  538. case catch Mod:format_status(Opt,[PDict,StateData]) of
  539. {'EXIT', _} -> [{data, [{"StateData", StateData}]}];
  540. Else -> Else
  541. end;
  542. _ ->
  543. [{data, [{"StateData", StateData}]}]
  544. end,
  545. [{header, Header},
  546. {data, [{"Status", SysState},
  547. {"Parent", Parent},
  548. {"Logged events", Log},
  549. {"StateName", StateName}]} |
  550. Specfic].