src/gen_server2.erl
author Matthias Radestock <matthias@lshift.net>
Fri Feb 06 14:20:11 2009 +0000 (2009-02-06)
branchbug20345
changeset 831 b95f2fd4e3f6
parent 692 0bc7488f22d8
child 924 421981ef4e8d
permissions -rw-r--r--
also prevent path expansion in rabbit-multi
matthias@691
     1
%% This file is a copy of gen_server.erl from the R11B-5 Erlang/OTP
matthias@691
     2
%% distribution, with the following modifications:
matthias@691
     3
%%
matthias@691
     4
%% 1) the module name is gen_server2
matthias@691
     5
%%
matthias@692
     6
%% 2) more efficient handling of selective receives in callbacks
matthias@692
     7
%% gen_server2 processes drain their message queue into an internal
matthias@692
     8
%% buffer before invoking any callback module functions. Messages are
matthias@692
     9
%% dequeued from the buffer for processing. Thus the effective message
matthias@692
    10
%% queue of a gen_server2 process is the concatenation of the internal
matthias@692
    11
%% buffer and the real message queue.
matthias@692
    12
%% As a result of the draining, any selective receive invoked inside a
matthias@692
    13
%% callback is less likely to have to scan a large message queue.
matthias@692
    14
%%
matthias@693
    15
%% 3) gen_server2:cast is guaranteed to be order-preserving
matthias@693
    16
%% The original code could reorder messages when communicating with a
matthias@693
    17
%% process on a remote node that was not currently connected.
matthias@693
    18
%%
matthias@691
    19
%% All modifications are (C) 2009 LShift Ltd.
matthias@691
    20
matthias@691
    21
%% ``The contents of this file are subject to the Erlang Public License,
matthias@691
    22
%% Version 1.1, (the "License"); you may not use this file except in
matthias@691
    23
%% compliance with the License. You should have received a copy of the
matthias@691
    24
%% Erlang Public License along with this software. If not, it can be
matthias@691
    25
%% retrieved via the world wide web at http://www.erlang.org/.
matthias@691
    26
%% 
matthias@691
    27
%% Software distributed under the License is distributed on an "AS IS"
matthias@691
    28
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
matthias@691
    29
%% the License for the specific language governing rights and limitations
matthias@691
    30
%% under the License.
matthias@691
    31
%% 
matthias@691
    32
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
matthias@691
    33
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
matthias@691
    34
%% AB. All Rights Reserved.''
matthias@691
    35
%% 
matthias@691
    36
%%     $Id$
matthias@691
    37
%%
matthias@691
    38
-module(gen_server2).
matthias@691
    39
matthias@691
    40
%%% ---------------------------------------------------
matthias@691
    41
%%%
matthias@691
    42
%%% The idea behind THIS server is that the user module
matthias@691
    43
%%% provides (different) functions to handle different
matthias@691
    44
%%% kind of inputs. 
matthias@691
    45
%%% If the Parent process terminates the Module:terminate/2
matthias@691
    46
%%% function is called.
matthias@691
    47
%%%
matthias@691
    48
%%% The user module should export:
matthias@691
    49
%%%
matthias@691
    50
%%%   init(Args)  
matthias@691
    51
%%%     ==> {ok, State}
matthias@691
    52
%%%         {ok, State, Timeout}
matthias@691
    53
%%%         ignore
matthias@691
    54
%%%         {stop, Reason}
matthias@691
    55
%%%
matthias@691
    56
%%%   handle_call(Msg, {From, Tag}, State)
matthias@691
    57
%%%
matthias@691
    58
%%%    ==> {reply, Reply, State}
matthias@691
    59
%%%        {reply, Reply, State, Timeout}
matthias@691
    60
%%%        {noreply, State}
matthias@691
    61
%%%        {noreply, State, Timeout}
matthias@691
    62
%%%        {stop, Reason, Reply, State}  
matthias@691
    63
%%%              Reason = normal | shutdown | Term terminate(State) is called
matthias@691
    64
%%%
matthias@691
    65
%%%   handle_cast(Msg, State)
matthias@691
    66
%%%
matthias@691
    67
%%%    ==> {noreply, State}
matthias@691
    68
%%%        {noreply, State, Timeout}
matthias@691
    69
%%%        {stop, Reason, State} 
matthias@691
    70
%%%              Reason = normal | shutdown | Term terminate(State) is called
matthias@691
    71
%%%
matthias@691
    72
%%%   handle_info(Info, State) Info is e.g. {'EXIT', P, R}, {nodedown, N}, ...
matthias@691
    73
%%%
matthias@691
    74
%%%    ==> {noreply, State}
matthias@691
    75
%%%        {noreply, State, Timeout}
matthias@691
    76
%%%        {stop, Reason, State} 
matthias@691
    77
%%%              Reason = normal | shutdown | Term, terminate(State) is called
matthias@691
    78
%%%
matthias@691
    79
%%%   terminate(Reason, State) Let the user module clean up
matthias@691
    80
%%%        always called when server terminates
matthias@691
    81
%%%
matthias@691
    82
%%%    ==> ok
matthias@691
    83
%%%
matthias@691
    84
%%%
matthias@691
    85
%%% The work flow (of the server) can be described as follows:
matthias@691
    86
%%%
matthias@691
    87
%%%   User module                          Generic
matthias@691
    88
%%%   -----------                          -------
matthias@691
    89
%%%     start            ----->             start
matthias@691
    90
%%%     init             <-----              .
matthias@691
    91
%%%
matthias@691
    92
%%%                                         loop
matthias@691
    93
%%%     handle_call      <-----              .
matthias@691
    94
%%%                      ----->             reply
matthias@691
    95
%%%
matthias@691
    96
%%%     handle_cast      <-----              .
matthias@691
    97
%%%
matthias@691
    98
%%%     handle_info      <-----              .
matthias@691
    99
%%%
matthias@691
   100
%%%     terminate        <-----              .
matthias@691
   101
%%%
matthias@691
   102
%%%                      ----->             reply
matthias@691
   103
%%%
matthias@691
   104
%%%
matthias@691
   105
%%% ---------------------------------------------------
matthias@691
   106
matthias@691
   107
%% API
matthias@691
   108
-export([start/3, start/4,
matthias@691
   109
	 start_link/3, start_link/4,
matthias@691
   110
	 call/2, call/3,
matthias@691
   111
	 cast/2, reply/2,
matthias@691
   112
	 abcast/2, abcast/3,
matthias@691
   113
	 multi_call/2, multi_call/3, multi_call/4,
matthias@691
   114
	 enter_loop/3, enter_loop/4, enter_loop/5]).
matthias@691
   115
matthias@691
   116
-export([behaviour_info/1]).
matthias@691
   117
matthias@691
   118
%% System exports
matthias@691
   119
-export([system_continue/3,
matthias@691
   120
	 system_terminate/4,
matthias@691
   121
	 system_code_change/4,
matthias@691
   122
	 format_status/2]).
matthias@691
   123
matthias@691
   124
%% Internal exports
matthias@691
   125
-export([init_it/6, print_event/3]).
matthias@691
   126
matthias@691
   127
-import(error_logger, [format/2]).
matthias@691
   128
matthias@691
   129
%%%=========================================================================
matthias@691
   130
%%%  API
matthias@691
   131
%%%=========================================================================
matthias@691
   132
matthias@691
   133
behaviour_info(callbacks) ->
matthias@691
   134
    [{init,1},{handle_call,3},{handle_cast,2},{handle_info,2},
matthias@691
   135
     {terminate,2},{code_change,3}];
matthias@691
   136
behaviour_info(_Other) ->
matthias@691
   137
    undefined.
matthias@691
   138
matthias@691
   139
%%%  -----------------------------------------------------------------
matthias@691
   140
%%% Starts a generic server.
matthias@691
   141
%%% start(Mod, Args, Options)
matthias@691
   142
%%% start(Name, Mod, Args, Options)
matthias@691
   143
%%% start_link(Mod, Args, Options)
matthias@691
   144
%%% start_link(Name, Mod, Args, Options) where:
matthias@691
   145
%%%    Name ::= {local, atom()} | {global, atom()}
matthias@691
   146
%%%    Mod  ::= atom(), callback module implementing the 'real' server
matthias@691
   147
%%%    Args ::= term(), init arguments (to Mod:init/1)
matthias@691
   148
%%%    Options ::= [{timeout, Timeout} | {debug, [Flag]}]
matthias@691
   149
%%%      Flag ::= trace | log | {logfile, File} | statistics | debug
matthias@691
   150
%%%          (debug == log && statistics)
matthias@691
   151
%%% Returns: {ok, Pid} |
matthias@691
   152
%%%          {error, {already_started, Pid}} |
matthias@691
   153
%%%          {error, Reason}
matthias@691
   154
%%% -----------------------------------------------------------------
matthias@691
   155
start(Mod, Args, Options) ->
matthias@691
   156
    gen:start(?MODULE, nolink, Mod, Args, Options).
matthias@691
   157
matthias@691
   158
start(Name, Mod, Args, Options) ->
matthias@691
   159
    gen:start(?MODULE, nolink, Name, Mod, Args, Options).
matthias@691
   160
matthias@691
   161
start_link(Mod, Args, Options) ->
matthias@691
   162
    gen:start(?MODULE, link, Mod, Args, Options).
matthias@691
   163
matthias@691
   164
start_link(Name, Mod, Args, Options) ->
matthias@691
   165
    gen:start(?MODULE, link, Name, Mod, Args, Options).
matthias@691
   166
matthias@691
   167
matthias@691
   168
%% -----------------------------------------------------------------
matthias@691
   169
%% Make a call to a generic server.
matthias@691
   170
%% If the server is located at another node, that node will
matthias@691
   171
%% be monitored.
matthias@691
   172
%% If the client is trapping exits and is linked server termination
matthias@691
   173
%% is handled here (? Shall we do that here (or rely on timeouts) ?).
matthias@691
   174
%% ----------------------------------------------------------------- 
matthias@691
   175
call(Name, Request) ->
matthias@691
   176
    case catch gen:call(Name, '$gen_call', Request) of
matthias@691
   177
	{ok,Res} ->
matthias@691
   178
	    Res;
matthias@691
   179
	{'EXIT',Reason} ->
matthias@691
   180
	    exit({Reason, {?MODULE, call, [Name, Request]}})
matthias@691
   181
    end.
matthias@691
   182
matthias@691
   183
call(Name, Request, Timeout) ->
matthias@691
   184
    case catch gen:call(Name, '$gen_call', Request, Timeout) of
matthias@691
   185
	{ok,Res} ->
matthias@691
   186
	    Res;
matthias@691
   187
	{'EXIT',Reason} ->
matthias@691
   188
	    exit({Reason, {?MODULE, call, [Name, Request, Timeout]}})
matthias@691
   189
    end.
matthias@691
   190
matthias@691
   191
%% -----------------------------------------------------------------
matthias@691
   192
%% Make a cast to a generic server.
matthias@691
   193
%% -----------------------------------------------------------------
matthias@691
   194
cast({global,Name}, Request) ->
matthias@691
   195
    catch global:send(Name, cast_msg(Request)),
matthias@691
   196
    ok;
matthias@691
   197
cast({Name,Node}=Dest, Request) when is_atom(Name), is_atom(Node) -> 
matthias@691
   198
    do_cast(Dest, Request);
matthias@691
   199
cast(Dest, Request) when is_atom(Dest) ->
matthias@691
   200
    do_cast(Dest, Request);
matthias@691
   201
cast(Dest, Request) when is_pid(Dest) ->
matthias@691
   202
    do_cast(Dest, Request).
matthias@691
   203
matthias@691
   204
do_cast(Dest, Request) -> 
matthias@691
   205
    do_send(Dest, cast_msg(Request)),
matthias@691
   206
    ok.
matthias@691
   207
    
matthias@691
   208
cast_msg(Request) -> {'$gen_cast',Request}.
matthias@691
   209
matthias@691
   210
%% -----------------------------------------------------------------
matthias@691
   211
%% Send a reply to the client.
matthias@691
   212
%% -----------------------------------------------------------------
matthias@691
   213
reply({To, Tag}, Reply) ->
matthias@691
   214
    catch To ! {Tag, Reply}.
matthias@691
   215
matthias@691
   216
%% ----------------------------------------------------------------- 
matthias@691
   217
%% Asyncronous broadcast, returns nothing, it's just send'n prey
matthias@691
   218
%%-----------------------------------------------------------------  
matthias@691
   219
abcast(Name, Request) when is_atom(Name) ->
matthias@691
   220
    do_abcast([node() | nodes()], Name, cast_msg(Request)).
matthias@691
   221
matthias@691
   222
abcast(Nodes, Name, Request) when is_list(Nodes), is_atom(Name) ->
matthias@691
   223
    do_abcast(Nodes, Name, cast_msg(Request)).
matthias@691
   224
matthias@691
   225
do_abcast([Node|Nodes], Name, Msg) when is_atom(Node) ->
matthias@691
   226
    do_send({Name,Node},Msg),
matthias@691
   227
    do_abcast(Nodes, Name, Msg);
matthias@691
   228
do_abcast([], _,_) -> abcast.
matthias@691
   229
matthias@691
   230
%%% -----------------------------------------------------------------
matthias@691
   231
%%% Make a call to servers at several nodes.
matthias@691
   232
%%% Returns: {[Replies],[BadNodes]}
matthias@691
   233
%%% A Timeout can be given
matthias@691
   234
%%% 
matthias@691
   235
%%% A middleman process is used in case late answers arrives after
matthias@691
   236
%%% the timeout. If they would be allowed to glog the callers message
matthias@691
   237
%%% queue, it would probably become confused. Late answers will 
matthias@691
   238
%%% now arrive to the terminated middleman and so be discarded.
matthias@691
   239
%%% -----------------------------------------------------------------
matthias@691
   240
multi_call(Name, Req)
matthias@691
   241
  when is_atom(Name) ->
matthias@691
   242
    do_multi_call([node() | nodes()], Name, Req, infinity).
matthias@691
   243
matthias@691
   244
multi_call(Nodes, Name, Req) 
matthias@691
   245
  when is_list(Nodes), is_atom(Name) ->
matthias@691
   246
    do_multi_call(Nodes, Name, Req, infinity).
matthias@691
   247
matthias@691
   248
multi_call(Nodes, Name, Req, infinity) ->
matthias@691
   249
    do_multi_call(Nodes, Name, Req, infinity);
matthias@691
   250
multi_call(Nodes, Name, Req, Timeout) 
matthias@691
   251
  when is_list(Nodes), is_atom(Name), is_integer(Timeout), Timeout >= 0 ->
matthias@691
   252
    do_multi_call(Nodes, Name, Req, Timeout).
matthias@691
   253
matthias@691
   254
matthias@691
   255
%%-----------------------------------------------------------------
matthias@691
   256
%% enter_loop(Mod, Options, State, <ServerName>, <TimeOut>) ->_ 
matthias@691
   257
%%   
matthias@691
   258
%% Description: Makes an existing process into a gen_server. 
matthias@691
   259
%%              The calling process will enter the gen_server receive 
matthias@691
   260
%%              loop and become a gen_server process.
matthias@691
   261
%%              The process *must* have been started using one of the 
matthias@691
   262
%%              start functions in proc_lib, see proc_lib(3). 
matthias@691
   263
%%              The user is responsible for any initialization of the 
matthias@691
   264
%%              process, including registering a name for it.
matthias@691
   265
%%-----------------------------------------------------------------
matthias@691
   266
enter_loop(Mod, Options, State) ->
matthias@691
   267
    enter_loop(Mod, Options, State, self(), infinity).
matthias@691
   268
matthias@691
   269
enter_loop(Mod, Options, State, ServerName = {_, _}) ->
matthias@691
   270
    enter_loop(Mod, Options, State, ServerName, infinity);
matthias@691
   271
matthias@691
   272
enter_loop(Mod, Options, State, Timeout) ->
matthias@691
   273
    enter_loop(Mod, Options, State, self(), Timeout).
matthias@691
   274
matthias@691
   275
enter_loop(Mod, Options, State, ServerName, Timeout) ->
matthias@691
   276
    Name = get_proc_name(ServerName),
matthias@691
   277
    Parent = get_parent(),
matthias@691
   278
    Debug = debug_options(Name, Options),
matthias@692
   279
    Queue = queue:new(),
matthias@692
   280
    loop(Parent, Name, State, Mod, Timeout, Queue, Debug).
matthias@691
   281
matthias@691
   282
%%%========================================================================
matthias@691
   283
%%% Gen-callback functions
matthias@691
   284
%%%========================================================================
matthias@691
   285
matthias@691
   286
%%% ---------------------------------------------------
matthias@691
   287
%%% Initiate the new process.
matthias@691
   288
%%% Register the name using the Rfunc function
matthias@691
   289
%%% Calls the Mod:init/Args function.
matthias@691
   290
%%% Finally an acknowledge is sent to Parent and the main
matthias@691
   291
%%% loop is entered.
matthias@691
   292
%%% ---------------------------------------------------
matthias@691
   293
init_it(Starter, self, Name, Mod, Args, Options) ->
matthias@691
   294
    init_it(Starter, self(), Name, Mod, Args, Options);
matthias@691
   295
init_it(Starter, Parent, Name, Mod, Args, Options) ->
matthias@691
   296
    Debug = debug_options(Name, Options),
matthias@692
   297
    Queue = queue:new(),
matthias@691
   298
    case catch Mod:init(Args) of
matthias@691
   299
	{ok, State} ->
matthias@691
   300
	    proc_lib:init_ack(Starter, {ok, self()}), 	    
matthias@692
   301
	    loop(Parent, Name, State, Mod, infinity, Queue, Debug);
matthias@691
   302
	{ok, State, Timeout} ->
matthias@691
   303
	    proc_lib:init_ack(Starter, {ok, self()}), 	    
matthias@692
   304
	    loop(Parent, Name, State, Mod, Timeout, Queue, Debug);
matthias@691
   305
	{stop, Reason} ->
matthias@691
   306
	    proc_lib:init_ack(Starter, {error, Reason}),
matthias@691
   307
	    exit(Reason);
matthias@691
   308
	ignore ->
matthias@691
   309
	    proc_lib:init_ack(Starter, ignore),
matthias@691
   310
	    exit(normal);
matthias@691
   311
	{'EXIT', Reason} ->
matthias@691
   312
	    proc_lib:init_ack(Starter, {error, Reason}),
matthias@691
   313
	    exit(Reason);
matthias@691
   314
	Else ->
matthias@691
   315
	    Error = {bad_return_value, Else},
matthias@691
   316
	    proc_lib:init_ack(Starter, {error, Error}),
matthias@691
   317
	    exit(Error)
matthias@691
   318
    end.
matthias@691
   319
matthias@691
   320
%%%========================================================================
matthias@691
   321
%%% Internal functions
matthias@691
   322
%%%========================================================================
matthias@691
   323
%%% ---------------------------------------------------
matthias@691
   324
%%% The MAIN loop.
matthias@691
   325
%%% ---------------------------------------------------
matthias@692
   326
loop(Parent, Name, State, Mod, Time, Queue, Debug) ->
matthias@692
   327
    receive
matthias@692
   328
        Input -> loop(Parent, Name, State, Mod,
matthias@692
   329
                      Time, queue:in(Input, Queue), Debug)
matthias@692
   330
    after 0 ->
matthias@692
   331
            case queue:out(Queue) of
matthias@692
   332
                {{value, Msg}, Queue1} ->
matthias@692
   333
                    process_msg(Parent, Name, State, Mod,
matthias@692
   334
                                Time, Queue1, Debug, Msg);
matthias@692
   335
                {empty, Queue1} ->
matthias@692
   336
                    receive
matthias@692
   337
                        Input ->
matthias@692
   338
                            loop(Parent, Name, State, Mod,
matthias@692
   339
                                 Time, queue:in(Input, Queue1), Debug)
matthias@692
   340
                    after Time ->
matthias@692
   341
                            process_msg(Parent, Name, State, Mod,
matthias@692
   342
                                        Time, Queue1, Debug, timeout)
matthias@692
   343
                    end
matthias@692
   344
            end
matthias@692
   345
    end.
matthias@692
   346
                    
matthias@692
   347
process_msg(Parent, Name, State, Mod, Time, Queue, Debug, Msg) ->
matthias@691
   348
    case Msg of
matthias@691
   349
	{system, From, Req} ->
matthias@691
   350
	    sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
matthias@692
   351
				  [Name, State, Mod, Time, Queue]);
matthias@691
   352
	{'EXIT', Parent, Reason} ->
matthias@691
   353
	    terminate(Reason, Name, Msg, Mod, State, Debug);
matthias@691
   354
	_Msg when Debug =:= [] ->
matthias@692
   355
	    handle_msg(Msg, Parent, Name, State, Mod, Time, Queue);
matthias@691
   356
	_Msg ->
matthias@691
   357
	    Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, 
matthias@691
   358
				      Name, {in, Msg}),
matthias@692
   359
	    handle_msg(Msg, Parent, Name, State, Mod, Time, Queue, Debug1)
matthias@691
   360
    end.
matthias@691
   361
matthias@691
   362
%%% ---------------------------------------------------
matthias@691
   363
%%% Send/recive functions
matthias@691
   364
%%% ---------------------------------------------------
matthias@691
   365
do_send(Dest, Msg) ->
matthias@693
   366
    catch erlang:send(Dest, Msg).
matthias@691
   367
matthias@691
   368
do_multi_call(Nodes, Name, Req, infinity) ->
matthias@691
   369
    Tag = make_ref(),
matthias@691
   370
    Monitors = send_nodes(Nodes, Name, Tag, Req),
matthias@691
   371
    rec_nodes(Tag, Monitors, Name, undefined);
matthias@691
   372
do_multi_call(Nodes, Name, Req, Timeout) ->
matthias@691
   373
    Tag = make_ref(),
matthias@691
   374
    Caller = self(),
matthias@691
   375
    Receiver =
matthias@691
   376
	spawn(
matthias@691
   377
	  fun() ->
matthias@691
   378
		  %% Middleman process. Should be unsensitive to regular
matthias@691
   379
		  %% exit signals. The sychronization is needed in case
matthias@691
   380
		  %% the receiver would exit before the caller started
matthias@691
   381
		  %% the monitor.
matthias@691
   382
		  process_flag(trap_exit, true),
matthias@691
   383
		  Mref = erlang:monitor(process, Caller),
matthias@691
   384
		  receive
matthias@691
   385
		      {Caller,Tag} ->
matthias@691
   386
			  Monitors = send_nodes(Nodes, Name, Tag, Req),
matthias@691
   387
			  TimerId = erlang:start_timer(Timeout, self(), ok),
matthias@691
   388
			  Result = rec_nodes(Tag, Monitors, Name, TimerId),
matthias@691
   389
			  exit({self(),Tag,Result});
matthias@691
   390
		      {'DOWN',Mref,_,_,_} ->
matthias@691
   391
			  %% Caller died before sending us the go-ahead.
matthias@691
   392
			  %% Give up silently.
matthias@691
   393
			  exit(normal)
matthias@691
   394
		  end
matthias@691
   395
	  end),
matthias@691
   396
    Mref = erlang:monitor(process, Receiver),
matthias@691
   397
    Receiver ! {self(),Tag},
matthias@691
   398
    receive
matthias@691
   399
	{'DOWN',Mref,_,_,{Receiver,Tag,Result}} ->
matthias@691
   400
	    Result;
matthias@691
   401
	{'DOWN',Mref,_,_,Reason} ->
matthias@691
   402
	    %% The middleman code failed. Or someone did 
matthias@691
   403
	    %% exit(_, kill) on the middleman process => Reason==killed
matthias@691
   404
	    exit(Reason)
matthias@691
   405
    end.
matthias@691
   406
matthias@691
   407
send_nodes(Nodes, Name, Tag, Req) ->
matthias@691
   408
    send_nodes(Nodes, Name, Tag, Req, []).
matthias@691
   409
matthias@691
   410
send_nodes([Node|Tail], Name, Tag, Req, Monitors)
matthias@691
   411
  when is_atom(Node) ->
matthias@691
   412
    Monitor = start_monitor(Node, Name),
matthias@691
   413
    %% Handle non-existing names in rec_nodes.
matthias@691
   414
    catch {Name, Node} ! {'$gen_call', {self(), {Tag, Node}}, Req},
matthias@691
   415
    send_nodes(Tail, Name, Tag, Req, [Monitor | Monitors]);
matthias@691
   416
send_nodes([_Node|Tail], Name, Tag, Req, Monitors) ->
matthias@691
   417
    %% Skip non-atom Node
matthias@691
   418
    send_nodes(Tail, Name, Tag, Req, Monitors);
matthias@691
   419
send_nodes([], _Name, _Tag, _Req, Monitors) -> 
matthias@691
   420
    Monitors.
matthias@691
   421
matthias@691
   422
%% Against old nodes:
matthias@691
   423
%% If no reply has been delivered within 2 secs. (per node) check that
matthias@691
   424
%% the server really exists and wait for ever for the answer.
matthias@691
   425
%%
matthias@691
   426
%% Against contemporary nodes:
matthias@691
   427
%% Wait for reply, server 'DOWN', or timeout from TimerId.
matthias@691
   428
matthias@691
   429
rec_nodes(Tag, Nodes, Name, TimerId) -> 
matthias@691
   430
    rec_nodes(Tag, Nodes, Name, [], [], 2000, TimerId).
matthias@691
   431
matthias@691
   432
rec_nodes(Tag, [{N,R}|Tail], Name, Badnodes, Replies, Time, TimerId ) ->
matthias@691
   433
    receive
matthias@691
   434
	{'DOWN', R, _, _, _} ->
matthias@691
   435
	    rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, Time, TimerId);
matthias@691
   436
	{{Tag, N}, Reply} ->  %% Tag is bound !!!
matthias@691
   437
	    unmonitor(R), 
matthias@691
   438
	    rec_nodes(Tag, Tail, Name, Badnodes, 
matthias@691
   439
		      [{N,Reply}|Replies], Time, TimerId);
matthias@691
   440
	{timeout, TimerId, _} ->	
matthias@691
   441
	    unmonitor(R),
matthias@691
   442
	    %% Collect all replies that already have arrived
matthias@691
   443
	    rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
matthias@691
   444
    end;
matthias@691
   445
rec_nodes(Tag, [N|Tail], Name, Badnodes, Replies, Time, TimerId) ->
matthias@691
   446
    %% R6 node
matthias@691
   447
    receive
matthias@691
   448
	{nodedown, N} ->
matthias@691
   449
	    monitor_node(N, false),
matthias@691
   450
	    rec_nodes(Tag, Tail, Name, [N|Badnodes], Replies, 2000, TimerId);
matthias@691
   451
	{{Tag, N}, Reply} ->  %% Tag is bound !!!
matthias@691
   452
	    receive {nodedown, N} -> ok after 0 -> ok end,
matthias@691
   453
	    monitor_node(N, false),
matthias@691
   454
	    rec_nodes(Tag, Tail, Name, Badnodes,
matthias@691
   455
		      [{N,Reply}|Replies], 2000, TimerId);
matthias@691
   456
	{timeout, TimerId, _} ->	
matthias@691
   457
	    receive {nodedown, N} -> ok after 0 -> ok end,
matthias@691
   458
	    monitor_node(N, false),
matthias@691
   459
	    %% Collect all replies that already have arrived
matthias@691
   460
	    rec_nodes_rest(Tag, Tail, Name, [N | Badnodes], Replies)
matthias@691
   461
    after Time ->
matthias@691
   462
	    case rpc:call(N, erlang, whereis, [Name]) of
matthias@691
   463
		Pid when is_pid(Pid) -> % It exists try again.
matthias@691
   464
		    rec_nodes(Tag, [N|Tail], Name, Badnodes,
matthias@691
   465
			      Replies, infinity, TimerId);
matthias@691
   466
		_ -> % badnode
matthias@691
   467
		    receive {nodedown, N} -> ok after 0 -> ok end,
matthias@691
   468
		    monitor_node(N, false),
matthias@691
   469
		    rec_nodes(Tag, Tail, Name, [N|Badnodes],
matthias@691
   470
			      Replies, 2000, TimerId)
matthias@691
   471
	    end
matthias@691
   472
    end;
matthias@691
   473
rec_nodes(_, [], _, Badnodes, Replies, _, TimerId) ->
matthias@691
   474
    case catch erlang:cancel_timer(TimerId) of
matthias@691
   475
	false ->  % It has already sent it's message
matthias@691
   476
	    receive
matthias@691
   477
		{timeout, TimerId, _} -> ok
matthias@691
   478
	    after 0 ->
matthias@691
   479
		    ok
matthias@691
   480
	    end;
matthias@691
   481
	_ -> % Timer was cancelled, or TimerId was 'undefined'
matthias@691
   482
	    ok
matthias@691
   483
    end,
matthias@691
   484
    {Replies, Badnodes}.
matthias@691
   485
matthias@691
   486
%% Collect all replies that already have arrived
matthias@691
   487
rec_nodes_rest(Tag, [{N,R}|Tail], Name, Badnodes, Replies) ->
matthias@691
   488
    receive
matthias@691
   489
	{'DOWN', R, _, _, _} ->
matthias@691
   490
	    rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies);
matthias@691
   491
	{{Tag, N}, Reply} -> %% Tag is bound !!!
matthias@691
   492
	    unmonitor(R),
matthias@691
   493
	    rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies])
matthias@691
   494
    after 0 ->
matthias@691
   495
	    unmonitor(R),
matthias@691
   496
	    rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
matthias@691
   497
    end;
matthias@691
   498
rec_nodes_rest(Tag, [N|Tail], Name, Badnodes, Replies) ->
matthias@691
   499
    %% R6 node
matthias@691
   500
    receive
matthias@691
   501
	{nodedown, N} ->
matthias@691
   502
	    monitor_node(N, false),
matthias@691
   503
	    rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies);
matthias@691
   504
	{{Tag, N}, Reply} ->  %% Tag is bound !!!
matthias@691
   505
	    receive {nodedown, N} -> ok after 0 -> ok end,
matthias@691
   506
	    monitor_node(N, false),
matthias@691
   507
	    rec_nodes_rest(Tag, Tail, Name, Badnodes, [{N,Reply}|Replies])
matthias@691
   508
    after 0 ->
matthias@691
   509
	    receive {nodedown, N} -> ok after 0 -> ok end,
matthias@691
   510
	    monitor_node(N, false),
matthias@691
   511
	    rec_nodes_rest(Tag, Tail, Name, [N|Badnodes], Replies)
matthias@691
   512
    end;
matthias@691
   513
rec_nodes_rest(_Tag, [], _Name, Badnodes, Replies) ->
matthias@691
   514
    {Replies, Badnodes}.
matthias@691
   515
matthias@691
   516
matthias@691
   517
%%% ---------------------------------------------------
matthias@691
   518
%%% Monitor functions
matthias@691
   519
%%% ---------------------------------------------------
matthias@691
   520
matthias@691
   521
start_monitor(Node, Name) when is_atom(Node), is_atom(Name) ->
matthias@691
   522
    if node() =:= nonode@nohost, Node =/= nonode@nohost ->
matthias@691
   523
	    Ref = make_ref(),
matthias@691
   524
	    self() ! {'DOWN', Ref, process, {Name, Node}, noconnection},
matthias@691
   525
	    {Node, Ref};
matthias@691
   526
       true ->
matthias@691
   527
	    case catch erlang:monitor(process, {Name, Node}) of
matthias@691
   528
		{'EXIT', _} ->
matthias@691
   529
		    %% Remote node is R6
matthias@691
   530
		    monitor_node(Node, true),
matthias@691
   531
		    Node;
matthias@691
   532
		Ref when is_reference(Ref) ->
matthias@691
   533
		    {Node, Ref}
matthias@691
   534
	    end
matthias@691
   535
    end.
matthias@691
   536
matthias@691
   537
%% Cancels a monitor started with Ref=erlang:monitor(_, _).
matthias@691
   538
unmonitor(Ref) when is_reference(Ref) ->
matthias@691
   539
    erlang:demonitor(Ref),
matthias@691
   540
    receive
matthias@691
   541
	{'DOWN', Ref, _, _, _} ->
matthias@691
   542
	    true
matthias@691
   543
    after 0 ->
matthias@691
   544
	    true
matthias@691
   545
    end.
matthias@691
   546
matthias@691
   547
%%% ---------------------------------------------------
matthias@691
   548
%%% Message handling functions
matthias@691
   549
%%% ---------------------------------------------------
matthias@691
   550
matthias@691
   551
dispatch({'$gen_cast', Msg}, Mod, State) ->
matthias@691
   552
    Mod:handle_cast(Msg, State);
matthias@691
   553
dispatch(Info, Mod, State) ->
matthias@691
   554
    Mod:handle_info(Info, State).
matthias@691
   555
matthias@692
   556
handle_msg({'$gen_call', From, Msg},
matthias@692
   557
           Parent, Name, State, Mod, _Time, Queue) ->
matthias@691
   558
    case catch Mod:handle_call(Msg, From, State) of
matthias@691
   559
	{reply, Reply, NState} ->
matthias@691
   560
	    reply(From, Reply),
matthias@692
   561
	    loop(Parent, Name, NState, Mod, infinity, Queue, []);
matthias@691
   562
	{reply, Reply, NState, Time1} ->
matthias@691
   563
	    reply(From, Reply),
matthias@692
   564
	    loop(Parent, Name, NState, Mod, Time1, Queue, []);
matthias@691
   565
	{noreply, NState} ->
matthias@692
   566
	    loop(Parent, Name, NState, Mod, infinity, Queue, []);
matthias@691
   567
	{noreply, NState, Time1} ->
matthias@692
   568
	    loop(Parent, Name, NState, Mod, Time1, Queue, []);
matthias@691
   569
	{stop, Reason, Reply, NState} ->
matthias@691
   570
	    {'EXIT', R} = 
matthias@691
   571
		(catch terminate(Reason, Name, Msg, Mod, NState, [])),
matthias@691
   572
	    reply(From, Reply),
matthias@691
   573
	    exit(R);
matthias@692
   574
	Other -> handle_common_reply(Other,
matthias@692
   575
                                     Parent, Name, Msg, Mod, State, Queue)
matthias@691
   576
    end;
matthias@692
   577
handle_msg(Msg,
matthias@692
   578
           Parent, Name, State, Mod, _Time, Queue) ->
matthias@691
   579
    Reply = (catch dispatch(Msg, Mod, State)),
matthias@692
   580
    handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Queue).
matthias@691
   581
matthias@692
   582
handle_msg({'$gen_call', From, Msg},
matthias@692
   583
           Parent, Name, State, Mod, _Time, Queue, Debug) ->
matthias@691
   584
    case catch Mod:handle_call(Msg, From, State) of
matthias@691
   585
	{reply, Reply, NState} ->
matthias@691
   586
	    Debug1 = reply(Name, From, Reply, NState, Debug),
matthias@692
   587
	    loop(Parent, Name, NState, Mod, infinity, Queue, Debug1);
matthias@691
   588
	{reply, Reply, NState, Time1} ->
matthias@691
   589
	    Debug1 = reply(Name, From, Reply, NState, Debug),
matthias@692
   590
	    loop(Parent, Name, NState, Mod, Time1, Queue, Debug1);
matthias@691
   591
	{noreply, NState} ->
matthias@691
   592
	    Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
matthias@691
   593
				      {noreply, NState}),
matthias@692
   594
	    loop(Parent, Name, NState, Mod, infinity, Queue, Debug1);
matthias@691
   595
	{noreply, NState, Time1} ->
matthias@691
   596
	    Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
matthias@691
   597
				      {noreply, NState}),
matthias@692
   598
	    loop(Parent, Name, NState, Mod, Time1, Queue, Debug1);
matthias@691
   599
	{stop, Reason, Reply, NState} ->
matthias@691
   600
	    {'EXIT', R} = 
matthias@691
   601
		(catch terminate(Reason, Name, Msg, Mod, NState, Debug)),
matthias@691
   602
	    reply(Name, From, Reply, NState, Debug),
matthias@691
   603
	    exit(R);
matthias@691
   604
	Other ->
matthias@692
   605
	    handle_common_reply(Other,
matthias@692
   606
                                Parent, Name, Msg, Mod, State, Queue, Debug)
matthias@691
   607
    end;
matthias@692
   608
handle_msg(Msg,
matthias@692
   609
           Parent, Name, State, Mod, _Time, Queue, Debug) ->
matthias@691
   610
    Reply = (catch dispatch(Msg, Mod, State)),
matthias@692
   611
    handle_common_reply(Reply,
matthias@692
   612
                        Parent, Name, Msg, Mod, State, Queue, Debug).
matthias@691
   613
matthias@692
   614
handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Queue) ->
matthias@691
   615
    case Reply of
matthias@691
   616
	{noreply, NState} ->
matthias@692
   617
	    loop(Parent, Name, NState, Mod, infinity, Queue, []);
matthias@691
   618
	{noreply, NState, Time1} ->
matthias@692
   619
	    loop(Parent, Name, NState, Mod, Time1, Queue, []);
matthias@691
   620
	{stop, Reason, NState} ->
matthias@691
   621
	    terminate(Reason, Name, Msg, Mod, NState, []);
matthias@691
   622
	{'EXIT', What} ->
matthias@691
   623
	    terminate(What, Name, Msg, Mod, State, []);
matthias@691
   624
	_ ->
matthias@691
   625
	    terminate({bad_return_value, Reply}, Name, Msg, Mod, State, [])
matthias@691
   626
    end.
matthias@691
   627
matthias@692
   628
handle_common_reply(Reply, Parent, Name, Msg, Mod, State, Queue, Debug) ->
matthias@691
   629
    case Reply of
matthias@691
   630
	{noreply, NState} ->
matthias@691
   631
	    Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
matthias@691
   632
				      {noreply, NState}),
matthias@692
   633
	    loop(Parent, Name, NState, Mod, infinity, Queue, Debug1);
matthias@691
   634
	{noreply, NState, Time1} ->
matthias@691
   635
	    Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name,
matthias@691
   636
				      {noreply, NState}),
matthias@692
   637
	    loop(Parent, Name, NState, Mod, Time1, Queue, Debug1);
matthias@691
   638
	{stop, Reason, NState} ->
matthias@691
   639
	    terminate(Reason, Name, Msg, Mod, NState, Debug);
matthias@691
   640
	{'EXIT', What} ->
matthias@691
   641
	    terminate(What, Name, Msg, Mod, State, Debug);
matthias@691
   642
	_ ->
matthias@691
   643
	    terminate({bad_return_value, Reply}, Name, Msg, Mod, State, Debug)
matthias@691
   644
    end.
matthias@691
   645
matthias@691
   646
reply(Name, {To, Tag}, Reply, State, Debug) ->
matthias@691
   647
    reply({To, Tag}, Reply),
matthias@691
   648
    sys:handle_debug(Debug, {?MODULE, print_event}, Name, 
matthias@691
   649
		     {out, Reply, To, State} ).
matthias@691
   650
matthias@691
   651
matthias@691
   652
%%-----------------------------------------------------------------
matthias@691
   653
%% Callback functions for system messages handling.
matthias@691
   654
%%-----------------------------------------------------------------
matthias@692
   655
system_continue(Parent, Debug, [Name, State, Mod, Time, Queue]) ->
matthias@692
   656
    loop(Parent, Name, State, Mod, Time, Queue, Debug).
matthias@691
   657
matthias@692
   658
system_terminate(Reason, _Parent, Debug, [Name, State, Mod, _Time, _Queue]) ->
matthias@691
   659
    terminate(Reason, Name, [], Mod, State, Debug).
matthias@691
   660
matthias@692
   661
system_code_change([Name, State, Mod, Time, Queue], _Module, OldVsn, Extra) ->
matthias@691
   662
    case catch Mod:code_change(OldVsn, State, Extra) of
matthias@692
   663
	{ok, NewState} -> {ok, [Name, NewState, Mod, Time, Queue]};
matthias@691
   664
	Else -> Else
matthias@691
   665
    end.
matthias@691
   666
matthias@691
   667
%%-----------------------------------------------------------------
matthias@691
   668
%% Format debug messages.  Print them as the call-back module sees
matthias@691
   669
%% them, not as the real erlang messages.  Use trace for that.
matthias@691
   670
%%-----------------------------------------------------------------
matthias@691
   671
print_event(Dev, {in, Msg}, Name) ->
matthias@691
   672
    case Msg of
matthias@691
   673
	{'$gen_call', {From, _Tag}, Call} ->
matthias@691
   674
	    io:format(Dev, "*DBG* ~p got call ~p from ~w~n",
matthias@691
   675
		      [Name, Call, From]);
matthias@691
   676
	{'$gen_cast', Cast} ->
matthias@691
   677
	    io:format(Dev, "*DBG* ~p got cast ~p~n",
matthias@691
   678
		      [Name, Cast]);
matthias@691
   679
	_ ->
matthias@691
   680
	    io:format(Dev, "*DBG* ~p got ~p~n", [Name, Msg])
matthias@691
   681
    end;
matthias@691
   682
print_event(Dev, {out, Msg, To, State}, Name) ->
matthias@691
   683
    io:format(Dev, "*DBG* ~p sent ~p to ~w, new state ~w~n", 
matthias@691
   684
	      [Name, Msg, To, State]);
matthias@691
   685
print_event(Dev, {noreply, State}, Name) ->
matthias@691
   686
    io:format(Dev, "*DBG* ~p new state ~w~n", [Name, State]);
matthias@691
   687
print_event(Dev, Event, Name) ->
matthias@691
   688
    io:format(Dev, "*DBG* ~p dbg  ~p~n", [Name, Event]).
matthias@691
   689
matthias@691
   690
matthias@691
   691
%%% ---------------------------------------------------
matthias@691
   692
%%% Terminate the server.
matthias@691
   693
%%% ---------------------------------------------------
matthias@691
   694
matthias@691
   695
terminate(Reason, Name, Msg, Mod, State, Debug) ->
matthias@691
   696
    case catch Mod:terminate(Reason, State) of
matthias@691
   697
	{'EXIT', R} ->
matthias@691
   698
	    error_info(R, Name, Msg, State, Debug),
matthias@691
   699
	    exit(R);
matthias@691
   700
	_ ->
matthias@691
   701
	    case Reason of
matthias@691
   702
		normal ->
matthias@691
   703
		    exit(normal);
matthias@691
   704
		shutdown ->
matthias@691
   705
		    exit(shutdown);
matthias@691
   706
		_ ->
matthias@691
   707
		    error_info(Reason, Name, Msg, State, Debug),
matthias@691
   708
		    exit(Reason)
matthias@691
   709
	    end
matthias@691
   710
    end.
matthias@691
   711
matthias@691
   712
error_info(_Reason, application_controller, _Msg, _State, _Debug) ->
matthias@691
   713
    %% OTP-5811 Don't send an error report if it's the system process
matthias@691
   714
    %% application_controller which is terminating - let init take care
matthias@691
   715
    %% of it instead
matthias@691
   716
    ok;
matthias@691
   717
error_info(Reason, Name, Msg, State, Debug) ->
matthias@691
   718
    Reason1 = 
matthias@691
   719
	case Reason of
matthias@691
   720
	    {undef,[{M,F,A}|MFAs]} ->
matthias@691
   721
		case code:is_loaded(M) of
matthias@691
   722
		    false ->
matthias@691
   723
			{'module could not be loaded',[{M,F,A}|MFAs]};
matthias@691
   724
		    _ ->
matthias@691
   725
			case erlang:function_exported(M, F, length(A)) of
matthias@691
   726
			    true ->
matthias@691
   727
				Reason;
matthias@691
   728
			    false ->
matthias@691
   729
				{'function not exported',[{M,F,A}|MFAs]}
matthias@691
   730
			end
matthias@691
   731
		end;
matthias@691
   732
	    _ ->
matthias@691
   733
		Reason
matthias@691
   734
	end,    
matthias@691
   735
    format("** Generic server ~p terminating \n"
matthias@691
   736
           "** Last message in was ~p~n"
matthias@691
   737
           "** When Server state == ~p~n"
matthias@691
   738
           "** Reason for termination == ~n** ~p~n",
matthias@691
   739
	   [Name, Msg, State, Reason1]),
matthias@691
   740
    sys:print_log(Debug),
matthias@691
   741
    ok.
matthias@691
   742
matthias@691
   743
%%% ---------------------------------------------------
matthias@691
   744
%%% Misc. functions.
matthias@691
   745
%%% ---------------------------------------------------
matthias@691
   746
matthias@691
   747
opt(Op, [{Op, Value}|_]) ->
matthias@691
   748
    {ok, Value};
matthias@691
   749
opt(Op, [_|Options]) ->
matthias@691
   750
    opt(Op, Options);
matthias@691
   751
opt(_, []) ->
matthias@691
   752
    false.
matthias@691
   753
matthias@691
   754
debug_options(Name, Opts) ->
matthias@691
   755
    case opt(debug, Opts) of
matthias@691
   756
	{ok, Options} -> dbg_options(Name, Options);
matthias@691
   757
	_ -> dbg_options(Name, [])
matthias@691
   758
    end.
matthias@691
   759
matthias@691
   760
dbg_options(Name, []) ->
matthias@691
   761
    Opts = 
matthias@691
   762
	case init:get_argument(generic_debug) of
matthias@691
   763
	    error ->
matthias@691
   764
		[];
matthias@691
   765
	    _ ->
matthias@691
   766
		[log, statistics]
matthias@691
   767
	end,
matthias@691
   768
    dbg_opts(Name, Opts);
matthias@691
   769
dbg_options(Name, Opts) ->
matthias@691
   770
    dbg_opts(Name, Opts).
matthias@691
   771
matthias@691
   772
dbg_opts(Name, Opts) ->
matthias@691
   773
    case catch sys:debug_options(Opts) of
matthias@691
   774
	{'EXIT',_} ->
matthias@691
   775
	    format("~p: ignoring erroneous debug options - ~p~n",
matthias@691
   776
		   [Name, Opts]),
matthias@691
   777
	    [];
matthias@691
   778
	Dbg ->
matthias@691
   779
	    Dbg
matthias@691
   780
    end.
matthias@691
   781
matthias@691
   782
get_proc_name(Pid) when is_pid(Pid) ->
matthias@691
   783
    Pid;
matthias@691
   784
get_proc_name({local, Name}) ->
matthias@691
   785
    case process_info(self(), registered_name) of
matthias@691
   786
	{registered_name, Name} ->
matthias@691
   787
	    Name;
matthias@691
   788
	{registered_name, _Name} ->
matthias@691
   789
	    exit(process_not_registered);
matthias@691
   790
	[] ->
matthias@691
   791
	    exit(process_not_registered)
matthias@691
   792
    end;    
matthias@691
   793
get_proc_name({global, Name}) ->
matthias@691
   794
    case global:safe_whereis_name(Name) of
matthias@691
   795
	undefined ->
matthias@691
   796
	    exit(process_not_registered_globally);
matthias@691
   797
	Pid when Pid =:= self() ->
matthias@691
   798
	    Name;
matthias@691
   799
	_Pid ->
matthias@691
   800
	    exit(process_not_registered_globally)
matthias@691
   801
    end.
matthias@691
   802
matthias@691
   803
get_parent() ->
matthias@691
   804
    case get('$ancestors') of
matthias@691
   805
	[Parent | _] when is_pid(Parent)->
matthias@691
   806
            Parent;
matthias@691
   807
        [Parent | _] when is_atom(Parent)->
matthias@691
   808
            name_to_pid(Parent);
matthias@691
   809
	_ ->
matthias@691
   810
	    exit(process_was_not_started_by_proc_lib)
matthias@691
   811
    end.
matthias@691
   812
matthias@691
   813
name_to_pid(Name) ->
matthias@691
   814
    case whereis(Name) of
matthias@691
   815
	undefined ->
matthias@691
   816
	    case global:safe_whereis_name(Name) of
matthias@691
   817
		undefined ->
matthias@691
   818
		    exit(could_not_find_registerd_name);
matthias@691
   819
		Pid ->
matthias@691
   820
		    Pid
matthias@691
   821
	    end;
matthias@691
   822
	Pid ->
matthias@691
   823
	    Pid
matthias@691
   824
    end.
matthias@691
   825
matthias@691
   826
%%-----------------------------------------------------------------
matthias@691
   827
%% Status information
matthias@691
   828
%%-----------------------------------------------------------------
matthias@691
   829
format_status(Opt, StatusData) ->
matthias@692
   830
    [PDict, SysState, Parent, Debug, [Name, State, Mod, _Time, Queue]] =
matthias@692
   831
        StatusData,
matthias@691
   832
    NameTag = if is_pid(Name) ->
matthias@691
   833
		      pid_to_list(Name);
matthias@691
   834
		 is_atom(Name) ->
matthias@691
   835
		      Name
matthias@691
   836
	      end,
matthias@691
   837
    Header = lists:concat(["Status for generic server ", NameTag]),
matthias@691
   838
    Log = sys:get_debug(log, Debug, []),
matthias@691
   839
    Specfic = 
matthias@691
   840
	case erlang:function_exported(Mod, format_status, 2) of
matthias@691
   841
	    true ->
matthias@691
   842
		case catch Mod:format_status(Opt, [PDict, State]) of
matthias@691
   843
		    {'EXIT', _} -> [{data, [{"State", State}]}];
matthias@691
   844
		    Else -> Else
matthias@691
   845
		end;
matthias@691
   846
	    _ ->
matthias@691
   847
		[{data, [{"State", State}]}]
matthias@691
   848
	end,
matthias@691
   849
    [{header, Header},
matthias@691
   850
     {data, [{"Status", SysState},
matthias@691
   851
	     {"Parent", Parent},
matthias@692
   852
	     {"Logged events", Log},
matthias@692
   853
             {"Queued messages", queue:to_list(Queue)}]} |
matthias@691
   854
     Specfic].