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
7 %% 2) more efficient handling of selective receives in callbacks
8 %% gen_server2 processes drain their message queue into an internal
9 %% buffer before invoking any callback module functions. Messages are
10 %% dequeued from the buffer for processing. Thus the effective message
11 %% queue of a gen_server2 process is the concatenation of the internal
12 %% buffer and the real message queue.
13 %% As a result of the draining, any selective receive invoked inside a
14 %% callback is less likely to have to scan a large message queue.
16 %% All modifications are (C) 2009 LShift Ltd.
18 %% ``The contents of this file are subject to the Erlang Public License,
19 %% Version 1.1, (the "License"); you may not use this file except in
20 %% compliance with the License. You should have received a copy of the
21 %% Erlang Public License along with this software. If not, it can be
22 %% retrieved via the world wide web at http://www.erlang.org/.
24 %% Software distributed under the License is distributed on an "AS IS"
25 %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
26 %% the License for the specific language governing rights and limitations
29 %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
30 %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
31 %% AB. All Rights Reserved.''
37 %%% ---------------------------------------------------
39 %%% The idea behind THIS server is that the user module
40 %%% provides (different) functions to handle different
42 %%% If the Parent process terminates the Module:terminate/2
43 %%% function is called.
45 %%% The user module should export:
49 %%% {ok, State, Timeout}
53 %%% handle_call(Msg, {From, Tag}, State)
55 %%% ==> {reply, Reply, State}
56 %%% {reply, Reply, State, Timeout}
58 %%% {noreply, State, Timeout}
59 %%% {stop, Reason, Reply, State}
60 %%% Reason = normal | shutdown | Term terminate(State) is called
62 %%% handle_cast(Msg, State)
64 %%% ==> {noreply, State}
65 %%% {noreply, State, Timeout}
66 %%% {stop, Reason, State}
67 %%% Reason = normal | shutdown | Term terminate(State) is called
69 %%% handle_info(Info, State) Info is e.g. {'EXIT', P, R}, {nodedown, N}, ...
71 %%% ==> {noreply, State}
72 %%% {noreply, State, Timeout}
73 %%% {stop, Reason, State}
74 %%% Reason = normal | shutdown | Term, terminate(State) is called
76 %%% terminate(Reason, State) Let the user module clean up
77 %%% always called when server terminates
82 %%% The work flow (of the server) can be described as follows:
84 %%% User module Generic
85 %%% ----------- -------
86 %%% start -----> start
90 %%% handle_call <----- .
93 %%% handle_cast <----- .
95 %%% handle_info <----- .
97 %%% terminate <----- .
102 %%% ---------------------------------------------------
105 -export([start/3, start/4,
106 start_link/3, start_link/4,
110 multi_call/2, multi_call/3, multi_call/4,
111 enter_loop/3, enter_loop/4, enter_loop/5]).
113 -export([behaviour_info/1]).
116 -export([system_continue/3,
118 system_code_change/4,
122 -export([init_it/6, print_event/3]).
124 -import(error_logger, [format/2]).
126 %%%=========================================================================
128 %%%=========================================================================
130 behaviour_info(callbacks) ->
131 [{init,1},{handle_call,3},{handle_cast,2},{handle_info,2},
132 {terminate,2},{code_change,3}];
133 behaviour_info(_Other) ->
136 %%% -----------------------------------------------------------------
137 %%% Starts a generic server.
138 %%% start(Mod, Args, Options)
139 %%% start(Name, Mod, Args, Options)
140 %%% start_link(Mod, Args, Options)
141 %%% start_link(Name, Mod, Args, Options) where:
142 %%% Name ::= {local, atom()} | {global, atom()}
143 %%% Mod ::= atom(), callback module implementing the 'real' server
144 %%% Args ::= term(), init arguments (to Mod:init/1)
145 %%% Options ::= [{timeout, Timeout} | {debug, [Flag]}]
146 %%% Flag ::= trace | log | {logfile, File} | statistics | debug
147 %%% (debug == log && statistics)
148 %%% Returns: {ok, Pid} |
149 %%% {error, {already_started, Pid}} |
151 %%% -----------------------------------------------------------------
152 start(Mod, Args, Options) ->
153 gen:start(?MODULE, nolink, Mod, Args, Options).
155 start(Name, Mod, Args, Options) ->
156 gen:start(?MODULE, nolink, Name, Mod, Args, Options).
158 start_link(Mod, Args, Options) ->
159 gen:start(?MODULE, link, Mod, Args, Options).
161 start_link(Name, Mod, Args, Options) ->
162 gen:start(?MODULE, link, Name, Mod, Args, Options).
165 %% -----------------------------------------------------------------
166 %% Make a call to a generic server.
167 %% If the server is located at another node, that node will
169 %% If the client is trapping exits and is linked server termination
170 %% is handled here (? Shall we do that here (or rely on timeouts) ?).
171 %% -----------------------------------------------------------------
172 call(Name, Request) ->
173 case catch gen:call(Name, '$gen_call', Request) of
177 exit({Reason, {?MODULE, call, [Name, Request]}})
180 call(Name, Request, Timeout) ->
181 case catch gen:call(Name, '$gen_call', Request, Timeout) of
185 exit({Reason, {?MODULE, call, [Name, Request, Timeout]}})
188 %% -----------------------------------------------------------------
189 %% Make a cast to a generic server.
190 %% -----------------------------------------------------------------
191 cast({global,Name}, Request) ->
192 catch global:send(Name, cast_msg(Request)),
194 cast({Name,Node}=Dest, Request) when is_atom(Name), is_atom(Node) ->
195 do_cast(Dest, Request);
196 cast(Dest, Request) when is_atom(Dest) ->
197 do_cast(Dest, Request);
198 cast(Dest, Request) when is_pid(Dest) ->
199 do_cast(Dest, Request).
201 do_cast(Dest, Request) ->
202 do_send(Dest, cast_msg(Request)),
205 cast_msg(Request) -> {'$gen_cast',Request}.
207 %% -----------------------------------------------------------------
208 %% Send a reply to the client.
209 %% -----------------------------------------------------------------
210 reply({To, Tag}, Reply) ->
211 catch To ! {Tag, Reply}.
213 %% -----------------------------------------------------------------
214 %% Asyncronous broadcast, returns nothing, it's just send'n prey
215 %%-----------------------------------------------------------------
216 abcast(Name, Request) when is_atom(Name) ->
217 do_abcast([node() | nodes()], Name, cast_msg(Request)).
219 abcast(Nodes, Name, Request) when is_list(Nodes), is_atom(Name) ->
220 do_abcast(Nodes, Name, cast_msg(Request)).
222 do_abcast([Node|Nodes], Name, Msg) when is_atom(Node) ->
223 do_send({Name,Node},Msg),
224 do_abcast(Nodes, Name, Msg);
225 do_abcast([], _,_) -> abcast.
227 %%% -----------------------------------------------------------------
228 %%% Make a call to servers at several nodes.
229 %%% Returns: {[Replies],[BadNodes]}
230 %%% A Timeout can be given
232 %%% A middleman process is used in case late answers arrives after
233 %%% the timeout. If they would be allowed to glog the callers message
234 %%% queue, it would probably become confused. Late answers will
235 %%% now arrive to the terminated middleman and so be discarded.
236 %%% -----------------------------------------------------------------
237 multi_call(Name, Req)
238 when is_atom(Name) ->
239 do_multi_call([node() | nodes()], Name, Req, infinity).
241 multi_call(Nodes, Name, Req)
242 when is_list(Nodes), is_atom(Name) ->
243 do_multi_call(Nodes, Name, Req, infinity).
245 multi_call(Nodes, Name, Req, infinity) ->
246 do_multi_call(Nodes, Name, Req, infinity);
247 multi_call(Nodes, Name, Req, Timeout)
248 when is_list(Nodes), is_atom(Name), is_integer(Timeout), Timeout >= 0 ->
249 do_multi_call(Nodes, Name, Req, Timeout).
252 %%-----------------------------------------------------------------
253 %% enter_loop(Mod, Options, State, <ServerName>, <TimeOut>) ->_
255 %% Description: Makes an existing process into a gen_server.
256 %% The calling process will enter the gen_server receive
257 %% loop and become a gen_server process.
258 %% The process *must* have been started using one of the
259 %% start functions in proc_lib, see proc_lib(3).
260 %% The user is responsible for any initialization of the
261 %% process, including registering a name for it.
262 %%-----------------------------------------------------------------
263 enter_loop(Mod, Options, State) ->
264 enter_loop(Mod, Options, State, self(), infinity).
266 enter_loop(Mod, Options, State, ServerName = {_, _}) ->
267 enter_loop(Mod, Options, State, ServerName, infinity);
269 enter_loop(Mod, Options, State, Timeout) ->
270 enter_loop(Mod, Options, State, self(), Timeout).
272 enter_loop(Mod, Options, State, ServerName, Timeout) ->
273 Name = get_proc_name(ServerName),
274 Parent = get_parent(),
275 Debug = debug_options(Name, Options),
277 loop(Parent, Name, State, Mod, Timeout, Queue, Debug).
279 %%%========================================================================
280 %%% Gen-callback functions
281 %%%========================================================================
283 %%% ---------------------------------------------------
284 %%% Initiate the new process.
285 %%% Register the name using the Rfunc function
286 %%% Calls the Mod:init/Args function.
287 %%% Finally an acknowledge is sent to Parent and the main
289 %%% ---------------------------------------------------
290 init_it(Starter, self, Name, Mod, Args, Options) ->
291 init_it(Starter, self(), Name, Mod, Args, Options);
292 init_it(Starter, Parent, Name, Mod, Args, Options) ->
293 Debug = debug_options(Name, Options),
295 case catch Mod:init(Args) of
297 proc_lib:init_ack(Starter, {ok, self()}),
298 loop(Parent, Name, State, Mod, infinity, Queue, Debug);
299 {ok, State, Timeout} ->
300 proc_lib:init_ack(Starter, {ok, self()}),
301 loop(Parent, Name, State, Mod, Timeout, Queue, Debug);
303 proc_lib:init_ack(Starter, {error, Reason}),
306 proc_lib:init_ack(Starter, ignore),
309 proc_lib:init_ack(Starter, {error, Reason}),
312 Error = {bad_return_value, Else},
313 proc_lib:init_ack(Starter, {error, Error}),
317 %%%========================================================================
318 %%% Internal functions
319 %%%========================================================================
320 %%% ---------------------------------------------------
322 %%% ---------------------------------------------------
323 loop(Parent, Name, State, Mod, Time, Queue, Debug) ->
325 Input -> loop(Parent, Name, State, Mod,
326 Time, queue:in(Input, Queue), Debug)
328 case queue:out(Queue) of
329 {{value, Msg}, Queue1} ->
330 process_msg(Parent, Name, State, Mod,
331 Time, Queue1, Debug, Msg);
335 loop(Parent, Name, State, Mod,
336 Time, queue:in(Input, Queue1), Debug)
338 process_msg(Parent, Name, State, Mod,
339 Time, Queue1, Debug, timeout)
344 process_msg(Parent, Name, State, Mod, Time, Queue, Debug, Msg) ->
346 {system, From, Req} ->
347 sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
348 [Name, State, Mod, Time, Queue]);
349 {'EXIT', Parent, Reason} ->
350 terminate(Reason, Name, Msg, Mod, State, Debug);
351 _Msg when Debug =:= [] ->
352 handle_msg(Msg, Parent, Name, State, Mod, Time, Queue);
354 Debug1 = sys:handle_debug(Debug, {?MODULE, print_event},
356 handle_msg(Msg, Parent, Name, State, Mod, Time, Queue, Debug1)
359 %%% ---------------------------------------------------
360 %%% Send/recive functions
361 %%% ---------------------------------------------------
362 do_send(Dest, Msg) ->
363 case catch erlang:send(Dest, Msg, [noconnect]) of
365 spawn(erlang, send, [Dest,Msg]);
370 do_multi_call(Nodes, Name, Req, infinity) ->
372 Monitors = send_nodes(Nodes, Name, Tag, Req),
373 rec_nodes(Tag, Monitors, Name, undefined);
374 do_multi_call(Nodes, Name, Req, Timeout) ->
380 %% Middleman process. Should be unsensitive to regular
381 %% exit signals. The sychronization is needed in case
382 %% the receiver would exit before the caller started
384 process_flag(trap_exit, true),
385 Mref = erlang:monitor(process, Caller),
388 Monitors = send_nodes(Nodes, Name, Tag, Req),
389 TimerId = erlang:start_timer(Timeout, self(), ok),
390 Result = rec_nodes(Tag, Monitors, Name, TimerId),
391 exit({self(),Tag,Result});
392 {'DOWN',Mref,_,_,_} ->
393 %% Caller died before sending us the go-ahead.
398 Mref = erlang:monitor(process, Receiver),
399 Receiver ! {self(),Tag},
401 {'DOWN',Mref,_,_,{Receiver,Tag,Result}} ->
403 {'DOWN',Mref,_,_,Reason} ->
404 %% The middleman code failed. Or someone did
405 %% exit(_, kill) on the middleman process => Reason==killed
409 send_nodes(Nodes, Name, Tag, Req) ->
410 send_nodes(Nodes, Name, Tag, Req, []).
412 send_nodes([Node|Tail], Name, Tag, Req, Monitors)
413 when is_atom(Node) ->
414 Monitor = start_monitor(Node, Name),
415 %% Handle non-existing names in rec_nodes.
416 catch {Name, Node} ! {'$gen_call', {self(), {Tag, Node}}, Req},
417 send_nodes(Tail, Name, Tag, Req, [Monitor | Monitors]);
418 send_nodes([_Node|Tail], Name, Tag, Req, Monitors) ->
419 %% Skip non-atom Node
420 send_nodes(Tail, Name, Tag, Req, Monitors);
421 send_nodes([], _Name, _Tag, _Req, Monitors) ->
424 %% Against old nodes:
425 %% If no reply has been delivered within 2 secs. (per node) check that
426 %% the server really exists and wait for ever for the answer.
428 %% Against contemporary nodes:
429 %% Wait for reply, server 'DOWN', or timeout from TimerId.
431 rec_nodes(Tag, Nodes, Name, TimerId) ->
432 rec_nodes(Tag, Nodes, Name, [], [], 2000, TimerId).
434 rec_nodes(Tag, [{N,R}|Tail], Name, Badnodes, Replies, Time, TimerId ) ->
436 {'DOWN', R, _, _, _} ->
437 rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, Time, TimerId);
438 {{Tag, N}, Reply} -> %% Tag is bound !!!
440 rec_nodes(Tag, Tail, Name, Badnodes,
441 [{N,Reply}|Replies], Time, TimerId);
442 {timeout, TimerId, _} ->
444 %% Collect all replies that already have arrived
445 rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
447 rec_nodes(Tag, [N|Tail], Name, Badnodes, Replies, Time, TimerId) ->
451 monitor_node(N, false),
452 rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, 2000, TimerId);
453 {{Tag, N}, Reply} -> %% Tag is bound !!!
454 receive {nodedown, N} -> ok after 0 -> ok end,
455 monitor_node(N, false),
456 rec_nodes(Tag, Tail, Name, Badnodes,
457 [{N,Reply}|Replies], 2000, TimerId);
458 {timeout, TimerId, _} ->
459 receive {nodedown, N} -> ok after 0 -> ok end,
460 monitor_node(N, false),
461 %% Collect all replies that already have arrived
462 rec_nodes_rest(Tag, Tail, Name, [N | Badnodes], Replies)
464 case rpc:call(N, erlang, whereis, [Name]) of
465 Pid when is_pid(Pid) -> % It exists try again.
466 rec_nodes(Tag, [N|Tail], Name, Badnodes,
467 Replies, infinity, TimerId);
469 receive {nodedown, N} -> ok after 0 -> ok end,
470 monitor_node(N, false),
471 rec_nodes(Tag, Tail, Name, [N|Badnodes],
472 Replies, 2000, TimerId)
475 rec_nodes(_, [], _, Badnodes, Replies, _, TimerId) ->
476 case catch erlang:cancel_timer(TimerId) of
477 false -> % It has already sent it's message
479 {timeout, TimerId, _} -> ok
483 _ -> % Timer was cancelled, or TimerId was 'undefined'
488 %% Collect all replies that already have arrived
489 rec_nodes_rest(Tag, [{N,R}|Tail], Name, Badnodes, Replies) ->
491 {'DOWN', R, _, _, _} ->
492 rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies);
493 {{Tag, N}, Reply} -> %% Tag is bound !!!
495 rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies])
498 rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
500 rec_nodes_rest(Tag, [N|Tail], Name, Badnodes, Replies) ->
504 monitor_node(N, false),
505 rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies);
506 {{Tag, N}, Reply} -> %% Tag is bound !!!
507 receive {nodedown, N} -> ok after 0 -> ok end,
508 monitor_node(N, false),
509 rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies])
511 receive {nodedown, N} -> ok after 0 -> ok end,
512 monitor_node(N, false),
513 rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
515 rec_nodes_rest(_Tag, [], _Name, Badnodes, Replies) ->
519 %%% ---------------------------------------------------
520 %%% Monitor functions
521 %%% ---------------------------------------------------
523 start_monitor(Node, Name) when is_atom(Node), is_atom(Name) ->
524 if node() =:= nonode@nohost, Node =/= nonode@nohost ->
526 self() ! {'DOWN', Ref, process, {Name, Node}, noconnection},
529 case catch erlang:monitor(process, {Name, Node}) of
532 monitor_node(Node, true),
534 Ref when is_reference(Ref) ->
539 %% Cancels a monitor started with Ref=erlang:monitor(_, _).
540 unmonitor(Ref) when is_reference(Ref) ->
541 erlang:demonitor(Ref),
543 {'DOWN', Ref, _, _, _} ->
549 %%% ---------------------------------------------------
550 %%% Message handling functions
551 %%% ---------------------------------------------------
553 dispatch({'$gen_cast', Msg}, Mod, State) ->
554 Mod:handle_cast(Msg, State);
555 dispatch(Info, Mod, State) ->
556 Mod:handle_info(Info, State).
558 handle_msg({'$gen_call', From, Msg},
559 Parent, Name, State, Mod, _Time, Queue) ->
560 case catch Mod:handle_call(Msg, From, State) of
561 {reply, Reply, NState} ->
563 loop(Parent, Name, NState, Mod, infinity, Queue, []);
564 {reply, Reply, NState, Time1} ->
566 loop(Parent, Name, NState, Mod, Time1, Queue, []);
568 loop(Parent, Name, NState, Mod, infinity, Queue, []);
569 {noreply, NState, Time1} ->
570 loop(Parent, Name, NState, Mod, Time1, Queue, []);
571 {stop, Reason, Reply, NState} ->
573 (catch terminate(Reason, Name, Msg, Mod, NState, [])),
576 Other -> handle_common_reply(Other,
577 Parent, Name, Msg, Mod, State, Queue)
580 Parent, Name, State, Mod, _Time, Queue) ->
581 Reply = (catch dispatch(Msg, Mod, State)),
582 handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Queue).
584 handle_msg({'$gen_call', From, Msg},
585 Parent, Name, State, Mod, _Time, Queue, Debug) ->
586 case catch Mod:handle_call(Msg, From, State) of
587 {reply, Reply, NState} ->
588 Debug1 = reply(Name, From, Reply, NState, Debug),
589 loop(Parent, Name, NState, Mod, infinity, Queue, Debug1);
590 {reply, Reply, NState, Time1} ->
591 Debug1 = reply(Name, From, Reply, NState, Debug),
592 loop(Parent, Name, NState, Mod, Time1, Queue, Debug1);
594 Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
596 loop(Parent, Name, NState, Mod, infinity, Queue, Debug1);
597 {noreply, NState, Time1} ->
598 Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
600 loop(Parent, Name, NState, Mod, Time1, Queue, Debug1);
601 {stop, Reason, Reply, NState} ->
603 (catch terminate(Reason, Name, Msg, Mod, NState, Debug)),
604 reply(Name, From, Reply, NState, Debug),
607 handle_common_reply(Other,
608 Parent, Name, Msg, Mod, State, Queue, Debug)
611 Parent, Name, State, Mod, _Time, Queue, Debug) ->
612 Reply = (catch dispatch(Msg, Mod, State)),
613 handle_common_reply(Reply,
614 Parent, Name, Msg, Mod, State, Queue, Debug).
616 handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Queue) ->
619 loop(Parent, Name, NState, Mod, infinity, Queue, []);
620 {noreply, NState, Time1} ->
621 loop(Parent, Name, NState, Mod, Time1, Queue, []);
622 {stop, Reason, NState} ->
623 terminate(Reason, Name, Msg, Mod, NState, []);
625 terminate(What, Name, Msg, Mod, State, []);
627 terminate({bad_return_value, Reply}, Name, Msg, Mod, State, [])
630 handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Queue, Debug) ->
633 Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
635 loop(Parent, Name, NState, Mod, infinity, Queue, Debug1);
636 {noreply, NState, Time1} ->
637 Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
639 loop(Parent, Name, NState, Mod, Time1, Queue, Debug1);
640 {stop, Reason, NState} ->
641 terminate(Reason, Name, Msg, Mod, NState, Debug);
643 terminate(What, Name, Msg, Mod, State, Debug);
645 terminate({bad_return_value, Reply}, Name, Msg, Mod, State, Debug)
648 reply(Name, {To, Tag}, Reply, State, Debug) ->
649 reply({To, Tag}, Reply),
650 sys:handle_debug(Debug, {?MODULE, print_event}, Name,
651 {out, Reply, To, State} ).
654 %%-----------------------------------------------------------------
655 %% Callback functions for system messages handling.
656 %%-----------------------------------------------------------------
657 system_continue(Parent, Debug, [Name, State, Mod, Time, Queue]) ->
658 loop(Parent, Name, State, Mod, Time, Queue, Debug).
660 system_terminate(Reason, _Parent, Debug, [Name, State, Mod, _Time, _Queue]) ->
661 terminate(Reason, Name, [], Mod, State, Debug).
663 system_code_change([Name, State, Mod, Time, Queue], _Module, OldVsn, Extra) ->
664 case catch Mod:code_change(OldVsn, State, Extra) of
665 {ok, NewState} -> {ok, [Name, NewState, Mod, Time, Queue]};
669 %%-----------------------------------------------------------------
670 %% Format debug messages. Print them as the call-back module sees
671 %% them, not as the real erlang messages. Use trace for that.
672 %%-----------------------------------------------------------------
673 print_event(Dev, {in, Msg}, Name) ->
675 {'$gen_call', {From, _Tag}, Call} ->
676 io:format(Dev, "*DBG* ~p got call ~p from ~w~n",
678 {'$gen_cast', Cast} ->
679 io:format(Dev, "*DBG* ~p got cast ~p~n",
682 io:format(Dev, "*DBG* ~p got ~p~n", [Name, Msg])
684 print_event(Dev, {out, Msg, To, State}, Name) ->
685 io:format(Dev, "*DBG* ~p sent ~p to ~w, new state ~w~n",
686 [Name, Msg, To, State]);
687 print_event(Dev, {noreply, State}, Name) ->
688 io:format(Dev, "*DBG* ~p new state ~w~n", [Name, State]);
689 print_event(Dev, Event, Name) ->
690 io:format(Dev, "*DBG* ~p dbg ~p~n", [Name, Event]).
693 %%% ---------------------------------------------------
694 %%% Terminate the server.
695 %%% ---------------------------------------------------
697 terminate(Reason, Name, Msg, Mod, State, Debug) ->
698 case catch Mod:terminate(Reason, State) of
700 error_info(R, Name, Msg, State, Debug),
709 error_info(Reason, Name, Msg, State, Debug),
714 error_info(_Reason, application_controller, _Msg, _State, _Debug) ->
715 %% OTP-5811 Don't send an error report if it's the system process
716 %% application_controller which is terminating - let init take care
719 error_info(Reason, Name, Msg, State, Debug) ->
722 {undef,[{M,F,A}|MFAs]} ->
723 case code:is_loaded(M) of
725 {'module could not be loaded',[{M,F,A}|MFAs]};
727 case erlang:function_exported(M, F, length(A)) of
731 {'function not exported',[{M,F,A}|MFAs]}
737 format("** Generic server ~p terminating \n"
738 "** Last message in was ~p~n"
739 "** When Server state == ~p~n"
740 "** Reason for termination == ~n** ~p~n",
741 [Name, Msg, State, Reason1]),
742 sys:print_log(Debug),
745 %%% ---------------------------------------------------
747 %%% ---------------------------------------------------
749 opt(Op, [{Op, Value}|_]) ->
751 opt(Op, [_|Options]) ->
756 debug_options(Name, Opts) ->
757 case opt(debug, Opts) of
758 {ok, Options} -> dbg_options(Name, Options);
759 _ -> dbg_options(Name, [])
762 dbg_options(Name, []) ->
764 case init:get_argument(generic_debug) of
770 dbg_opts(Name, Opts);
771 dbg_options(Name, Opts) ->
772 dbg_opts(Name, Opts).
774 dbg_opts(Name, Opts) ->
775 case catch sys:debug_options(Opts) of
777 format("~p: ignoring erroneous debug options - ~p~n",
784 get_proc_name(Pid) when is_pid(Pid) ->
786 get_proc_name({local, Name}) ->
787 case process_info(self(), registered_name) of
788 {registered_name, Name} ->
790 {registered_name, _Name} ->
791 exit(process_not_registered);
793 exit(process_not_registered)
795 get_proc_name({global, Name}) ->
796 case global:safe_whereis_name(Name) of
798 exit(process_not_registered_globally);
799 Pid when Pid =:= self() ->
802 exit(process_not_registered_globally)
806 case get('$ancestors') of
807 [Parent | _] when is_pid(Parent)->
809 [Parent | _] when is_atom(Parent)->
812 exit(process_was_not_started_by_proc_lib)
816 case whereis(Name) of
818 case global:safe_whereis_name(Name) of
820 exit(could_not_find_registerd_name);
828 %%-----------------------------------------------------------------
829 %% Status information
830 %%-----------------------------------------------------------------
831 format_status(Opt, StatusData) ->
832 [PDict, SysState, Parent, Debug, [Name, State, Mod, _Time, Queue]] =
834 NameTag = if is_pid(Name) ->
839 Header = lists:concat(["Status for generic server ", NameTag]),
840 Log = sys:get_debug(log, Debug, []),
842 case erlang:function_exported(Mod, format_status, 2) of
844 case catch Mod:format_status(Opt, [PDict, State]) of
845 {'EXIT', _} -> [{data, [{"State", State}]}];
849 [{data, [{"State", State}]}]
852 {data, [{"Status", SysState},
854 {"Logged events", Log},
855 {"Queued messages", queue:to_list(Queue)}]} |