1 %% This file is a copy of gen_server.erl from the R11B-5 Erlang/OTP
2 %% distribution, with the following modifications:
4 %% 1) the module name is gen_server2
6 %% 2) more efficient handling of selective receives in callbacks
7 %% gen_server2 processes drain their message queue into an internal
8 %% buffer before invoking any callback module functions. Messages are
9 %% dequeued from the buffer for processing. Thus the effective message
10 %% queue of a gen_server2 process is the concatenation of the internal
11 %% buffer and the real message queue.
12 %% As a result of the draining, any selective receive invoked inside a
13 %% callback is less likely to have to scan a large message queue.
15 %% 3) gen_server2:cast is guaranteed to be order-preserving
16 %% The original code could reorder messages when communicating with a
17 %% process on a remote node that was not currently connected.
19 %% All modifications are (C) 2009 LShift Ltd.
21 %% ``The contents of this file are subject to the Erlang Public License,
22 %% Version 1.1, (the "License"); you may not use this file except in
23 %% compliance with the License. You should have received a copy of the
24 %% Erlang Public License along with this software. If not, it can be
25 %% retrieved via the world wide web at http://www.erlang.org/.
27 %% Software distributed under the License is distributed on an "AS IS"
28 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
29 %% the License for the specific language governing rights and limitations
32 %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
33 %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
34 %% AB. All Rights Reserved.''
40 %%% ---------------------------------------------------
42 %%% The idea behind THIS server is that the user module
43 %%% provides (different) functions to handle different
45 %%% If the Parent process terminates the Module:terminate/2
46 %%% function is called.
48 %%% The user module should export:
52 %%% {ok, State, Timeout}
56 %%% handle_call(Msg, {From, Tag}, State)
58 %%% ==> {reply, Reply, State}
59 %%% {reply, Reply, State, Timeout}
61 %%% {noreply, State, Timeout}
62 %%% {stop, Reason, Reply, State}
63 %%% Reason = normal | shutdown | Term terminate(State) is called
65 %%% handle_cast(Msg, State)
67 %%% ==> {noreply, State}
68 %%% {noreply, State, Timeout}
69 %%% {stop, Reason, State}
70 %%% Reason = normal | shutdown | Term terminate(State) is called
72 %%% handle_info(Info, State) Info is e.g. {'EXIT', P, R}, {nodedown, N}, ...
74 %%% ==> {noreply, State}
75 %%% {noreply, State, Timeout}
76 %%% {stop, Reason, State}
77 %%% Reason = normal | shutdown | Term, terminate(State) is called
79 %%% terminate(Reason, State) Let the user module clean up
80 %%% always called when server terminates
85 %%% The work flow (of the server) can be described as follows:
87 %%% User module Generic
88 %%% ----------- -------
89 %%% start -----> start
93 %%% handle_call <----- .
96 %%% handle_cast <----- .
98 %%% handle_info <----- .
100 %%% terminate <----- .
105 %%% ---------------------------------------------------
108 -export([start/3, start/4,
109 start_link/3, start_link/4,
113 multi_call/2, multi_call/3, multi_call/4,
114 enter_loop/3, enter_loop/4, enter_loop/5]).
116 -export([behaviour_info/1]).
119 -export([system_continue/3,
121 system_code_change/4,
125 -export([init_it/6, print_event/3]).
127 -import(error_logger, [format/2]).
129 %%%=========================================================================
131 %%%=========================================================================
133 behaviour_info(callbacks) ->
134 [{init,1},{handle_call,3},{handle_cast,2},{handle_info,2},
135 {terminate,2},{code_change,3}];
136 behaviour_info(_Other) ->
139 %%% -----------------------------------------------------------------
140 %%% Starts a generic server.
141 %%% start(Mod, Args, Options)
142 %%% start(Name, Mod, Args, Options)
143 %%% start_link(Mod, Args, Options)
144 %%% start_link(Name, Mod, Args, Options) where:
145 %%% Name ::= {local, atom()} | {global, atom()}
146 %%% Mod ::= atom(), callback module implementing the 'real' server
147 %%% Args ::= term(), init arguments (to Mod:init/1)
148 %%% Options ::= [{timeout, Timeout} | {debug, [Flag]}]
149 %%% Flag ::= trace | log | {logfile, File} | statistics | debug
150 %%% (debug == log && statistics)
151 %%% Returns: {ok, Pid} |
152 %%% {error, {already_started, Pid}} |
154 %%% -----------------------------------------------------------------
155 start(Mod, Args, Options) ->
156 gen:start(?MODULE, nolink, Mod, Args, Options).
158 start(Name, Mod, Args, Options) ->
159 gen:start(?MODULE, nolink, Name, Mod, Args, Options).
161 start_link(Mod, Args, Options) ->
162 gen:start(?MODULE, link, Mod, Args, Options).
164 start_link(Name, Mod, Args, Options) ->
165 gen:start(?MODULE, link, Name, Mod, Args, Options).
168 %% -----------------------------------------------------------------
169 %% Make a call to a generic server.
170 %% If the server is located at another node, that node will
172 %% If the client is trapping exits and is linked server termination
173 %% is handled here (? Shall we do that here (or rely on timeouts) ?).
174 %% -----------------------------------------------------------------
175 call(Name, Request) ->
176 case catch gen:call(Name, '$gen_call', Request) of
180 exit({Reason, {?MODULE, call, [Name, Request]}})
183 call(Name, Request, Timeout) ->
184 case catch gen:call(Name, '$gen_call', Request, Timeout) of
188 exit({Reason, {?MODULE, call, [Name, Request, Timeout]}})
191 %% -----------------------------------------------------------------
192 %% Make a cast to a generic server.
193 %% -----------------------------------------------------------------
194 cast({global,Name}, Request) ->
195 catch global:send(Name, cast_msg(Request)),
197 cast({Name,Node}=Dest, Request) when is_atom(Name), is_atom(Node) ->
198 do_cast(Dest, Request);
199 cast(Dest, Request) when is_atom(Dest) ->
200 do_cast(Dest, Request);
201 cast(Dest, Request) when is_pid(Dest) ->
202 do_cast(Dest, Request).
204 do_cast(Dest, Request) ->
205 do_send(Dest, cast_msg(Request)),
208 cast_msg(Request) -> {'$gen_cast',Request}.
210 %% -----------------------------------------------------------------
211 %% Send a reply to the client.
212 %% -----------------------------------------------------------------
213 reply({To, Tag}, Reply) ->
214 catch To ! {Tag, Reply}.
216 %% -----------------------------------------------------------------
217 %% Asyncronous broadcast, returns nothing, it's just send'n prey
218 %%-----------------------------------------------------------------
219 abcast(Name, Request) when is_atom(Name) ->
220 do_abcast([node() | nodes()], Name, cast_msg(Request)).
222 abcast(Nodes, Name, Request) when is_list(Nodes), is_atom(Name) ->
223 do_abcast(Nodes, Name, cast_msg(Request)).
225 do_abcast([Node|Nodes], Name, Msg) when is_atom(Node) ->
226 do_send({Name,Node},Msg),
227 do_abcast(Nodes, Name, Msg);
228 do_abcast([], _,_) -> abcast.
230 %%% -----------------------------------------------------------------
231 %%% Make a call to servers at several nodes.
232 %%% Returns: {[Replies],[BadNodes]}
233 %%% A Timeout can be given
235 %%% A middleman process is used in case late answers arrives after
236 %%% the timeout. If they would be allowed to glog the callers message
237 %%% queue, it would probably become confused. Late answers will
238 %%% now arrive to the terminated middleman and so be discarded.
239 %%% -----------------------------------------------------------------
240 multi_call(Name, Req)
241 when is_atom(Name) ->
242 do_multi_call([node() | nodes()], Name, Req, infinity).
244 multi_call(Nodes, Name, Req)
245 when is_list(Nodes), is_atom(Name) ->
246 do_multi_call(Nodes, Name, Req, infinity).
248 multi_call(Nodes, Name, Req, infinity) ->
249 do_multi_call(Nodes, Name, Req, infinity);
250 multi_call(Nodes, Name, Req, Timeout)
251 when is_list(Nodes), is_atom(Name), is_integer(Timeout), Timeout >= 0 ->
252 do_multi_call(Nodes, Name, Req, Timeout).
255 %%-----------------------------------------------------------------
256 %% enter_loop(Mod, Options, State, <ServerName>, <TimeOut>) ->_
258 %% Description: Makes an existing process into a gen_server.
259 %% The calling process will enter the gen_server receive
260 %% loop and become a gen_server process.
261 %% The process *must* have been started using one of the
262 %% start functions in proc_lib, see proc_lib(3).
263 %% The user is responsible for any initialization of the
264 %% process, including registering a name for it.
265 %%-----------------------------------------------------------------
266 enter_loop(Mod, Options, State) ->
267 enter_loop(Mod, Options, State, self(), infinity).
269 enter_loop(Mod, Options, State, ServerName = {_, _}) ->
270 enter_loop(Mod, Options, State, ServerName, infinity);
272 enter_loop(Mod, Options, State, Timeout) ->
273 enter_loop(Mod, Options, State, self(), Timeout).
275 enter_loop(Mod, Options, State, ServerName, Timeout) ->
276 Name = get_proc_name(ServerName),
277 Parent = get_parent(),
278 Debug = debug_options(Name, Options),
280 loop(Parent, Name, State, Mod, Timeout, Queue, Debug).
282 %%%========================================================================
283 %%% Gen-callback functions
284 %%%========================================================================
286 %%% ---------------------------------------------------
287 %%% Initiate the new process.
288 %%% Register the name using the Rfunc function
289 %%% Calls the Mod:init/Args function.
290 %%% Finally an acknowledge is sent to Parent and the main
292 %%% ---------------------------------------------------
293 init_it(Starter, self, Name, Mod, Args, Options) ->
294 init_it(Starter, self(), Name, Mod, Args, Options);
295 init_it(Starter, Parent, Name, Mod, Args, Options) ->
296 Debug = debug_options(Name, Options),
298 case catch Mod:init(Args) of
300 proc_lib:init_ack(Starter, {ok, self()}),
301 loop(Parent, Name, State, Mod, infinity, Queue, Debug);
302 {ok, State, Timeout} ->
303 proc_lib:init_ack(Starter, {ok, self()}),
304 loop(Parent, Name, State, Mod, Timeout, Queue, Debug);
306 proc_lib:init_ack(Starter, {error, Reason}),
309 proc_lib:init_ack(Starter, ignore),
312 proc_lib:init_ack(Starter, {error, Reason}),
315 Error = {bad_return_value, Else},
316 proc_lib:init_ack(Starter, {error, Error}),
320 %%%========================================================================
321 %%% Internal functions
322 %%%========================================================================
323 %%% ---------------------------------------------------
325 %%% ---------------------------------------------------
326 loop(Parent, Name, State, Mod, Time, Queue, Debug) ->
328 Input -> loop(Parent, Name, State, Mod,
329 Time, queue:in(Input, Queue), Debug)
331 case queue:out(Queue) of
332 {{value, Msg}, Queue1} ->
333 process_msg(Parent, Name, State, Mod,
334 Time, Queue1, Debug, Msg);
338 loop(Parent, Name, State, Mod,
339 Time, queue:in(Input, Queue1), Debug)
341 process_msg(Parent, Name, State, Mod,
342 Time, Queue1, Debug, timeout)
347 process_msg(Parent, Name, State, Mod, Time, Queue, Debug, Msg) ->
349 {system, From, Req} ->
350 sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
351 [Name, State, Mod, Time, Queue]);
352 {'EXIT', Parent, Reason} ->
353 terminate(Reason, Name, Msg, Mod, State, Debug);
354 _Msg when Debug =:= [] ->
355 handle_msg(Msg, Parent, Name, State, Mod, Time, Queue);
357 Debug1 = sys:handle_debug(Debug, {?MODULE, print_event},
359 handle_msg(Msg, Parent, Name, State, Mod, Time, Queue, Debug1)
362 %%% ---------------------------------------------------
363 %%% Send/recive functions
364 %%% ---------------------------------------------------
365 do_send(Dest, Msg) ->
366 catch erlang:send(Dest, Msg).
368 do_multi_call(Nodes, Name, Req, infinity) ->
370 Monitors = send_nodes(Nodes, Name, Tag, Req),
371 rec_nodes(Tag, Monitors, Name, undefined);
372 do_multi_call(Nodes, Name, Req, Timeout) ->
378 %% Middleman process. Should be unsensitive to regular
379 %% exit signals. The sychronization is needed in case
380 %% the receiver would exit before the caller started
382 process_flag(trap_exit, true),
383 Mref = erlang:monitor(process, Caller),
386 Monitors = send_nodes(Nodes, Name, Tag, Req),
387 TimerId = erlang:start_timer(Timeout, self(), ok),
388 Result = rec_nodes(Tag, Monitors, Name, TimerId),
389 exit({self(),Tag,Result});
390 {'DOWN',Mref,_,_,_} ->
391 %% Caller died before sending us the go-ahead.
396 Mref = erlang:monitor(process, Receiver),
397 Receiver ! {self(),Tag},
399 {'DOWN',Mref,_,_,{Receiver,Tag,Result}} ->
401 {'DOWN',Mref,_,_,Reason} ->
402 %% The middleman code failed. Or someone did
403 %% exit(_, kill) on the middleman process => Reason==killed
407 send_nodes(Nodes, Name, Tag, Req) ->
408 send_nodes(Nodes, Name, Tag, Req, []).
410 send_nodes([Node|Tail], Name, Tag, Req, Monitors)
411 when is_atom(Node) ->
412 Monitor = start_monitor(Node, Name),
413 %% Handle non-existing names in rec_nodes.
414 catch {Name, Node} ! {'$gen_call', {self(), {Tag, Node}}, Req},
415 send_nodes(Tail, Name, Tag, Req, [Monitor | Monitors]);
416 send_nodes([_Node|Tail], Name, Tag, Req, Monitors) ->
417 %% Skip non-atom Node
418 send_nodes(Tail, Name, Tag, Req, Monitors);
419 send_nodes([], _Name, _Tag, _Req, Monitors) ->
422 %% Against old nodes:
423 %% If no reply has been delivered within 2 secs. (per node) check that
424 %% the server really exists and wait for ever for the answer.
426 %% Against contemporary nodes:
427 %% Wait for reply, server 'DOWN', or timeout from TimerId.
429 rec_nodes(Tag, Nodes, Name, TimerId) ->
430 rec_nodes(Tag, Nodes, Name, [], [], 2000, TimerId).
432 rec_nodes(Tag, [{N,R}|Tail], Name, Badnodes, Replies, Time, TimerId ) ->
434 {'DOWN', R, _, _, _} ->
435 rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, Time, TimerId);
436 {{Tag, N}, Reply} -> %% Tag is bound !!!
438 rec_nodes(Tag, Tail, Name, Badnodes,
439 [{N,Reply}|Replies], Time, TimerId);
440 {timeout, TimerId, _} ->
442 %% Collect all replies that already have arrived
443 rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
445 rec_nodes(Tag, [N|Tail], Name, Badnodes, Replies, Time, TimerId) ->
449 monitor_node(N, false),
450 rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, 2000, TimerId);
451 {{Tag, N}, Reply} -> %% Tag is bound !!!
452 receive {nodedown, N} -> ok after 0 -> ok end,
453 monitor_node(N, false),
454 rec_nodes(Tag, Tail, Name, Badnodes,
455 [{N,Reply}|Replies], 2000, TimerId);
456 {timeout, TimerId, _} ->
457 receive {nodedown, N} -> ok after 0 -> ok end,
458 monitor_node(N, false),
459 %% Collect all replies that already have arrived
460 rec_nodes_rest(Tag, Tail, Name, [N | Badnodes], Replies)
462 case rpc:call(N, erlang, whereis, [Name]) of
463 Pid when is_pid(Pid) -> % It exists try again.
464 rec_nodes(Tag, [N|Tail], Name, Badnodes,
465 Replies, infinity, TimerId);
467 receive {nodedown, N} -> ok after 0 -> ok end,
468 monitor_node(N, false),
469 rec_nodes(Tag, Tail, Name, [N|Badnodes],
470 Replies, 2000, TimerId)
473 rec_nodes(_, [], _, Badnodes, Replies, _, TimerId) ->
474 case catch erlang:cancel_timer(TimerId) of
475 false -> % It has already sent it's message
477 {timeout, TimerId, _} -> ok
481 _ -> % Timer was cancelled, or TimerId was 'undefined'
486 %% Collect all replies that already have arrived
487 rec_nodes_rest(Tag, [{N,R}|Tail], Name, Badnodes, Replies) ->
489 {'DOWN', R, _, _, _} ->
490 rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies);
491 {{Tag, N}, Reply} -> %% Tag is bound !!!
493 rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies])
496 rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
498 rec_nodes_rest(Tag, [N|Tail], Name, Badnodes, Replies) ->
502 monitor_node(N, false),
503 rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies);
504 {{Tag, N}, Reply} -> %% Tag is bound !!!
505 receive {nodedown, N} -> ok after 0 -> ok end,
506 monitor_node(N, false),
507 rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies])
509 receive {nodedown, N} -> ok after 0 -> ok end,
510 monitor_node(N, false),
511 rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
513 rec_nodes_rest(_Tag, [], _Name, Badnodes, Replies) ->
517 %%% ---------------------------------------------------
518 %%% Monitor functions
519 %%% ---------------------------------------------------
521 start_monitor(Node, Name) when is_atom(Node), is_atom(Name) ->
522 if node() =:= nonode@nohost, Node =/= nonode@nohost ->
524 self() ! {'DOWN', Ref, process, {Name, Node}, noconnection},
527 case catch erlang:monitor(process, {Name, Node}) of
530 monitor_node(Node, true),
532 Ref when is_reference(Ref) ->
537 %% Cancels a monitor started with Ref=erlang:monitor(_, _).
538 unmonitor(Ref) when is_reference(Ref) ->
539 erlang:demonitor(Ref),
541 {'DOWN', Ref, _, _, _} ->
547 %%% ---------------------------------------------------
548 %%% Message handling functions
549 %%% ---------------------------------------------------
551 dispatch({'$gen_cast', Msg}, Mod, State) ->
552 Mod:handle_cast(Msg, State);
553 dispatch(Info, Mod, State) ->
554 Mod:handle_info(Info, State).
556 handle_msg({'$gen_call', From, Msg},
557 Parent, Name, State, Mod, _Time, Queue) ->
558 case catch Mod:handle_call(Msg, From, State) of
559 {reply, Reply, NState} ->
561 loop(Parent, Name, NState, Mod, infinity, Queue, []);
562 {reply, Reply, NState, Time1} ->
564 loop(Parent, Name, NState, Mod, Time1, Queue, []);
566 loop(Parent, Name, NState, Mod, infinity, Queue, []);
567 {noreply, NState, Time1} ->
568 loop(Parent, Name, NState, Mod, Time1, Queue, []);
569 {stop, Reason, Reply, NState} ->
571 (catch terminate(Reason, Name, Msg, Mod, NState, [])),
574 Other -> handle_common_reply(Other,
575 Parent, Name, Msg, Mod, State, Queue)
578 Parent, Name, State, Mod, _Time, Queue) ->
579 Reply = (catch dispatch(Msg, Mod, State)),
580 handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Queue).
582 handle_msg({'$gen_call', From, Msg},
583 Parent, Name, State, Mod, _Time, Queue, Debug) ->
584 case catch Mod:handle_call(Msg, From, State) of
585 {reply, Reply, NState} ->
586 Debug1 = reply(Name, From, Reply, NState, Debug),
587 loop(Parent, Name, NState, Mod, infinity, Queue, Debug1);
588 {reply, Reply, NState, Time1} ->
589 Debug1 = reply(Name, From, Reply, NState, Debug),
590 loop(Parent, Name, NState, Mod, Time1, Queue, Debug1);
592 Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
594 loop(Parent, Name, NState, Mod, infinity, Queue, Debug1);
595 {noreply, NState, Time1} ->
596 Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
598 loop(Parent, Name, NState, Mod, Time1, Queue, Debug1);
599 {stop, Reason, Reply, NState} ->
601 (catch terminate(Reason, Name, Msg, Mod, NState, Debug)),
602 reply(Name, From, Reply, NState, Debug),
605 handle_common_reply(Other,
606 Parent, Name, Msg, Mod, State, Queue, Debug)
609 Parent, Name, State, Mod, _Time, Queue, Debug) ->
610 Reply = (catch dispatch(Msg, Mod, State)),
611 handle_common_reply(Reply,
612 Parent, Name, Msg, Mod, State, Queue, Debug).
614 handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Queue) ->
617 loop(Parent, Name, NState, Mod, infinity, Queue, []);
618 {noreply, NState, Time1} ->
619 loop(Parent, Name, NState, Mod, Time1, Queue, []);
620 {stop, Reason, NState} ->
621 terminate(Reason, Name, Msg, Mod, NState, []);
623 terminate(What, Name, Msg, Mod, State, []);
625 terminate({bad_return_value, Reply}, Name, Msg, Mod, State, [])
628 handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Queue, Debug) ->
631 Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
633 loop(Parent, Name, NState, Mod, infinity, Queue, Debug1);
634 {noreply, NState, Time1} ->
635 Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
637 loop(Parent, Name, NState, Mod, Time1, Queue, Debug1);
638 {stop, Reason, NState} ->
639 terminate(Reason, Name, Msg, Mod, NState, Debug);
641 terminate(What, Name, Msg, Mod, State, Debug);
643 terminate({bad_return_value, Reply}, Name, Msg, Mod, State, Debug)
646 reply(Name, {To, Tag}, Reply, State, Debug) ->
647 reply({To, Tag}, Reply),
648 sys:handle_debug(Debug, {?MODULE, print_event}, Name,
649 {out, Reply, To, State} ).
652 %%-----------------------------------------------------------------
653 %% Callback functions for system messages handling.
654 %%-----------------------------------------------------------------
655 system_continue(Parent, Debug, [Name, State, Mod, Time, Queue]) ->
656 loop(Parent, Name, State, Mod, Time, Queue, Debug).
658 system_terminate(Reason, _Parent, Debug, [Name, State, Mod, _Time, _Queue]) ->
659 terminate(Reason, Name, [], Mod, State, Debug).
661 system_code_change([Name, State, Mod, Time, Queue], _Module, OldVsn, Extra) ->
662 case catch Mod:code_change(OldVsn, State, Extra) of
663 {ok, NewState} -> {ok, [Name, NewState, Mod, Time, Queue]};
667 %%-----------------------------------------------------------------
668 %% Format debug messages. Print them as the call-back module sees
669 %% them, not as the real erlang messages. Use trace for that.
670 %%-----------------------------------------------------------------
671 print_event(Dev, {in, Msg}, Name) ->
673 {'$gen_call', {From, _Tag}, Call} ->
674 io:format(Dev, "*DBG* ~p got call ~p from ~w~n",
676 {'$gen_cast', Cast} ->
677 io:format(Dev, "*DBG* ~p got cast ~p~n",
680 io:format(Dev, "*DBG* ~p got ~p~n", [Name, Msg])
682 print_event(Dev, {out, Msg, To, State}, Name) ->
683 io:format(Dev, "*DBG* ~p sent ~p to ~w, new state ~w~n",
684 [Name, Msg, To, State]);
685 print_event(Dev, {noreply, State}, Name) ->
686 io:format(Dev, "*DBG* ~p new state ~w~n", [Name, State]);
687 print_event(Dev, Event, Name) ->
688 io:format(Dev, "*DBG* ~p dbg ~p~n", [Name, Event]).
691 %%% ---------------------------------------------------
692 %%% Terminate the server.
693 %%% ---------------------------------------------------
695 terminate(Reason, Name, Msg, Mod, State, Debug) ->
696 case catch Mod:terminate(Reason, State) of
698 error_info(R, Name, Msg, State, Debug),
707 error_info(Reason, Name, Msg, State, Debug),
712 error_info(_Reason, application_controller, _Msg, _State, _Debug) ->
713 %% OTP-5811 Don't send an error report if it's the system process
714 %% application_controller which is terminating - let init take care
717 error_info(Reason, Name, Msg, State, Debug) ->
720 {undef,[{M,F,A}|MFAs]} ->
721 case code:is_loaded(M) of
723 {'module could not be loaded',[{M,F,A}|MFAs]};
725 case erlang:function_exported(M, F, length(A)) of
729 {'function not exported',[{M,F,A}|MFAs]}
735 format("** Generic server ~p terminating \n"
736 "** Last message in was ~p~n"
737 "** When Server state == ~p~n"
738 "** Reason for termination == ~n** ~p~n",
739 [Name, Msg, State, Reason1]),
740 sys:print_log(Debug),
743 %%% ---------------------------------------------------
745 %%% ---------------------------------------------------
747 opt(Op, [{Op, Value}|_]) ->
749 opt(Op, [_|Options]) ->
754 debug_options(Name, Opts) ->
755 case opt(debug, Opts) of
756 {ok, Options} -> dbg_options(Name, Options);
757 _ -> dbg_options(Name, [])
760 dbg_options(Name, []) ->
762 case init:get_argument(generic_debug) of
768 dbg_opts(Name, Opts);
769 dbg_options(Name, Opts) ->
770 dbg_opts(Name, Opts).
772 dbg_opts(Name, Opts) ->
773 case catch sys:debug_options(Opts) of
775 format("~p: ignoring erroneous debug options - ~p~n",
782 get_proc_name(Pid) when is_pid(Pid) ->
784 get_proc_name({local, Name}) ->
785 case process_info(self(), registered_name) of
786 {registered_name, Name} ->
788 {registered_name, _Name} ->
789 exit(process_not_registered);
791 exit(process_not_registered)
793 get_proc_name({global, Name}) ->
794 case global:safe_whereis_name(Name) of
796 exit(process_not_registered_globally);
797 Pid when Pid =:= self() ->
800 exit(process_not_registered_globally)
804 case get('$ancestors') of
805 [Parent | _] when is_pid(Parent)->
807 [Parent | _] when is_atom(Parent)->
810 exit(process_was_not_started_by_proc_lib)
814 case whereis(Name) of
816 case global:safe_whereis_name(Name) of
818 exit(could_not_find_registerd_name);
826 %%-----------------------------------------------------------------
827 %% Status information
828 %%-----------------------------------------------------------------
829 format_status(Opt, StatusData) ->
830 [PDict, SysState, Parent, Debug, [Name, State, Mod, _Time, Queue]] =
832 NameTag = if is_pid(Name) ->
837 Header = lists:concat(["Status for generic server ", NameTag]),
838 Log = sys:get_debug(log, Debug, []),
840 case erlang:function_exported(Mod, format_status, 2) of
842 case catch Mod:format_status(Opt, [PDict, State]) of
843 {'EXIT', _} -> [{data, [{"State", State}]}];
847 [{data, [{"State", State}]}]
850 {data, [{"Status", SysState},
852 {"Logged events", Log},
853 {"Queued messages", queue:to_list(Queue)}]} |