src/gen_server2.erl
author Matthias Radestock <matthias@lshift.net>
Wed Jan 07 13:39:36 2009 +0000 (2009-01-07)
branchbug20088
changeset 692 0bc7488f22d8
parent 691 cec3b4a6ecbf
child 693 3b5343f4b47d
permissions -rw-r--r--
reduce impact of long message queues on selective receives
     1 %% This file is a copy of gen_server.erl from the R11B-5 Erlang/OTP
     2 %% distribution, with the following modifications:
     3 %%
     4 %% 1) the module name is gen_server2
     5 %%
     6 %%
     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.
    15 %%
    16 %% All modifications are (C) 2009 LShift Ltd.
    17 
    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/.
    23 %% 
    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
    27 %% under the License.
    28 %% 
    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.''
    32 %% 
    33 %%     $Id$
    34 %%
    35 -module(gen_server2).
    36 
    37 %%% ---------------------------------------------------
    38 %%%
    39 %%% The idea behind THIS server is that the user module
    40 %%% provides (different) functions to handle different
    41 %%% kind of inputs. 
    42 %%% If the Parent process terminates the Module:terminate/2
    43 %%% function is called.
    44 %%%
    45 %%% The user module should export:
    46 %%%
    47 %%%   init(Args)  
    48 %%%     ==> {ok, State}
    49 %%%         {ok, State, Timeout}
    50 %%%         ignore
    51 %%%         {stop, Reason}
    52 %%%
    53 %%%   handle_call(Msg, {From, Tag}, State)
    54 %%%
    55 %%%    ==> {reply, Reply, State}
    56 %%%        {reply, Reply, State, Timeout}
    57 %%%        {noreply, State}
    58 %%%        {noreply, State, Timeout}
    59 %%%        {stop, Reason, Reply, State}  
    60 %%%              Reason = normal | shutdown | Term terminate(State) is called
    61 %%%
    62 %%%   handle_cast(Msg, State)
    63 %%%
    64 %%%    ==> {noreply, State}
    65 %%%        {noreply, State, Timeout}
    66 %%%        {stop, Reason, State} 
    67 %%%              Reason = normal | shutdown | Term terminate(State) is called
    68 %%%
    69 %%%   handle_info(Info, State) Info is e.g. {'EXIT', P, R}, {nodedown, N}, ...
    70 %%%
    71 %%%    ==> {noreply, State}
    72 %%%        {noreply, State, Timeout}
    73 %%%        {stop, Reason, State} 
    74 %%%              Reason = normal | shutdown | Term, terminate(State) is called
    75 %%%
    76 %%%   terminate(Reason, State) Let the user module clean up
    77 %%%        always called when server terminates
    78 %%%
    79 %%%    ==> ok
    80 %%%
    81 %%%
    82 %%% The work flow (of the server) can be described as follows:
    83 %%%
    84 %%%   User module                          Generic
    85 %%%   -----------                          -------
    86 %%%     start            ----->             start
    87 %%%     init             <-----              .
    88 %%%
    89 %%%                                         loop
    90 %%%     handle_call      <-----              .
    91 %%%                      ----->             reply
    92 %%%
    93 %%%     handle_cast      <-----              .
    94 %%%
    95 %%%     handle_info      <-----              .
    96 %%%
    97 %%%     terminate        <-----              .
    98 %%%
    99 %%%                      ----->             reply
   100 %%%
   101 %%%
   102 %%% ---------------------------------------------------
   103 
   104 %% API
   105 -export([start/3, start/4,
   106 	 start_link/3, start_link/4,
   107 	 call/2, call/3,
   108 	 cast/2, reply/2,
   109 	 abcast/2, abcast/3,
   110 	 multi_call/2, multi_call/3, multi_call/4,
   111 	 enter_loop/3, enter_loop/4, enter_loop/5]).
   112 
   113 -export([behaviour_info/1]).
   114 
   115 %% System exports
   116 -export([system_continue/3,
   117 	 system_terminate/4,
   118 	 system_code_change/4,
   119 	 format_status/2]).
   120 
   121 %% Internal exports
   122 -export([init_it/6, print_event/3]).
   123 
   124 -import(error_logger, [format/2]).
   125 
   126 %%%=========================================================================
   127 %%%  API
   128 %%%=========================================================================
   129 
   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) ->
   134     undefined.
   135 
   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}} |
   150 %%%          {error, Reason}
   151 %%% -----------------------------------------------------------------
   152 start(Mod, Args, Options) ->
   153     gen:start(?MODULE, nolink, Mod, Args, Options).
   154 
   155 start(Name, Mod, Args, Options) ->
   156     gen:start(?MODULE, nolink, Name, Mod, Args, Options).
   157 
   158 start_link(Mod, Args, Options) ->
   159     gen:start(?MODULE, link, Mod, Args, Options).
   160 
   161 start_link(Name, Mod, Args, Options) ->
   162     gen:start(?MODULE, link, Name, Mod, Args, Options).
   163 
   164 
   165 %% -----------------------------------------------------------------
   166 %% Make a call to a generic server.
   167 %% If the server is located at another node, that node will
   168 %% be monitored.
   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
   174 	{ok,Res} ->
   175 	    Res;
   176 	{'EXIT',Reason} ->
   177 	    exit({Reason, {?MODULE, call, [Name, Request]}})
   178     end.
   179 
   180 call(Name, Request, Timeout) ->
   181     case catch gen:call(Name, '$gen_call', Request, Timeout) of
   182 	{ok,Res} ->
   183 	    Res;
   184 	{'EXIT',Reason} ->
   185 	    exit({Reason, {?MODULE, call, [Name, Request, Timeout]}})
   186     end.
   187 
   188 %% -----------------------------------------------------------------
   189 %% Make a cast to a generic server.
   190 %% -----------------------------------------------------------------
   191 cast({global,Name}, Request) ->
   192     catch global:send(Name, cast_msg(Request)),
   193     ok;
   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).
   200 
   201 do_cast(Dest, Request) -> 
   202     do_send(Dest, cast_msg(Request)),
   203     ok.
   204     
   205 cast_msg(Request) -> {'$gen_cast',Request}.
   206 
   207 %% -----------------------------------------------------------------
   208 %% Send a reply to the client.
   209 %% -----------------------------------------------------------------
   210 reply({To, Tag}, Reply) ->
   211     catch To ! {Tag, Reply}.
   212 
   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)).
   218 
   219 abcast(Nodes, Name, Request) when is_list(Nodes), is_atom(Name) ->
   220     do_abcast(Nodes, Name, cast_msg(Request)).
   221 
   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.
   226 
   227 %%% -----------------------------------------------------------------
   228 %%% Make a call to servers at several nodes.
   229 %%% Returns: {[Replies],[BadNodes]}
   230 %%% A Timeout can be given
   231 %%% 
   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).
   240 
   241 multi_call(Nodes, Name, Req) 
   242   when is_list(Nodes), is_atom(Name) ->
   243     do_multi_call(Nodes, Name, Req, infinity).
   244 
   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).
   250 
   251 
   252 %%-----------------------------------------------------------------
   253 %% enter_loop(Mod, Options, State, <ServerName>, <TimeOut>) ->_ 
   254 %%   
   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).
   265 
   266 enter_loop(Mod, Options, State, ServerName = {_, _}) ->
   267     enter_loop(Mod, Options, State, ServerName, infinity);
   268 
   269 enter_loop(Mod, Options, State, Timeout) ->
   270     enter_loop(Mod, Options, State, self(), Timeout).
   271 
   272 enter_loop(Mod, Options, State, ServerName, Timeout) ->
   273     Name = get_proc_name(ServerName),
   274     Parent = get_parent(),
   275     Debug = debug_options(Name, Options),
   276     Queue = queue:new(),
   277     loop(Parent, Name, State, Mod, Timeout, Queue, Debug).
   278 
   279 %%%========================================================================
   280 %%% Gen-callback functions
   281 %%%========================================================================
   282 
   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
   288 %%% loop is entered.
   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),
   294     Queue = queue:new(),
   295     case catch Mod:init(Args) of
   296 	{ok, State} ->
   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);
   302 	{stop, Reason} ->
   303 	    proc_lib:init_ack(Starter, {error, Reason}),
   304 	    exit(Reason);
   305 	ignore ->
   306 	    proc_lib:init_ack(Starter, ignore),
   307 	    exit(normal);
   308 	{'EXIT', Reason} ->
   309 	    proc_lib:init_ack(Starter, {error, Reason}),
   310 	    exit(Reason);
   311 	Else ->
   312 	    Error = {bad_return_value, Else},
   313 	    proc_lib:init_ack(Starter, {error, Error}),
   314 	    exit(Error)
   315     end.
   316 
   317 %%%========================================================================
   318 %%% Internal functions
   319 %%%========================================================================
   320 %%% ---------------------------------------------------
   321 %%% The MAIN loop.
   322 %%% ---------------------------------------------------
   323 loop(Parent, Name, State, Mod, Time, Queue, Debug) ->
   324     receive
   325         Input -> loop(Parent, Name, State, Mod,
   326                       Time, queue:in(Input, Queue), Debug)
   327     after 0 ->
   328             case queue:out(Queue) of
   329                 {{value, Msg}, Queue1} ->
   330                     process_msg(Parent, Name, State, Mod,
   331                                 Time, Queue1, Debug, Msg);
   332                 {empty, Queue1} ->
   333                     receive
   334                         Input ->
   335                             loop(Parent, Name, State, Mod,
   336                                  Time, queue:in(Input, Queue1), Debug)
   337                     after Time ->
   338                             process_msg(Parent, Name, State, Mod,
   339                                         Time, Queue1, Debug, timeout)
   340                     end
   341             end
   342     end.
   343                     
   344 process_msg(Parent, Name, State, Mod, Time, Queue, Debug, Msg) ->
   345     case Msg of
   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);
   353 	_Msg ->
   354 	    Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, 
   355 				      Name, {in, Msg}),
   356 	    handle_msg(Msg, Parent, Name, State, Mod, Time, Queue, Debug1)
   357     end.
   358 
   359 %%% ---------------------------------------------------
   360 %%% Send/recive functions
   361 %%% ---------------------------------------------------
   362 do_send(Dest, Msg) ->
   363     case catch erlang:send(Dest, Msg, [noconnect]) of
   364 	noconnect ->
   365 	    spawn(erlang, send, [Dest,Msg]);
   366 	Other ->
   367 	    Other
   368     end.
   369 
   370 do_multi_call(Nodes, Name, Req, infinity) ->
   371     Tag = make_ref(),
   372     Monitors = send_nodes(Nodes, Name, Tag, Req),
   373     rec_nodes(Tag, Monitors, Name, undefined);
   374 do_multi_call(Nodes, Name, Req, Timeout) ->
   375     Tag = make_ref(),
   376     Caller = self(),
   377     Receiver =
   378 	spawn(
   379 	  fun() ->
   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
   383 		  %% the monitor.
   384 		  process_flag(trap_exit, true),
   385 		  Mref = erlang:monitor(process, Caller),
   386 		  receive
   387 		      {Caller,Tag} ->
   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.
   394 			  %% Give up silently.
   395 			  exit(normal)
   396 		  end
   397 	  end),
   398     Mref = erlang:monitor(process, Receiver),
   399     Receiver ! {self(),Tag},
   400     receive
   401 	{'DOWN',Mref,_,_,{Receiver,Tag,Result}} ->
   402 	    Result;
   403 	{'DOWN',Mref,_,_,Reason} ->
   404 	    %% The middleman code failed. Or someone did 
   405 	    %% exit(_, kill) on the middleman process => Reason==killed
   406 	    exit(Reason)
   407     end.
   408 
   409 send_nodes(Nodes, Name, Tag, Req) ->
   410     send_nodes(Nodes, Name, Tag, Req, []).
   411 
   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) -> 
   422     Monitors.
   423 
   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.
   427 %%
   428 %% Against contemporary nodes:
   429 %% Wait for reply, server 'DOWN', or timeout from TimerId.
   430 
   431 rec_nodes(Tag, Nodes, Name, TimerId) -> 
   432     rec_nodes(Tag, Nodes, Name, [], [], 2000, TimerId).
   433 
   434 rec_nodes(Tag, [{N,R}|Tail], Name, Badnodes, Replies, Time, TimerId ) ->
   435     receive
   436 	{'DOWN', R, _, _, _} ->
   437 	    rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, Time, TimerId);
   438 	{{Tag, N}, Reply} ->  %% Tag is bound !!!
   439 	    unmonitor(R), 
   440 	    rec_nodes(Tag, Tail, Name, Badnodes, 
   441 		      [{N,Reply}|Replies], Time, TimerId);
   442 	{timeout, TimerId, _} ->	
   443 	    unmonitor(R),
   444 	    %% Collect all replies that already have arrived
   445 	    rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
   446     end;
   447 rec_nodes(Tag, [N|Tail], Name, Badnodes, Replies, Time, TimerId) ->
   448     %% R6 node
   449     receive
   450 	{nodedown, N} ->
   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)
   463     after Time ->
   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);
   468 		_ -> % badnode
   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)
   473 	    end
   474     end;
   475 rec_nodes(_, [], _, Badnodes, Replies, _, TimerId) ->
   476     case catch erlang:cancel_timer(TimerId) of
   477 	false ->  % It has already sent it's message
   478 	    receive
   479 		{timeout, TimerId, _} -> ok
   480 	    after 0 ->
   481 		    ok
   482 	    end;
   483 	_ -> % Timer was cancelled, or TimerId was 'undefined'
   484 	    ok
   485     end,
   486     {Replies, Badnodes}.
   487 
   488 %% Collect all replies that already have arrived
   489 rec_nodes_rest(Tag, [{N,R}|Tail], Name, Badnodes, Replies) ->
   490     receive
   491 	{'DOWN', R, _, _, _} ->
   492 	    rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies);
   493 	{{Tag, N}, Reply} -> %% Tag is bound !!!
   494 	    unmonitor(R),
   495 	    rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies])
   496     after 0 ->
   497 	    unmonitor(R),
   498 	    rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
   499     end;
   500 rec_nodes_rest(Tag, [N|Tail], Name, Badnodes, Replies) ->
   501     %% R6 node
   502     receive
   503 	{nodedown, N} ->
   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])
   510     after 0 ->
   511 	    receive {nodedown, N} -> ok after 0 -> ok end,
   512 	    monitor_node(N, false),
   513 	    rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
   514     end;
   515 rec_nodes_rest(_Tag, [], _Name, Badnodes, Replies) ->
   516     {Replies, Badnodes}.
   517 
   518 
   519 %%% ---------------------------------------------------
   520 %%% Monitor functions
   521 %%% ---------------------------------------------------
   522 
   523 start_monitor(Node, Name) when is_atom(Node), is_atom(Name) ->
   524     if node() =:= nonode@nohost, Node =/= nonode@nohost ->
   525 	    Ref = make_ref(),
   526 	    self() ! {'DOWN', Ref, process, {Name, Node}, noconnection},
   527 	    {Node, Ref};
   528        true ->
   529 	    case catch erlang:monitor(process, {Name, Node}) of
   530 		{'EXIT', _} ->
   531 		    %% Remote node is R6
   532 		    monitor_node(Node, true),
   533 		    Node;
   534 		Ref when is_reference(Ref) ->
   535 		    {Node, Ref}
   536 	    end
   537     end.
   538 
   539 %% Cancels a monitor started with Ref=erlang:monitor(_, _).
   540 unmonitor(Ref) when is_reference(Ref) ->
   541     erlang:demonitor(Ref),
   542     receive
   543 	{'DOWN', Ref, _, _, _} ->
   544 	    true
   545     after 0 ->
   546 	    true
   547     end.
   548 
   549 %%% ---------------------------------------------------
   550 %%% Message handling functions
   551 %%% ---------------------------------------------------
   552 
   553 dispatch({'$gen_cast', Msg}, Mod, State) ->
   554     Mod:handle_cast(Msg, State);
   555 dispatch(Info, Mod, State) ->
   556     Mod:handle_info(Info, State).
   557 
   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} ->
   562 	    reply(From, Reply),
   563 	    loop(Parent, Name, NState, Mod, infinity, Queue, []);
   564 	{reply, Reply, NState, Time1} ->
   565 	    reply(From, Reply),
   566 	    loop(Parent, Name, NState, Mod, Time1, Queue, []);
   567 	{noreply, NState} ->
   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} ->
   572 	    {'EXIT', R} = 
   573 		(catch terminate(Reason, Name, Msg, Mod, NState, [])),
   574 	    reply(From, Reply),
   575 	    exit(R);
   576 	Other -> handle_common_reply(Other,
   577                                      Parent, Name, Msg, Mod, State, Queue)
   578     end;
   579 handle_msg(Msg,
   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).
   583 
   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);
   593 	{noreply, NState} ->
   594 	    Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
   595 				      {noreply, NState}),
   596 	    loop(Parent, Name, NState, Mod, infinity, Queue, Debug1);
   597 	{noreply, NState, Time1} ->
   598 	    Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
   599 				      {noreply, NState}),
   600 	    loop(Parent, Name, NState, Mod, Time1, Queue, Debug1);
   601 	{stop, Reason, Reply, NState} ->
   602 	    {'EXIT', R} = 
   603 		(catch terminate(Reason, Name, Msg, Mod, NState, Debug)),
   604 	    reply(Name, From, Reply, NState, Debug),
   605 	    exit(R);
   606 	Other ->
   607 	    handle_common_reply(Other,
   608                                 Parent, Name, Msg, Mod, State, Queue, Debug)
   609     end;
   610 handle_msg(Msg,
   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).
   615 
   616 handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Queue) ->
   617     case Reply of
   618 	{noreply, NState} ->
   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, []);
   624 	{'EXIT', What} ->
   625 	    terminate(What, Name, Msg, Mod, State, []);
   626 	_ ->
   627 	    terminate({bad_return_value, Reply}, Name, Msg, Mod, State, [])
   628     end.
   629 
   630 handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Queue, Debug) ->
   631     case Reply of
   632 	{noreply, NState} ->
   633 	    Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
   634 				      {noreply, NState}),
   635 	    loop(Parent, Name, NState, Mod, infinity, Queue, Debug1);
   636 	{noreply, NState, Time1} ->
   637 	    Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
   638 				      {noreply, NState}),
   639 	    loop(Parent, Name, NState, Mod, Time1, Queue, Debug1);
   640 	{stop, Reason, NState} ->
   641 	    terminate(Reason, Name, Msg, Mod, NState, Debug);
   642 	{'EXIT', What} ->
   643 	    terminate(What, Name, Msg, Mod, State, Debug);
   644 	_ ->
   645 	    terminate({bad_return_value, Reply}, Name, Msg, Mod, State, Debug)
   646     end.
   647 
   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} ).
   652 
   653 
   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).
   659 
   660 system_terminate(Reason, _Parent, Debug, [Name, State, Mod, _Time, _Queue]) ->
   661     terminate(Reason, Name, [], Mod, State, Debug).
   662 
   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]};
   666 	Else -> Else
   667     end.
   668 
   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) ->
   674     case Msg of
   675 	{'$gen_call', {From, _Tag}, Call} ->
   676 	    io:format(Dev, "*DBG* ~p got call ~p from ~w~n",
   677 		      [Name, Call, From]);
   678 	{'$gen_cast', Cast} ->
   679 	    io:format(Dev, "*DBG* ~p got cast ~p~n",
   680 		      [Name, Cast]);
   681 	_ ->
   682 	    io:format(Dev, "*DBG* ~p got ~p~n", [Name, Msg])
   683     end;
   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]).
   691 
   692 
   693 %%% ---------------------------------------------------
   694 %%% Terminate the server.
   695 %%% ---------------------------------------------------
   696 
   697 terminate(Reason, Name, Msg, Mod, State, Debug) ->
   698     case catch Mod:terminate(Reason, State) of
   699 	{'EXIT', R} ->
   700 	    error_info(R, Name, Msg, State, Debug),
   701 	    exit(R);
   702 	_ ->
   703 	    case Reason of
   704 		normal ->
   705 		    exit(normal);
   706 		shutdown ->
   707 		    exit(shutdown);
   708 		_ ->
   709 		    error_info(Reason, Name, Msg, State, Debug),
   710 		    exit(Reason)
   711 	    end
   712     end.
   713 
   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
   717     %% of it instead
   718     ok;
   719 error_info(Reason, Name, Msg, State, Debug) ->
   720     Reason1 = 
   721 	case Reason of
   722 	    {undef,[{M,F,A}|MFAs]} ->
   723 		case code:is_loaded(M) of
   724 		    false ->
   725 			{'module could not be loaded',[{M,F,A}|MFAs]};
   726 		    _ ->
   727 			case erlang:function_exported(M, F, length(A)) of
   728 			    true ->
   729 				Reason;
   730 			    false ->
   731 				{'function not exported',[{M,F,A}|MFAs]}
   732 			end
   733 		end;
   734 	    _ ->
   735 		Reason
   736 	end,    
   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),
   743     ok.
   744 
   745 %%% ---------------------------------------------------
   746 %%% Misc. functions.
   747 %%% ---------------------------------------------------
   748 
   749 opt(Op, [{Op, Value}|_]) ->
   750     {ok, Value};
   751 opt(Op, [_|Options]) ->
   752     opt(Op, Options);
   753 opt(_, []) ->
   754     false.
   755 
   756 debug_options(Name, Opts) ->
   757     case opt(debug, Opts) of
   758 	{ok, Options} -> dbg_options(Name, Options);
   759 	_ -> dbg_options(Name, [])
   760     end.
   761 
   762 dbg_options(Name, []) ->
   763     Opts = 
   764 	case init:get_argument(generic_debug) of
   765 	    error ->
   766 		[];
   767 	    _ ->
   768 		[log, statistics]
   769 	end,
   770     dbg_opts(Name, Opts);
   771 dbg_options(Name, Opts) ->
   772     dbg_opts(Name, Opts).
   773 
   774 dbg_opts(Name, Opts) ->
   775     case catch sys:debug_options(Opts) of
   776 	{'EXIT',_} ->
   777 	    format("~p: ignoring erroneous debug options - ~p~n",
   778 		   [Name, Opts]),
   779 	    [];
   780 	Dbg ->
   781 	    Dbg
   782     end.
   783 
   784 get_proc_name(Pid) when is_pid(Pid) ->
   785     Pid;
   786 get_proc_name({local, Name}) ->
   787     case process_info(self(), registered_name) of
   788 	{registered_name, Name} ->
   789 	    Name;
   790 	{registered_name, _Name} ->
   791 	    exit(process_not_registered);
   792 	[] ->
   793 	    exit(process_not_registered)
   794     end;    
   795 get_proc_name({global, Name}) ->
   796     case global:safe_whereis_name(Name) of
   797 	undefined ->
   798 	    exit(process_not_registered_globally);
   799 	Pid when Pid =:= self() ->
   800 	    Name;
   801 	_Pid ->
   802 	    exit(process_not_registered_globally)
   803     end.
   804 
   805 get_parent() ->
   806     case get('$ancestors') of
   807 	[Parent | _] when is_pid(Parent)->
   808             Parent;
   809         [Parent | _] when is_atom(Parent)->
   810             name_to_pid(Parent);
   811 	_ ->
   812 	    exit(process_was_not_started_by_proc_lib)
   813     end.
   814 
   815 name_to_pid(Name) ->
   816     case whereis(Name) of
   817 	undefined ->
   818 	    case global:safe_whereis_name(Name) of
   819 		undefined ->
   820 		    exit(could_not_find_registerd_name);
   821 		Pid ->
   822 		    Pid
   823 	    end;
   824 	Pid ->
   825 	    Pid
   826     end.
   827 
   828 %%-----------------------------------------------------------------
   829 %% Status information
   830 %%-----------------------------------------------------------------
   831 format_status(Opt, StatusData) ->
   832     [PDict, SysState, Parent, Debug, [Name, State, Mod, _Time, Queue]] =
   833         StatusData,
   834     NameTag = if is_pid(Name) ->
   835 		      pid_to_list(Name);
   836 		 is_atom(Name) ->
   837 		      Name
   838 	      end,
   839     Header = lists:concat(["Status for generic server ", NameTag]),
   840     Log = sys:get_debug(log, Debug, []),
   841     Specfic = 
   842 	case erlang:function_exported(Mod, format_status, 2) of
   843 	    true ->
   844 		case catch Mod:format_status(Opt, [PDict, State]) of
   845 		    {'EXIT', _} -> [{data, [{"State", State}]}];
   846 		    Else -> Else
   847 		end;
   848 	    _ ->
   849 		[{data, [{"State", State}]}]
   850 	end,
   851     [{header, Header},
   852      {data, [{"Status", SysState},
   853 	     {"Parent", Parent},
   854 	     {"Logged events", Log},
   855              {"Queued messages", queue:to_list(Queue)}]} |
   856      Specfic].