Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

restructuring

git-svn-id: http://svn.ulf.wiger.net/gproc/trunk/gproc@9 f3948e33-8234-0410-8a80-a07eae3b6c4d
  • Loading branch information...
commit d0bc9d2409f62f8b1f341c6ef148b695fc86dd5f 0 parents
uwiger authored
BIN  doc/erlang07-wiger.doc
Binary file not shown
BIN  doc/erlang07-wiger.pdf
Binary file not shown
245 doc/gproc.html
@@ -0,0 +1,245 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<title>Module gproc</title>
+<link rel="stylesheet" type="text/css" href="stylesheet.css" title="EDoc">
+</head>
+<body bgcolor="white">
+<div class="navbar"><a name="#navbar_top"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
+<hr>
+
+<h1>Module gproc</h1>
+<ul class="index"><li><a href="#description">Description</a></li><li><a href="#index">Function Index</a></li><li><a href="#functions">Function Details</a></li></ul>Extended process registry.
+
+<p><b>Behaviours:</b> <a href="gen_leader.html"><tt>gen_leader</tt></a>.</p>
+<p><b>Authors:</b> Ulf Wiger (<a href="mailto:ulf.wiger@ericsson.com"><tt>ulf.wiger@ericsson.com</tt></a>).</p>
+
+<h2><a name="description">Description</a></h2>Extended process registry
+ <p>This module implements an extended process registry</p>
+ <p>For a detailed description, see gproc/doc/erlang07-wiger.pdf.</p>
+<h2><a name="index">Function Index</a></h2>
+<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#code_change-4">code_change/4</a></td><td></td></tr>
+<tr><td valign="top"><a href="#elected-2">elected/2</a></td><td></td></tr>
+<tr><td valign="top"><a href="#first-1">first/1</a></td><td></td></tr>
+<tr><td valign="top"><a href="#from_leader-3">from_leader/3</a></td><td></td></tr>
+<tr><td valign="top"><a href="#get_value-1">get_value/1</a></td><td>Read the value stored with a key registered to the current process.</td></tr>
+<tr><td valign="top"><a href="#go_global-0">go_global/0</a></td><td></td></tr>
+<tr><td valign="top"><a href="#go_global-1">go_global/1</a></td><td></td></tr>
+<tr><td valign="top"><a href="#handle_DOWN-3">handle_DOWN/3</a></td><td></td></tr>
+<tr><td valign="top"><a href="#handle_call-3">handle_call/3</a></td><td></td></tr>
+<tr><td valign="top"><a href="#handle_cast-2">handle_cast/2</a></td><td></td></tr>
+<tr><td valign="top"><a href="#handle_info-2">handle_info/2</a></td><td></td></tr>
+<tr><td valign="top"><a href="#handle_leader_call-4">handle_leader_call/4</a></td><td></td></tr>
+<tr><td valign="top"><a href="#handle_leader_cast-3">handle_leader_cast/3</a></td><td></td></tr>
+<tr><td valign="top"><a href="#info-1">info/1</a></td><td></td></tr>
+<tr><td valign="top"><a href="#info-2">info/2</a></td><td></td></tr>
+<tr><td valign="top"><a href="#init-1">init/1</a></td><td></td></tr>
+<tr><td valign="top"><a href="#last-1">last/1</a></td><td></td></tr>
+<tr><td valign="top"><a href="#mreg-3">mreg/3</a></td><td></td></tr>
+<tr><td valign="top"><a href="#next-2">next/2</a></td><td></td></tr>
+<tr><td valign="top"><a href="#prev-2">prev/2</a></td><td></td></tr>
+<tr><td valign="top"><a href="#reg-2">reg/2</a></td><td>
+ Class = n - unique name
+ | p - non-unique property
+ | c - counter
+ | a - aggregated counter
+ Scope = l | g (global or local).</td></tr>
+<tr><td valign="top"><a href="#select-1">select/1</a></td><td></td></tr>
+<tr><td valign="top"><a href="#select-2">select/2</a></td><td></td></tr>
+<tr><td valign="top"><a href="#send-2">send/2</a></td><td></td></tr>
+<tr><td valign="top"><a href="#set_value-2">set_value/2</a></td><td></td></tr>
+<tr><td valign="top"><a href="#start_link-0">start_link/0</a></td><td></td></tr>
+<tr><td valign="top"><a href="#start_link-1">start_link/1</a></td><td></td></tr>
+<tr><td valign="top"><a href="#start_local-0">start_local/0</a></td><td></td></tr>
+<tr><td valign="top"><a href="#surrendered-3">surrendered/3</a></td><td></td></tr>
+<tr><td valign="top"><a href="#table-1">table/1</a></td><td></td></tr>
+<tr><td valign="top"><a href="#table-2">table/2</a></td><td></td></tr>
+<tr><td valign="top"><a href="#terminate-2">terminate/2</a></td><td></td></tr>
+<tr><td valign="top"><a href="#unreg-1">unreg/1</a></td><td></td></tr>
+<tr><td valign="top"><a href="#update_counter-2">update_counter/2</a></td><td></td></tr>
+</table>
+
+<h2><a name="functions">Function Details</a></h2>
+
+<h3 class="function"><a name="code_change-4">code_change/4</a></h3>
+<div class="spec">
+<p><tt>code_change(FromVsn, S, Extra, E) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="elected-2">elected/2</a></h3>
+<div class="spec">
+<p><tt>elected(S, E) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="first-1">first/1</a></h3>
+<div class="spec">
+<p><tt>first(Scope) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="from_leader-3">from_leader/3</a></h3>
+<div class="spec">
+<p><tt>from_leader(Ops, S, E) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="get_value-1">get_value/1</a></h3>
+<div class="spec">
+<p><tt>get_value(Key) -&gt; Value</tt></p>
+</div><p>Read the value stored with a key registered to the current process.
+ </p>
+
+<h3 class="function"><a name="go_global-0">go_global/0</a></h3>
+<div class="spec">
+<p><tt>go_global() -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="go_global-1">go_global/1</a></h3>
+<div class="spec">
+<p><tt>go_global(Nodes) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="handle_DOWN-3">handle_DOWN/3</a></h3>
+<div class="spec">
+<p><tt>handle_DOWN(Node, S, E) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="handle_call-3">handle_call/3</a></h3>
+<div class="spec">
+<p><tt>handle_call(X1, X2, S) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="handle_cast-2">handle_cast/2</a></h3>
+<div class="spec">
+<p><tt>handle_cast(X1, S) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="handle_info-2">handle_info/2</a></h3>
+<div class="spec">
+<p><tt>handle_info(X1, S) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="handle_leader_call-4">handle_leader_call/4</a></h3>
+<div class="spec">
+<p><tt>handle_leader_call(X1, From, State, E) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="handle_leader_cast-3">handle_leader_cast/3</a></h3>
+<div class="spec">
+<p><tt>handle_leader_cast(X1, State, E) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="info-1">info/1</a></h3>
+<div class="spec">
+<p><tt>info(Pid) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="info-2">info/2</a></h3>
+<div class="spec">
+<p><tt>info(Pid, I) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="init-1">init/1</a></h3>
+<div class="spec">
+<p><tt>init(X1) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="last-1">last/1</a></h3>
+<div class="spec">
+<p><tt>last(Scope) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="mreg-3">mreg/3</a></h3>
+<div class="spec">
+<p><tt>mreg(T, X2, KVL) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="next-2">next/2</a></h3>
+<div class="spec">
+<p><tt>next(Scope, K) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="prev-2">prev/2</a></h3>
+<div class="spec">
+<p><tt>prev(Scope, K) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="reg-2">reg/2</a></h3>
+<div class="spec">
+<p><tt>reg(Key, Value) -&gt; any()</tt></p>
+</div><p>
+ Class = n - unique name
+ | p - non-unique property
+ | c - counter
+ | a - aggregated counter
+ Scope = l | g (global or local)
+ </p>
+
+<h3 class="function"><a name="select-1">select/1</a></h3>
+<div class="spec">
+<p><tt>select(Pat) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="select-2">select/2</a></h3>
+<div class="spec">
+<p><tt>select(Scope, Pat) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="send-2">send/2</a></h3>
+<div class="spec">
+<p><tt>send(Key, Msg) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="set_value-2">set_value/2</a></h3>
+<div class="spec">
+<p><tt>set_value(Key, Value) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="start_link-0">start_link/0</a></h3>
+<div class="spec">
+<p><tt>start_link() -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="start_link-1">start_link/1</a></h3>
+<div class="spec">
+<p><tt>start_link(Nodes) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="start_local-0">start_local/0</a></h3>
+<div class="spec">
+<p><tt>start_local() -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="surrendered-3">surrendered/3</a></h3>
+<div class="spec">
+<p><tt>surrendered(S, X2, E) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="table-1">table/1</a></h3>
+<div class="spec">
+<p><tt>table(Scope) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="table-2">table/2</a></h3>
+<div class="spec">
+<p><tt>table(T, Opts) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="terminate-2">terminate/2</a></h3>
+<div class="spec">
+<p><tt>terminate(Reason, S) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="unreg-1">unreg/1</a></h3>
+<div class="spec">
+<p><tt>unreg(Key) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="update_counter-2">update_counter/2</a></h3>
+<div class="spec">
+<p><tt>update_counter(Key, Incr) -&gt; any()</tt></p>
+</div>
+<hr>
+
+<div class="navbar"><a name="#navbar_bottom"></a><table width="100%" border="0" cellspacing="0" cellpadding="2" summary="navigation bar"><tr><td><a href="overview-summary.html" target="overviewFrame">Overview</a></td><td><a href="http://www.erlang.org/"><img src="erlang.png" align="right" border="0" alt="erlang logo"></a></td></tr></table></div>
+<p><i>Generated by EDoc, Sep 4 2008, 11:29:40.</i></p>
+</body>
+</html>
1,076 patches/gen_leader/gen_leader.erl
@@ -0,0 +1,1076 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% @author Ulf Wiger <ulf.wiger@ericsson.com>
+%% @author Thomas Arts <thomas.arts@ituniv.se>
+%%
+%% @doc Leader election behaviour.
+%% <p>This application implements a leader election behaviour modeled after
+%% gen_server. This behaviour intends to make it reasonably
+%% straightforward to implement a fully distributed server with
+%% master-slave semantics.</p>
+%% <p>The gen_leader behaviour supports nearly everything that gen_server
+%% does (some functions, such as multicall() and the internal timeout,
+%% have been removed), and adds a few callbacks and API functions to
+%% support leader election etc.</p>
+%% <p>Also included is an example program, a global dictionary, based
+%% on the modules gen_leader and dict. The callback implementing the
+%% global dictionary is called 'test_cb', for no particularly logical
+%% reason.</p>
+%% @end
+%%
+%% @type election() = tuple(). Opaque state of the gen_leader behaviour.
+%% @type node() = atom(). A node name.
+%% @type name() = atom(). A locally registered name.
+%% @type serverRef() = Name | {name(),node()} | {global,Name} | pid().
+%% See gen_server.
+%% @type callerRef() = {pid(), reference()}. See gen_server.
+%%
+-module(gen_leader).
+
+
+-export([start/4, start/6,
+ start_link/4, start_link/6,
+ leader_call/2, leader_call/3, leader_cast/2,
+ call/2, call/3, cast/2,
+ reply/2]).
+
+%% Query functions
+-export([alive/1,
+ down/1,
+ candidates/1,
+ workers/1]).
+
+-export([
+ system_continue/3,
+ system_terminate/4,
+ system_code_change/4,
+ format_status/2
+ ]).
+
+-export([behaviour_info/1]).
+
+%% Internal exports
+-export([init_it/6, print_event/3
+ %%, safe_send/2
+ ]).
+
+-import(error_logger , [format/2]).
+-import(lists, [foldl/3,
+ foreach/2,
+ member/2,
+ keydelete/3,
+ keysearch/3,
+ keymember/3]).
+
+
+-record(election,{leader = none,
+ mode = global,
+ name,
+ leadernode = none,
+ candidate_nodes = [],
+ worker_nodes = [],
+ alive = [],
+ iteration,
+ down = [],
+ monitored = [],
+ buffered = []
+ }).
+
+-record(server, {parent,
+ mod,
+ state,
+ debug}).
+
+%%% ---------------------------------------------------
+%%% Interface functions.
+%%% ---------------------------------------------------
+
+%% @hidden
+behaviour_info(callbacks) ->
+ [{init,1},
+ {elected,2},
+ {surrendered,3},
+ {handle_leader_call,4},
+ {handle_leader_cast,3},
+ {handle_local_only, 4},
+ {from_leader,3},
+ {handle_call,3},
+ {handle_cast,2},
+ {handle_DOWN,3},
+ {handle_info,2},
+ {terminate,2},
+ {code_change,4}];
+behaviour_info(_Other) ->
+ undefined.
+
+start(Name, Mod, Arg, Options) when is_atom(Name) ->
+ gen:start(?MODULE, nolink, {local,Name},
+ Mod, {local_only, Arg}, Options).
+
+%% @spec start(Name::node(), CandidateNodes::[node()],
+%% Workers::[node()], Mod::atom(), Arg, Options::list()) ->
+%% {ok,pid()}
+%%
+%% @doc Starts a gen_leader process without linking to the parent.
+%%
+start(Name, [_|_] = CandidateNodes, Workers, Mod, Arg, Options)
+ when is_atom(Name) ->
+ gen:start(?MODULE, nolink, {local,Name},
+ Mod, {CandidateNodes, Workers, Arg}, Options).
+
+%% @spec start_link(Name::atom(), CandidateNodes::[atom()],
+%% Workers::[atom()], Mod::atom(), Arg, Options::list()) ->
+%% {ok, pid()}
+%%
+%% @doc Starts a gen_leader process.
+%% <table>
+%% <tr><td>Name</td><td>The locally registered name of the process</td></tr>
+%% <tr><td>CandidateNodes</td><td>The names of nodes capable of assuming
+%% a leadership role</td></tr>
+%% <tr><td>Workers</td>
+%% <td>The names of nodes that will be part of the "cluster",
+%% but cannot ever assume a leadership role.</td></tr>
+%% <tr><td>Mod</td><td>The name of the callback module</td></tr>
+%% <tr><td>Arg</td><td>Argument passed on to <code>Mod:init/1</code></td></tr>
+%% <tr><td>Options</td><td>Same as gen_server's Options</td></tr>
+%% </table>
+%%
+%% <p>The list of candidates needs to be known from the start. Workers
+%% can be added at runtime.</p>
+%% @end
+start_link(Name, [_|_] = CandidateNodes, Workers,
+ Mod, Arg, Options) when is_atom(Name) ->
+ gen:start(?MODULE, link, {local,Name}, Mod,
+ {CandidateNodes, Workers, Arg}, Options).
+
+start_link(Name, Mod, Arg, Options) when is_atom(Name) ->
+ gen:start(?MODULE, link, {local,Name}, Mod,
+ {local_only, Arg}, Options).
+
+%% Query functions to be used from the callback module
+
+%% @spec alive(E::election()) -> [node()]
+%%
+%% @doc Returns a list of live nodes (candidates and workers).
+%%
+alive(#election{alive = Alive}) ->
+ Alive.
+
+%% @spec down(E::election()) -> [node()]
+%%
+%% @doc Returns a list of candidates currently not running.
+%%
+down(#election{down = Down}) ->
+ Down.
+
+%% @spec candidates(E::election()) -> [node()]
+%%
+%% @doc Returns a list of known candidates.
+%%
+candidates(#election{candidate_nodes = Cands}) ->
+ Cands.
+
+%% @spec workers(E::election()) -> [node()]
+%%
+%% @doc Returns a list of known workers.
+%%
+workers(#election{worker_nodes = Workers}) ->
+ Workers.
+
+%% @spec call(Name::serverRef(), Request) -> term()
+%%
+%% @doc Equivalent to <code>gen_server:call/2</code>, but with a slightly
+%% different exit reason if something goes wrong. This function calls
+%% the <code>gen_leader</code> process exactly as if it were a gen_server
+%% (which, for practical purposes, it is.)
+%% @end
+call(Name, Request) ->
+ case catch gen:call(Name, '$gen_call', Request) of
+ {ok,Res} ->
+ Res;
+ {'EXIT',Reason} ->
+ exit({Reason, {?MODULE, local_call, [Name, Request]}})
+ end.
+
+%% @spec call(Name::serverRef(), Request, Timeout::integer()) ->
+%% Reply
+%%
+%% Reply = term()
+%%
+%% @doc Equivalent to <code>gen_server:call/3</code>, but with a slightly
+%% different exit reason if something goes wrong. This function calls
+%% the <code>gen_leader</code> process exactly as if it were a gen_server
+%% (which, for practical purposes, it is.)
+%% @end
+call(Name, Request, Timeout) ->
+ case catch gen:call(Name, '$gen_call', Request, Timeout) of
+ {ok,Res} ->
+ Res;
+ {'EXIT',Reason} ->
+ exit({Reason, {?MODULE, local_call, [Name, Request, Timeout]}})
+ end.
+
+%% @spec leader_call(Name::name(), Request::term())
+%% -> Reply
+%%
+%% Reply = term()
+%%
+%% @doc Makes a call (similar to <code>gen_server:call/2</code>) to the
+%% leader. The call is forwarded via the local gen_leader instance, if
+%% that one isn't actually the leader. The client will exit if the
+%% leader dies while the request is outstanding.
+%% <p>This function uses <code>gen:call/3</code>, and is subject to the
+%% same default timeout as e.g. <code>gen_server:call/2</code>.</p>
+%% @end
+%%
+leader_call(Name, Request) ->
+ case catch gen:call(Name, '$leader_call', Request) of
+ {ok,{leader,reply,Res}} ->
+ Res;
+ {ok,{error, leader_died}} ->
+ exit({leader_died, {?MODULE, leader_call, [Name, Request]}});
+ {'EXIT',Reason} ->
+ exit({Reason, {?MODULE, leader_call, [Name, Request]}})
+ end.
+
+%% @spec leader_call(Name::name(), Request::term(), Timeout::integer())
+%% -> Reply
+%%
+%% Reply = term()
+%%
+%% @doc Makes a call (similar to <code>gen_server:call/3</code>) to the
+%% leader. The call is forwarded via the local gen_leader instance, if
+%% that one isn't actually the leader. The client will exit if the
+%% leader dies while the request is outstanding.
+%% @end
+%%
+leader_call(Name, Request, Timeout) ->
+ case catch gen:call(Name, '$leader_call', Request, Timeout) of
+ {ok,{leader,reply,Res}} ->
+ Res;
+ {ok,{error, leader_died}} ->
+ exit({leader_died, {?MODULE, leader_call, [Name, Request]}});
+ {'EXIT',Reason} ->
+ exit({Reason, {?MODULE, leader_call, [Name, Request, Timeout]}})
+ end.
+
+
+
+%% @equiv gen_server:cast/2
+cast(Name, Request) ->
+ catch do_cast('$gen_cast', Name, Request),
+ ok.
+
+%% @spec leader_cast(Name::name(), Msg::term()) -> ok
+%% @doc Similar to <code>gen_server:cast/2</code> but will be forwarded to
+%% the leader via the local gen_leader instance.
+leader_cast(Name, Request) ->
+ catch do_cast('$leader_cast', Name, Request),
+ ok.
+
+
+do_cast(Tag, Name, Request) when atom(Name) ->
+ Name ! {Tag, Request};
+do_cast(Tag, Pid, Request) when pid(Pid) ->
+ Pid ! {Tag, Request}.
+
+
+%% @spec reply(From::callerRef(), Reply::term()) -> Void
+%% @equiv gen_server:reply/2
+reply({To, Tag}, Reply) ->
+ catch To ! {Tag, Reply}.
+
+
+%%% ---------------------------------------------------
+%%% Initiate the new process.
+%%% Register the name using the Rfunc function
+%%% Calls the Mod:init/Args function.
+%%% Finally an acknowledge is sent to Parent and the main
+%%% loop is entered.
+%%% ---------------------------------------------------
+%%% @hidden
+init_it(Starter, self, Name, Mod, {CandidateNodes, Workers, Arg}, Options) ->
+ if CandidateNodes == [] ->
+ erlang:error(no_candidates);
+ true ->
+ init_it(Starter, self(), Name, Mod,
+ {CandidateNodes, Workers, Arg}, Options)
+ end;
+init_it(Starter,Parent,Name,Mod,{local_only, _}=Arg,Options) ->
+ Debug = debug_options(Name, Options),
+ reg_behaviour(),
+ case catch Mod:init(Arg) of
+ {stop, Reason} ->
+ proc_lib:init_ack(Starter, {error, Reason}),
+ exit(Reason);
+ ignore ->
+ proc_lib:init_ack(Starter, ignore),
+ exit(normal);
+ {'EXIT', Reason} ->
+ proc_lib:init_ack(Starter, {error, Reason}),
+ exit(Reason);
+ {ok, State} ->
+ proc_lib:init_ack(Starter, {ok, self()}),
+ Server = #server{parent = Parent,
+ mod = Mod,
+ state = State,
+ debug = Debug},
+ loop(Server, local_only, #election{name = Name, mode = local});
+ Other ->
+ Error = {bad_return_value, Other},
+ proc_lib:init_ack(Starter, {error, Error}),
+ exit(Error)
+ end;
+init_it(Starter,Parent,Name,Mod,{CandidateNodes,Workers,Arg},Options) ->
+ Debug = debug_options(Name, Options),
+ reg_behaviour(),
+ AmCandidate = member(node(), CandidateNodes),
+ Election = init_election(CandidateNodes, Workers, #election{name = Name}),
+ case {catch Mod:init(Arg), AmCandidate} of
+ {{stop, Reason},_} ->
+ proc_lib:init_ack(Starter, {error, Reason}),
+ exit(Reason);
+ {ignore,_} ->
+ proc_lib:init_ack(Starter, ignore),
+ exit(normal);
+ {{'EXIT', Reason},_} ->
+ proc_lib:init_ack(Starter, {error, Reason}),
+ exit(Reason);
+ {{ok, State}, true} ->
+%%% NewE = broadcast(capture,Workers++(CandidateNodes -- [node()]),
+%%% Election),
+ proc_lib:init_ack(Starter, {ok, self()}),
+ begin_election(#server{parent = Parent,
+ mod = Mod,
+ state = State,
+ debug = Debug}, candidate, Election);
+ {{ok, State}, false} ->
+%%% NewE = broadcast(add_worker, CandidateNodes, Election),
+ proc_lib:init_ack(Starter, {ok, self()}),
+ begin_election(#server{parent = Parent,
+ mod = Mod,
+ state = State,
+ debug = Debug}, waiting_worker, Election);
+ Else ->
+ Error = {bad_return_value, Else},
+ proc_lib:init_ack(Starter, {error, Error}),
+ exit(Error)
+ end.
+
+reg_behaviour() ->
+ catch gproc:reg({p,l,behaviour}, ?MODULE).
+
+init_election(CandidateNodes, Workers, E) ->
+%%% dbg:tracer(),
+%%% dbg:tpl(?MODULE,lexcompare,[]),
+%%% dbg:p(self(),[m,c]),
+ AmCandidate = member(node(), CandidateNodes),
+ case AmCandidate of
+ true ->
+ E#election{mode = global,
+ candidate_nodes = CandidateNodes,
+ worker_nodes = Workers,
+ iteration = {[],
+ position(
+ node(),CandidateNodes)}};
+ false ->
+ E#election{mode = global,
+ candidate_nodes = CandidateNodes,
+ worker_nodes = Workers}
+ end.
+
+begin_election(#server{mod = Mod, state = State} = Server, candidate,
+ #election{candidate_nodes = Cands,
+ worker_nodes = Workers} = E) ->
+ case Cands of
+ [N] when N == node() ->
+ {ok, Synch, NewState} = Mod:elected(State, E),
+ NewE = broadcast({elect,Synch}, E),
+ loop(Server#server{state = NewState}, elected, NewE);
+ _ ->
+ NewE = broadcast(capture,Workers++(Cands -- [node()]), E),
+ safe_loop(Server, candidate, NewE)
+ end;
+begin_election(Server, waiting_worker, #election{candidate_nodes = Cands}=E) ->
+ NewE = broadcast(add_worker, Cands, E),
+ safe_loop(Server, waiting_worker, NewE).
+
+
+%%% ---------------------------------------------------
+%%% The MAIN loop.
+%%% ---------------------------------------------------
+
+
+safe_loop(#server{mod = Mod, state = State} = Server, Role,
+ #election{name = Name} = E) ->
+ receive
+ {system, From, Req} ->
+ #server{parent = Parent, debug = Debug} = Server,
+ sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
+ [safe, Server, Role, E]);
+ {'EXIT', _Parent, Reason} = Msg ->
+ terminate(Reason, Msg, Server, Role, E);
+ {leader,capture,Iteration,_Node,Candidate} ->
+ case Role of
+ candidate ->
+ NewE =
+ nodeup(node(Candidate),E),
+ case lexcompare(NewE#election.iteration,Iteration) of
+ less ->
+ Candidate !
+ {leader,accept,
+ NewE#election.iteration,self()},
+ safe_loop(Server, captured,
+ NewE#election{leader = Candidate});
+ greater ->
+ %% I'll get either an accept or DOWN
+ %% from Candidate later
+ safe_loop(Server, Role, NewE);
+ equal ->
+ safe_loop(Server, Role, NewE)
+ end;
+ captured ->
+ NewE = nodeup(node(Candidate), E),
+ safe_loop(Server, Role, NewE);
+ waiting_worker ->
+ NewE =
+ nodeup(node(Candidate),E),
+ safe_loop(Server, Role, NewE)
+ end;
+ {leader,add_worker,Worker} ->
+ NewE = nodeup(node(Worker), E),
+ safe_loop(Server, Role, NewE);
+ {leader,accept,Iteration,Candidate} ->
+ case Role of
+ candidate ->
+ NewE =
+ nodeup(node(Candidate),E),
+ {Captured,_} = Iteration,
+ NewIteration = % inherit all procs that have been
+ % accepted by Candidate
+ foldl(fun(C,Iter) ->
+ add_captured(Iter,C)
+ end,NewE#election.iteration,
+ [node(Candidate)|Captured]),
+ check_majority(NewE#election{
+ iteration = NewIteration}, Server);
+ captured ->
+ %% forward this to the leader
+ E#election.leader ! {leader,accept,Iteration,Candidate},
+ NewE = nodeup(node(Candidate), E),
+ safe_loop(Server, Role, NewE)
+ end;
+ {leader,elect,Synch,Candidate} ->
+ NewE =
+ case Role of
+ waiting_worker ->
+ nodeup(node(Candidate),
+ E#election{
+ leader = Candidate,
+ leadernode = node(Candidate)});
+ _ ->
+ nodeup(node(Candidate),
+ E#election{
+ leader = Candidate,
+ leadernode = node(Candidate),
+ iteration = {[],
+ position(
+ node(),
+ E#election.candidate_nodes)}
+ })
+ end,
+ {ok,NewState} = Mod:surrendered(State,Synch,NewE),
+ NewRole = case Role of
+ waiting_worker ->
+ worker;
+ _ ->
+ surrendered
+ end,
+ loop(Server#server{state = NewState}, NewRole, NewE);
+ {leader, local_only, Node, Candidate} ->
+ case lists:keysearch(node(Candidate), 2, E#election.monitored) of
+ {value, {Ref, N}} ->
+ NewE = down(Ref, {E#election.name,N},local_only,E),
+ io:format("local_only received from ~p~n"
+ "E0 = ~p~n"
+ "E1 = ~p~n", [Node, E, NewE]),
+ safe_after_down(Server, Role, NewE);
+ false ->
+ safe_loop(Server, Role, E)
+ end;
+ {'DOWN',Ref,process,{Name,_}=Who,Why} ->
+ NewE =
+ down(Ref,Who,Why,E),
+ safe_after_down(Server, Role, NewE)
+ end.
+
+safe_after_down(Server, Role, E) ->
+ case {Role,E#election.leader} of
+ {candidate,_} ->
+ check_majority(E, Server);
+ {captured,none} ->
+ check_majority(broadcast(capture,E), Server);
+ {waiting_worker,_} ->
+ safe_loop(Server, Role, E)
+ end.
+
+
+loop(#server{parent = Parent,
+ mod = Mod,
+ state = State,
+ debug = Debug} = Server, Role,
+ #election{mode = Mode, name = Name} = E) ->
+ Msg = receive
+
+ Input ->
+ Input
+ end,
+ case Msg of
+ {system, From, Req} ->
+ sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
+ [normal, Server, Role, E]);
+ {'EXIT', Parent, Reason} ->
+ terminate(Reason, Msg, Server, Role, E);
+ {leader, local_only, _, _Candidate} ->
+ loop(Server, Role, E);
+ LeaderMsg when element(1,LeaderMsg) == leader, Mode == local ->
+ Candidate = element(size(LeaderMsg), LeaderMsg),
+ Candidate ! {leader, local_only, node(), self()},
+ loop(Server, Role, E);
+ {leader,capture,_Iteration,_Node,Candidate} ->
+ NewE = nodeup(node(Candidate),E),
+ case Role of
+ R when R == surrendered; R == worker ->
+ loop(Server, Role, NewE);
+ elected ->
+ {ok,Synch,NewState} = Mod:elected(State,NewE),
+ Candidate ! {leader, elect, Synch, self()},
+ loop(Server#server{state = NewState}, Role, NewE)
+ end;
+ {leader,accept,_Iteration,Candidate} ->
+ NewE = nodeup(node(Candidate),E),
+ case Role of
+ surrendered ->
+ loop(Server, Role, NewE);
+ elected ->
+ {ok,Synch,NewState} = Mod:elected(State,NewE),
+ Candidate ! {leader, elect, Synch, self()},
+ loop(Server#server{state = NewState}, Role, NewE)
+ end;
+ {leader,elect,Synch,Candidate} ->
+ NewE =
+ case Role of
+ worker ->
+ nodeup(node(Candidate),
+ E#election{
+ leader = Candidate,
+ leadernode = node(Candidate)});
+ surrendered ->
+ nodeup(node(Candidate),
+ E#election{
+ leader = Candidate,
+ leadernode = node(Candidate),
+ iteration = {[],
+ position(
+ node(),
+ E#election.candidate_nodes)}
+ })
+ end,
+ {ok, NewState} = Mod:surrendered(State, Synch, NewE),
+ loop(Server#server{state = NewState}, Role, NewE);
+ {'DOWN',Ref,process,{Name,Node} = Who,Why} ->
+ #election{alive = PreviouslyAlive} = E,
+ NewE =
+ down(Ref,Who,Why,E),
+ case NewE#election.leader of
+ none ->
+ foreach(fun({_,From}) ->
+ reply(From,{error,leader_died})
+ end, E#election.buffered),
+ NewE1 = NewE#election{buffered = []},
+ case Role of
+ surrendered ->
+ check_majority(
+ broadcast(capture,NewE1), Server);
+ worker ->
+ safe_loop(Server, waiting_worker, NewE1)
+ end;
+ L when L == self() ->
+ case member(Node, PreviouslyAlive) of
+ true ->
+ case Mod:handle_DOWN(Node, State, E) of
+ {ok, NewState} ->
+ loop(Server#server{state = NewState},
+ Role, NewE);
+ {ok, Broadcast, NewState} ->
+ NewE1 = broadcast(
+ {from_leader,Broadcast}, NewE),
+ loop(Server#server{state = NewState},
+ Role, NewE1)
+ end;
+ false ->
+ loop(Server, Role, NewE)
+ end;
+ _ ->
+ loop(Server, Role, NewE)
+ end;
+ _Msg when Debug == [] ->
+ handle_msg(Msg, Server, Role, E);
+ _Msg ->
+ Debug1 = sys:handle_debug(Debug, {?MODULE, print_event},
+ E#election.name, {in, Msg}),
+ handle_msg(Msg, Server#server{debug = Debug1}, Role, E)
+ end.
+
+%%-----------------------------------------------------------------
+%% Callback functions for system messages handling.
+%%-----------------------------------------------------------------
+
+%% @hidden
+system_continue(_Parent, Debug, [safe, Server, Role, E]) ->
+ safe_loop(Server#server{debug = Debug}, Role, E);
+system_continue(_Parent, Debug, [normal, Server, Role, E]) ->
+ loop(Server#server{debug = Debug}, Role, E).
+
+%% @hidden
+system_terminate(Reason, _Parent, Debug, [_Mode, Server, Role, E]) ->
+ terminate(Reason, [], Server#server{debug = Debug}, Role, E).
+
+%% @hidden
+system_code_change([Mode, Server, Role, E], _Module, OldVsn, Extra) ->
+ #server{mod = Mod, state = State} = Server,
+ case catch Mod:code_change(OldVsn, State, E, Extra) of
+ {ok, NewState} ->
+ NewServer = Server#server{state = NewState},
+ {ok, [Mode, NewServer, Role, E]};
+ {ok, NewState, NewE} ->
+ NewServer = Server#server{state = NewState},
+ {ok, [Mode, NewServer, Role, NewE]};
+ Else -> Else
+ end.
+
+%%-----------------------------------------------------------------
+%% Format debug messages. Print them as the call-back module sees
+%% them, not as the real erlang messages. Use trace for that.
+%%-----------------------------------------------------------------
+%% @hidden
+print_event(Dev, {in, Msg}, Name) ->
+ case Msg of
+ {'$gen_call', {From, _Tag}, Call} ->
+ io:format(Dev, "*DBG* ~p got local call ~p from ~w~n",
+ [Name, Call, From]);
+ {'$leader_call', {From, _Tag}, Call} ->
+ io:format(Dev, "*DBG* ~p got global call ~p from ~w~n",
+ [Name, Call, From]);
+ {'$gen_cast', Cast} ->
+ io:format(Dev, "*DBG* ~p got local cast ~p~n",
+ [Name, Cast]);
+ {'$leader_cast', Cast} ->
+ io:format(Dev, "*DBG* ~p got global cast ~p~n",
+ [Name, Cast]);
+ _ ->
+ io:format(Dev, "*DBG* ~p got ~p~n", [Name, Msg])
+ end;
+print_event(Dev, {out, Msg, To, State}, Name) ->
+ io:format(Dev, "*DBG* ~p sent ~p to ~w, new state ~w~n",
+ [Name, Msg, To, State]);
+print_event(Dev, {noreply, State}, Name) ->
+ io:format(Dev, "*DBG* ~p new state ~w~n", [Name, State]);
+print_event(Dev, Event, Name) ->
+ io:format(Dev, "*DBG* ~p dbg ~p~n", [Name, Event]).
+
+
+handle_msg({'$leader_call', From, Request} = Msg,
+ #server{mod = Mod, state = State} = Server, elected = Role, E) ->
+ case catch Mod:handle_leader_call(Request, From, State, E) of
+ {reply, Reply, NState} ->
+ NewServer = reply(From, {leader,reply,Reply},
+ Server#server{state = NState}, Role, E),
+ loop(NewServer, Role, E);
+ {reply, Reply, Broadcast, NState} ->
+ NewE = broadcast({from_leader,Broadcast}, E),
+ NewServer = reply(From, {leader,reply,Reply},
+ Server#server{state = NState}, Role,
+ NewE),
+ loop(NewServer, Role, NewE);
+ {noreply, NState} = Reply ->
+ NewServer = handle_debug(Server#server{state = NState},
+ Role, E, Reply),
+ loop(NewServer, Role, E);
+ {stop, Reason, Reply, NState} ->
+ {'EXIT', R} =
+ (catch terminate(Reason, Msg,
+ Server#server{state = NState},
+ Role, E)),
+ reply(From, Reply),
+ exit(R);
+ Other ->
+ handle_common_reply(Other, Msg, Server, Role, E)
+ end;
+handle_msg({'$leader_call', From, Request} = Msg,
+ #server{mod = Mod, state = State} = Server, Role,
+ #election{mode = local} = E) ->
+ Reply = (catch Mod:handle_leader_call(Request,From,State,E)),
+ handle_call_reply(Reply, Msg, Server, Role, E);
+%%% handle_common_reply(Reply, Msg, Server, Role, E);
+handle_msg({'$leader_cast', Cast} = Msg,
+ #server{mod = Mod, state = State} = Server, Role,
+ #election{mode = local} = E) ->
+ Reply = (catch Mod:handle_leader_cast(Cast,State,E)),
+ handle_common_reply(Reply, Msg, Server, Role, E);
+handle_msg({'$leader_cast', Cast} = Msg,
+ #server{mod = Mod, state = State} = Server, elected = Role, E) ->
+ Reply = (catch Mod:handle_leader_cast(Cast, State, E)),
+ handle_common_reply(Reply, Msg, Server, Role, E);
+handle_msg({from_leader, Cmd} = Msg,
+ #server{mod = Mod, state = State} = Server, Role, E) ->
+ handle_common_reply(catch Mod:from_leader(Cmd, State, E),
+ Msg, Server, Role, E);
+handle_msg({'$leader_call', From, Request}, Server, Role,
+ #election{buffered = Buffered, leader = Leader} = E) ->
+ Ref = make_ref(),
+ Leader ! {'$leader_call', {self(),Ref}, Request},
+ NewBuffered = [{Ref,From}|Buffered],
+ loop(Server, Role, E#election{buffered = NewBuffered});
+handle_msg({Ref, {leader,reply,Reply}}, Server, Role,
+ #election{buffered = Buffered} = E) ->
+ {value, {_,From}} = keysearch(Ref,1,Buffered),
+ NewServer = reply(From, {leader,reply,Reply}, Server, Role,
+ E#election{buffered = keydelete(Ref,1,Buffered)}),
+ loop(NewServer, Role, E);
+handle_msg({'$gen_call', From, Request} = Msg,
+ #server{mod = Mod, state = State} = Server, Role, E) ->
+ Reply = (catch Mod:handle_call(Request, From, State)),
+ handle_call_reply(Reply, Msg, Server, Role, E);
+handle_msg({'$gen_cast',Msg} = Cast,
+ #server{mod = Mod, state = State} = Server, Role, E) ->
+ handle_common_reply(catch Mod:handle_cast(Msg, State),
+ Cast, Server, Role, E);
+handle_msg(Msg,
+ #server{mod = Mod, state = State} = Server, Role, E) ->
+ handle_common_reply(catch Mod:handle_info(Msg, State),
+ Msg, Server, Role, E).
+
+
+handle_call_reply(CB_reply, {_, From, _Request} = Msg, Server, Role, E) ->
+ case CB_reply of
+ {reply, Reply, NState} ->
+ NewServer = reply(From, Reply,
+ Server#server{state = NState}, Role, E),
+ loop(NewServer, Role, E);
+ {noreply, NState} = Reply ->
+ NewServer = handle_debug(Server#server{state = NState},
+ Role, E, Reply),
+ loop(NewServer, Role, E);
+ {activate, Cands, Workers, Reply, NState}
+ when E#election.mode == local ->
+ NewRole = case member(node(), Cands) of
+ true -> candidate;
+ false -> waiting_worker
+ end,
+ reply(From, Reply),
+ NServer = Server#server{state = NState},
+ NewE = init_election(Cands, Workers, E),
+ io:format("activating: NewE = ~p~n", [NewE]),
+ begin_election(NServer, NewRole, NewE);
+ {stop, Reason, Reply, NState} ->
+ {'EXIT', R} =
+ (catch terminate(Reason, Msg, Server#server{state = NState},
+ Role, E)),
+ reply(From, Reply),
+ exit(R);
+ Other ->
+ handle_common_reply(Other, Msg, Server, Role, E)
+ end.
+
+
+handle_common_reply(Reply, Msg, Server, Role, E) ->
+ case Reply of
+ {ok, NState} ->
+ NewServer = handle_debug(Server#server{state = NState},
+ Role, E, Reply),
+ loop(NewServer, Role, E);
+ {ok, Broadcast, NState} ->
+ NewE = broadcast({from_leader,Broadcast}, E),
+ NewServer = handle_debug(Server#server{state = NState},
+ Role, E, Reply),
+ loop(NewServer, Role, NewE);
+ {stop, Reason, NState} ->
+ terminate(Reason, Msg, Server#server{state = NState}, Role, E);
+ {'EXIT', Reason} ->
+ terminate(Reason, Msg, Server, Role, E);
+ _ ->
+ terminate({bad_return_value, Reply}, Msg, Server, Role, E)
+ end.
+
+
+reply({To, Tag}, Reply, #server{state = State} = Server, Role, E) ->
+ reply({To, Tag}, Reply),
+ handle_debug(Server, Role, E, {out, Reply, To, State}).
+
+
+handle_debug(#server{debug = []} = Server, _Role, _E, _Event) ->
+ Server;
+handle_debug(#server{debug = Debug} = Server, _Role, E, Event) ->
+ Debug1 = sys:handle_debug(Debug, {?MODULE, print_event},
+ E#election.name, Event),
+ Server#server{debug = Debug1}.
+
+%%% ---------------------------------------------------
+%%% Terminate the server.
+%%% ---------------------------------------------------
+
+terminate(Reason, Msg, #server{mod = Mod,
+ state = State,
+ debug = Debug}, _Role,
+ #election{name = Name}) ->
+ case catch Mod:terminate(Reason, State) of
+ {'EXIT', R} ->
+ error_info(R, Name, Msg, State, Debug),
+ exit(R);
+ _ ->
+ case Reason of
+ normal ->
+ exit(normal);
+ shutdown ->
+ exit(shutdown);
+ _ ->
+ error_info(Reason, Name, Msg, State, Debug),
+ exit(Reason)
+ end
+ end.
+
+%% Maybe we shouldn't do this? We have the crash report...
+error_info(Reason, Name, Msg, State, Debug) ->
+ format("** Generic leader ~p terminating \n"
+ "** Last message in was ~p~n"
+ "** When Server state == ~p~n"
+ "** Reason for termination == ~n** ~p~n",
+ [Name, Msg, State, Reason]),
+ sys:print_log(Debug),
+ ok.
+
+%%% ---------------------------------------------------
+%%% Misc. functions.
+%%% ---------------------------------------------------
+
+opt(Op, [{Op, Value}|_]) ->
+ {ok, Value};
+opt(Op, [_|Options]) ->
+ opt(Op, Options);
+opt(_, []) ->
+ false.
+
+debug_options(Name, Opts) ->
+ case opt(debug, Opts) of
+ {ok, Options} -> dbg_options(Name, Options);
+ _ -> dbg_options(Name, [])
+ end.
+
+dbg_options(Name, []) ->
+ Opts =
+ case init:get_argument(generic_debug) of
+ error ->
+ [];
+ _ ->
+ [log, statistics]
+ end,
+ dbg_opts(Name, Opts);
+dbg_options(Name, Opts) ->
+ dbg_opts(Name, Opts).
+
+dbg_opts(Name, Opts) ->
+ case catch sys:debug_options(Opts) of
+ {'EXIT',_} ->
+ format("~p: ignoring erroneous debug options - ~p~n",
+ [Name, Opts]),
+ [];
+ Dbg ->
+ Dbg
+ end.
+
+%%-----------------------------------------------------------------
+%% Status information
+%%-----------------------------------------------------------------
+%% @hidden
+format_status(Opt, StatusData) ->
+ [PDict, SysState, Parent, Debug, [_Mode, Server, _Role, E]] = StatusData,
+ Header = lists:concat(["Status for generic server ", E#election.name]),
+ Log = sys:get_debug(log, Debug, []),
+ #server{mod = Mod, state = State} = Server,
+ Specific =
+ case erlang:function_exported(Mod, format_status, 2) of
+ true ->
+ case catch apply(Mod, format_status, [Opt, [PDict, State]]) of
+ {'EXIT', _} -> [{data, [{"State", State}]}];
+ Else -> Else
+ end;
+ _ ->
+ [{data, [{"State", State}]}]
+ end,
+ [{header, Header},
+ {data, [{"Status", SysState},
+ {"Parent", Parent},
+ {"Logged events", Log}]} |
+ Specific].
+
+
+
+
+broadcast(Msg, #election{monitored = Monitored} = E) ->
+ %% When broadcasting the first time, we broadcast to all candidate nodes,
+ %% using broadcast/3. This function is used for subsequent broadcasts,
+ %% and we make sure only to broadcast to already known nodes.
+ %% It's the responsibility of new nodes to make themselves known through
+ %% a wider broadcast.
+ ToNodes = [N || {_,N} <- Monitored],
+ broadcast(Msg, ToNodes, E).
+
+broadcast(capture, ToNodes, #election{monitored = Monitored} = E) ->
+ ToMonitor = [N || N <- ToNodes,
+ not(keymember(N,2,Monitored))],
+ NewE =
+ foldl(fun(Node,Ex) ->
+ Ref = erlang:monitor(
+ process,{Ex#election.name,Node}),
+ Ex#election{monitored = [{Ref,Node}|
+ Ex#election.monitored]}
+ end,E,ToMonitor),
+ foreach(
+ fun(Node) ->
+ {NewE#election.name,Node} !
+ {leader,capture,NewE#election.iteration,node(),self()}
+ end,ToNodes),
+ NewE;
+broadcast({elect,Synch},ToNodes,E) ->
+ foreach(
+ fun(Node) ->
+ {E#election.name,Node} ! {leader,elect,Synch,self()}
+ end,ToNodes),
+ E;
+broadcast({from_leader, Msg}, ToNodes, E) ->
+ foreach(
+ fun(Node) ->
+ {E#election.name,Node} ! {from_leader, Msg}
+ end,ToNodes),
+ E;
+broadcast(add_worker, ToNodes, E) ->
+ foreach(
+ fun(Node) ->
+ {E#election.name,Node} ! {leader, add_worker, self()}
+ end,ToNodes),
+ E.
+
+
+
+check_majority(E, Server) ->
+ {Captured,_} = E#election.iteration,
+ AcceptMeAsLeader = length(Captured) + 1, % including myself
+ NrCandidates = length(E#election.candidate_nodes),
+ NrDown = E#election.down,
+ if AcceptMeAsLeader > NrCandidates/2 ->
+ NewE = E#election{leader = self(), leadernode = node()},
+ {ok,Synch,NewState} =
+ (Server#server.mod):elected(Server#server.state, NewE),
+ NewE1 = broadcast({elect,Synch}, NewE),
+ loop(Server#server{state = NewState}, elected, NewE1);
+ AcceptMeAsLeader+length(NrDown) == NrCandidates ->
+ NewE = E#election{leader = self(), leadernode = node()},
+ {ok,Synch,NewState} =
+ (Server#server.mod):elected(Server#server.state, NewE),
+ NewE1 = broadcast({elect,Synch}, NewE),
+ loop(Server#server{state = NewState}, elected, NewE1);
+ true ->
+ safe_loop(Server, candidate, E)
+ end.
+
+
+down(Ref,_Who,Why,E) ->
+ case lists:keysearch(Ref,1,E#election.monitored) of
+ {value, {_,Node}} ->
+ NewMonitored = if Why == local_only -> E#election.monitored;
+ true ->
+ E#election.monitored -- [{Ref,Node}]
+ end,
+ {Captured,Pos} = E#election.iteration,
+ case Node == E#election.leadernode of
+ true ->
+ E#election{leader = none,
+ leadernode = none,
+ iteration = {Captured -- [Node],
+ Pos}, % TAKE CARE !
+ down = [Node|E#election.down],
+ alive = E#election.alive -- [Node],
+ monitored = NewMonitored};
+ false ->
+ Down = case member(Node,E#election.candidate_nodes) of
+ true ->
+ [Node|E#election.down];
+ false ->
+ E#election.down
+ end,
+ E#election{iteration = {Captured -- [Node],
+ Pos}, % TAKE CARE !
+ down = Down,
+ alive = E#election.alive -- [Node],
+ monitored = NewMonitored}
+ end
+ end.
+
+
+
+%% position of element counted from end of the list
+%%
+position(X,[Head|Tail]) ->
+ case X==Head of
+ true ->
+ length(Tail);
+ false ->
+ position(X,Tail)
+ end.
+
+%% This is a multi-level comment
+%% This is the second line of the comment
+lexcompare({C1,P1},{C2,P2}) ->
+ lexcompare([{length(C1),length(C2)},{P1,P2}]).
+
+lexcompare([]) ->
+ equal;
+lexcompare([{X,Y}|Rest]) ->
+ if X<Y -> less;
+ X==Y -> lexcompare(Rest);
+ X>Y -> greater
+ end.
+
+add_captured({Captured,Pos}, CandidateNode) ->
+ {[CandidateNode|[ Node || Node <- Captured,
+ Node =/= CandidateNode ]], Pos}.
+
+nodeup(Node, #election{monitored = Monitored,
+ alive = Alive,
+ down = Down} = E) ->
+ %% make sure process is monitored from now on
+ case [ N || {_,N}<-Monitored, N==Node] of
+ [] ->
+ Ref = erlang:monitor(process,{E#election.name,Node}),
+ E#election{down = Down -- [Node],
+ alive = [Node | Alive],
+ monitored = [{Ref,Node}|Monitored]};
+ _ -> % already monitored, thus not in down
+ E#election{alive = [Node | [N || N <- Alive,
+ N =/= Node]]}
+ end.
+
428 patches/kernel/application_master.erl
@@ -0,0 +1,428 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id$
+%%
+-module(application_master).
+
+%% External exports
+-export([start_link/2, start_type/0, stop/1]).
+-export([get_child/1]).
+
+%% Internal exports
+-export([init/4, start_it/4]).
+
+-include("application_master.hrl").
+
+-record(state, {child, appl_data, children = [], procs = 0, gleader}).
+
+%%-----------------------------------------------------------------
+%% Func: start_link/1
+%% Args: ApplData = record(appl_data)
+%% Purpose: Starts an application master for the application.
+%% Called from application_controller. (The application is
+%% also started).
+%% Returns: {ok, Pid} | {error, Reason} (Pid is unregistered)
+%%-----------------------------------------------------------------
+start_link(ApplData, Type) ->
+ Parent = whereis(application_controller),
+ proc_lib:start_link(application_master, init,
+ [Parent, self(), ApplData, Type]).
+
+start_type() ->
+ group_leader() ! {start_type, self()},
+ receive
+ {start_type, Type} ->
+ Type
+ after 5000 ->
+ {error, timeout}
+ end.
+
+%%-----------------------------------------------------------------
+%% Func: stop/1
+%% Purpose: Stops the application. This function makes sure
+%% that all processes belonging to the applicication is
+%% stopped (shutdown or killed). The application master
+%% is also stopped.
+%% Returns: ok
+%%-----------------------------------------------------------------
+stop(AppMaster) -> call(AppMaster, stop).
+
+%%-----------------------------------------------------------------
+%% Func: get_child/1
+%% Purpose: Get the topmost supervisor of an application.
+%% Returns: {pid(), App}
+%%-----------------------------------------------------------------
+get_child(AppMaster) -> call(AppMaster, get_child).
+
+call(AppMaster, Req) ->
+ Tag = make_ref(),
+ Ref = erlang:monitor(process, AppMaster),
+ AppMaster ! {Req, Tag, self()},
+ receive
+ {'DOWN', Ref, process, _, _Info} ->
+ ok;
+ {Tag, Res} ->
+ erlang:demonitor(Ref),
+ receive
+ {'DOWN', Ref, process, _, _Info} ->
+ Res
+ after 0 ->
+ Res
+ end
+ end.
+
+%%%-----------------------------------------------------------------
+%%% The logical and physical process structrure is as follows:
+%%%
+%%% logical physical
+%%%
+%%% -------- --------
+%%% |AM(GL)| |AM(GL)|
+%%% -------- --------
+%%% | |
+%%% -------- --------
+%%% |Appl P| | X |
+%%% -------- --------
+%%% |
+%%% --------
+%%% |Appl P|
+%%% --------
+%%%
+%%% Where AM(GL) == Application Master (Group Leader)
+%%% Appl P == The application specific root process (child to AM)
+%%% X == A special 'invisible' process
+%%% The reason for not using the logical structrure is that
+%%% the application start function is synchronous, and
+%%% that the AM is GL. This means that if AM executed the start
+%%% function, and this function uses spawn_request/1
+%%% or io, deadlock would occur. Therefore, this function is
+%%% executed by the process X. Also, AM needs three loops;
+%%% init_loop (waiting for the start function to return)
+%%% main_loop
+%%% terminate_loop (waiting for the process to die)
+%%% In each of these loops, io and other requests are handled.
+%%%-----------------------------------------------------------------
+%%% Internal functions
+%%%-----------------------------------------------------------------
+init(Parent, Starter, ApplData, Type) ->
+ link(Parent),
+ process_flag(trap_exit, true),
+ gen:reg_behaviour(application),
+ OldGleader = group_leader(),
+ group_leader(self(), self()),
+ %% Insert ourselves as master for the process. This ensures that
+ %% the processes in the application can use get_env/1 at startup.
+ Name = ApplData#appl_data.name,
+ ets:insert(ac_tab, {{application_master, Name}, self()}),
+ State = #state{appl_data = ApplData, gleader = OldGleader},
+ case start_it(State, Type) of
+ {ok, Pid} -> % apply(M,F,A) returned ok
+ set_timer(ApplData#appl_data.maxT),
+ unlink(Starter),
+ proc_lib:init_ack(Starter, {ok,self()}),
+ main_loop(Parent, State#state{child = Pid});
+ {error, Reason} -> % apply(M,F,A) returned error
+ exit(Reason);
+ Else -> % apply(M,F,A) returned erroneous
+ exit(Else)
+ end.
+
+%%-----------------------------------------------------------------
+%% We want to start the new application synchronously, but we still
+%% want to handle io requests. So we spawn off a new process that
+%% performs the apply, and we wait for a start ack.
+%%-----------------------------------------------------------------
+start_it(State, Type) ->
+ Tag = make_ref(),
+ Pid = spawn_link(application_master, start_it, [Tag, State, self(), Type]),
+ init_loop(Pid, Tag, State, Type).
+
+
+%%-----------------------------------------------------------------
+%% These are the three different loops executed by the application_
+%% master
+%%-----------------------------------------------------------------
+init_loop(Pid, Tag, State, Type) ->
+ receive
+ IoReq when element(1, IoReq) =:= io_request ->
+ State#state.gleader ! IoReq,
+ init_loop(Pid, Tag, State, Type);
+ {Tag, Res} ->
+ Res;
+ {'EXIT', Pid, Reason} ->
+ {error, Reason};
+ {start_type, From} ->
+ From ! {start_type, Type},
+ init_loop(Pid, Tag, State, Type);
+ Other ->
+ NewState = handle_msg(Other, State),
+ init_loop(Pid, Tag, NewState, Type)
+ end.
+
+main_loop(Parent, State) ->
+ receive
+ IoReq when element(1, IoReq) =:= io_request ->
+ State#state.gleader ! IoReq,
+ main_loop(Parent, State);
+ {'EXIT', Parent, Reason} ->
+ terminate(Reason, State);
+ {'EXIT', Child, Reason} when State#state.child =:= Child ->
+ terminate(Reason, State#state{child=undefined});
+ {'EXIT', _, timeout} ->
+ terminate(normal, State);
+ {'EXIT', Pid, _Reason} ->
+ Children = lists:delete(Pid, State#state.children),
+ Procs = State#state.procs - 1,
+ main_loop(Parent, State#state{children=Children, procs=Procs});
+ {start_type, From} ->
+ From ! {start_type, local},
+ main_loop(Parent, State);
+ Other ->
+ NewState = handle_msg(Other, State),
+ main_loop(Parent, NewState)
+ end.
+
+terminate_loop(Child, State) ->
+ receive
+ IoReq when element(1, IoReq) =:= io_request ->
+ State#state.gleader ! IoReq,
+ terminate_loop(Child, State);
+ {'EXIT', Child, _} ->
+ ok;
+ Other ->
+ NewState = handle_msg(Other, State),
+ terminate_loop(Child, NewState)
+ end.
+
+
+%%-----------------------------------------------------------------
+%% The Application Master is linked to *all* processes in the group
+%% (application).
+%%-----------------------------------------------------------------
+handle_msg({get_child, Tag, From}, State) ->
+ From ! {Tag, get_child_i(State#state.child)},
+ State;
+handle_msg({stop, Tag, From}, State) ->
+ catch terminate(normal, State),
+ From ! {Tag, ok},
+ exit(normal);
+handle_msg(_, State) ->
+ State.
+
+
+terminate(Reason, State) ->
+ terminate_child(State#state.child, State),
+ kill_children(State#state.children),
+ exit(Reason).
+
+
+
+
+%%======================================================================
+%%======================================================================
+%%======================================================================
+%% This is the process X above...
+%%======================================================================
+%%======================================================================
+%%======================================================================
+
+%%======================================================================
+%% Start an application.
+%% If the start_phases is defined in the .app file, the application is
+%% to be started in one or several start phases.
+%% If the Module in the mod-key is set to application_starter then
+%% the generic help module application_starter is used to control
+%% the start.
+%%======================================================================
+
+start_it(Tag, State, From, Type) ->
+ process_flag(trap_exit, true),
+ ApplData = State#state.appl_data,
+ case {ApplData#appl_data.phases, ApplData#appl_data.mod} of
+ {undefined, _} ->
+ start_it_old(Tag, From, Type, ApplData);
+ {Phases, {application_starter, [M, A]}} ->
+ start_it_new(Tag, From, Type, M, A, Phases,
+ [ApplData#appl_data.name]);
+ {Phases, {M, A}} ->
+ start_it_new(Tag, From, Type, M, A, Phases,
+ [ApplData#appl_data.name]);
+ {OtherP, OtherM} ->
+ From ! {Tag, {error, {bad_keys, {{mod, OtherM},
+ {start_phases, OtherP}}}}}
+ end.
+
+
+%%%-----------------------------------------------------
+%%% No start phases are defined
+%%%-----------------------------------------------------
+start_it_old(Tag, From, Type, ApplData) ->
+ {M,A} = ApplData#appl_data.mod,
+ case catch M:start(Type, A) of
+ {ok, Pid} ->
+ link(Pid),
+ {ok, self()},
+ From ! {Tag, {ok, self()}},
+ loop_it(From, Pid, M, []);
+ {ok, Pid, AppState} ->
+ link(Pid),
+ {ok, self()},
+ From ! {Tag, {ok, self()}},
+ loop_it(From, Pid, M, AppState);
+ {'EXIT', normal} ->
+ From ! {Tag, {error, {{'EXIT',normal},{M,start,[Type,A]}}}};
+ {error, Reason} ->
+ From ! {Tag, {error, {Reason, {M,start,[Type,A]}}}};
+ Other ->
+ From ! {Tag, {error, {bad_return,{{M,start,[Type,A]},Other}}}}
+ end.
+
+
+%%%-----------------------------------------------------
+%%% Start phases are defined
+%%%-----------------------------------------------------
+start_it_new(Tag, From, Type, M, A, Phases, Apps) ->
+ case catch start_the_app(Type, M, A, Phases, Apps) of
+ {ok, Pid, AppState} ->
+ From ! {Tag, {ok, self()}},
+ loop_it(From, Pid, M, AppState);
+ Error ->
+ From ! {Tag, Error}
+ end.
+
+
+%%%=====================================================
+%%% Start the application in the defined phases,
+%%% but first the supervisors are starter.
+%%%=====================================================
+start_the_app(Type, M, A, Phases, Apps) ->
+ case start_supervisor(Type, M, A) of
+ {ok, Pid, AppState} ->
+ link(Pid),
+ case application_starter:start(Phases, Type, Apps) of
+ ok ->
+ {ok, Pid, AppState};
+ Error2 ->
+ unlink(Pid),
+ Error2
+ end;
+ Error ->
+ Error
+ end.
+
+%%%-------------------------------------------------------------
+%%% Start the supervisors
+%%%-------------------------------------------------------------
+start_supervisor(Type, M, A) ->
+ case catch M:start(Type, A) of
+ {ok, Pid} ->
+ {ok, Pid, []};
+ {ok, Pid, AppState} ->
+ {ok, Pid, AppState};
+ {error, Reason} ->
+ {error, {Reason, {M, start, [Type, A]}}};
+ {'EXIT', normal} ->
+ {error, {{'EXIT', normal}, {M, start, [Type, A]}}};
+ Other ->
+ {error, {bad_return, {{M, start, [Type, A]}, Other}}}
+ end.
+
+
+
+
+%%======================================================================
+%%
+%%======================================================================
+
+loop_it(Parent, Child, Mod, AppState) ->
+ receive
+ {Parent, get_child} ->
+ Parent ! {self(), Child, Mod},
+ loop_it(Parent, Child, Mod, AppState);
+ {Parent, terminate} ->
+ NewAppState = prep_stop(Mod, AppState),
+ exit(Child, shutdown),
+ receive
+ {'EXIT', Child, _} -> ok
+ end,
+ catch Mod:stop(NewAppState),
+ exit(normal);
+ {'EXIT', Parent, Reason} ->
+ NewAppState = prep_stop(Mod, AppState),
+ exit(Child, Reason),
+ receive
+ {'EXIT', Child, Reason2} ->
+ exit(Reason2)
+ end,
+ catch Mod:stop(NewAppState);
+ {'EXIT', Child, Reason} -> % forward *all* exit reasons (inc. normal)
+ NewAppState = prep_stop(Mod, AppState),
+ catch Mod:stop(NewAppState),
+ exit(Reason);
+ _ ->
+ loop_it(Parent, Child, Mod, AppState)
+ end.
+
+prep_stop(Mod, AppState) ->
+ case catch Mod:prep_stop(AppState) of
+ {'EXIT', {undef, _}} ->
+ AppState;
+ {'EXIT', Reason} ->
+ error_logger:error_report([{?MODULE, shutdown_error},
+ {Mod, {prep_stop, [AppState]}},
+ {error_info, Reason}]),
+ AppState;
+ NewAppState ->
+ NewAppState
+ end.
+
+get_child_i(Child) ->
+ Child ! {self(), get_child},
+ receive
+ {Child, GrandChild, Mod} -> {GrandChild, Mod}
+ end.
+
+terminate_child_i(Child, State) ->
+ Child ! {self(), terminate},
+ terminate_loop(Child, State).
+
+%% Try to shutdown the child gently
+terminate_child(undefined, _) -> ok;
+terminate_child(Child, State) ->
+ terminate_child_i(Child, State).
+
+kill_children(Children) ->
+ lists:foreach(fun(Pid) -> exit(Pid, kill) end, Children),
+ kill_all_procs().
+
+kill_all_procs() ->
+ kill_all_procs_1(processes(), self(), 0).
+
+kill_all_procs_1([Self|Ps], Self, N) ->
+ kill_all_procs_1(Ps, Self, N);
+kill_all_procs_1([P|Ps], Self, N) ->
+ case process_info(P, group_leader) of
+ {group_leader,Self} ->
+ exit(P, kill),
+ kill_all_procs_1(Ps, Self, N+1);
+ _ ->
+ kill_all_procs_1(Ps, Self, N)
+ end;
+kill_all_procs_1([], _, 0) -> ok;
+kill_all_procs_1([], _, _) -> kill_all_procs().
+
+set_timer(infinity) -> ok;
+set_timer(Time) -> timer:exit_after(Time, timeout).
110 patches/kernel/kernel.app.src
@@ -0,0 +1,110 @@
+%% This is an -*- erlang -*- file.
+{application, kernel,
+ [
+ {description, "ERTS CXC 138 10"},
+ {vsn, "%VSN%"},
+ {modules, [application,
+ application_controller,
+ application_master,
+ application_starter,
+ auth,
+ code,
+ code_aux,
+ packages,
+ code_server,
+ dist_util,
+ erl_boot_server,
+ erl_distribution,
+ erl_prim_loader,
+ erl_reply,
+ erlang,
+ error_handler,
+ error_logger,
+ file,
+ file_server,
+ file_io_server,
+ prim_file,
+ global,
+ global_group,
+ global_search,
+ gproc,
+ gen_leader,
+ group,
+ heart,
+ hipe_unified_loader,
+ inet6_tcp,
+ inet6_tcp_dist,
+ inet6_udp,
+ inet_config,
+ inet_hosts,
+ inet_gethost_native,
+ inet_tcp_dist,
+ init,
+ kernel,
+ kernel_config,
+ net,
+ net_adm,
+ net_kernel,
+ os,
+ ram_file,
+ rpc,
+ user,
+ user_drv,
+ user_sup,
+ disk_log,
+ disk_log_1,
+ disk_log_server,
+ disk_log_sup,
+ dist_ac,
+ erl_ddll,
+ erl_epmd,
+ erts_debug,
+ gen_tcp,
+ gen_udp,
+ gen_sctp,
+ prim_inet,
+ inet,
+ inet_db,
+ inet_dns,
+ inet_parse,
+ inet_res,
+ inet_tcp,
+ inet_udp,
+ inet_sctp,
+ pg2,
+ seq_trace,
+ wrap_log_reader,
+ zlib,
+ otp_ring0]},
+ {registered, [application_controller,
+ erl_reply,
+ auth,
+ boot_server,
+ code_server,
+ disk_log_server,
+ disk_log_sup,
+ erl_prim_loader,
+ error_logger,
+ file_server_2,
+ fixtable_server,
+ global_group,
+ global_name_server,
+ gproc,
+ heart,
+ init,
+ kernel_config,
+ kernel_sup,
+ net_kernel,
+ net_sup,
+ rex,
+ user,
+ os_server,
+ ddll_server,
+ erl_epmd,
+ inet_db,
+ pg2]},
+ {applications, []},
+ {env, [{error_logger, tty}]},
+ {mod, {kernel, []}}
+ ]
+}.
306 patches/kernel/kernel.erl
@@ -0,0 +1,306 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id$
+%%
+-module(kernel).
+
+-behaviour(supervisor).
+
+%% External exports
+-export([start/2, init/1, stop/1]).
+-export([config_change/3]).
+
+%%%-----------------------------------------------------------------
+%%% The kernel is the first application started.
+%%% Callback functions for the kernel application.
+%%%-----------------------------------------------------------------
+start(_, []) ->
+ {ok, _} = gproc:start_local(),
+ case supervisor:start_link({local, kernel_sup}, kernel, []) of
+ {ok, Pid} ->
+ Type = get_error_logger_type(),
+ error_logger:swap_handler(Type),
+ {ok, Pid, []};
+ Error -> Error
+ end.
+
+stop(_State) ->
+ ok.
+
+%%-------------------------------------------------------------------
+%% Some configuration parameters for kernel are changed
+%%-------------------------------------------------------------------
+config_change(Changed, New, Removed) ->
+ do_distribution_change(Changed, New, Removed),
+ do_global_groups_change(Changed, New, Removed),
+ ok.
+
+get_error_logger_type() ->
+ case application:get_env(kernel, error_logger) of
+ {ok, tty} -> tty;
+ {ok, {file, File}} when is_list(File) -> {logfile, File};
+ {ok, false} -> false;
+ {ok, silent} -> silent;
+ undefined -> tty; % default value
+ {ok, Bad} -> exit({bad_config, {kernel, {error_logger, Bad}}})
+ end.
+
+%%%-----------------------------------------------------------------
+%%% The process structure in kernel is as shown in the figure.
+%%%
+%%% ---------------
+%%% | kernel_sup (A)|
+%%% ---------------
+%%% |
+%%% -------------------------------
+%%% | | |
+%%% <std services> ------------- -------------
+%%% (file,code, | erl_dist (A)| | safe_sup (1)|
+%%% rpc, ...) ------------- -------------
+%%% | |
+%%% (net_kernel, (disk_log, pg2,
+%%% auth, ...) ...)
+%%%
+%%% The rectangular boxes are supervisors. All supervisors except
+%%% for kernel_safe_sup terminates the enitre erlang node if any of
+%%% their children dies. Any child that can't be restarted in case
+%%% of failure must be placed under one of these supervisors. Any
+%%% other child must be placed under safe_sup. These children may
+%%% be restarted. Be aware that if a child is restarted the old state
+%%% and all data will be lost.
+%%%-----------------------------------------------------------------
+%%% Callback functions for the kernel_sup supervisor.
+%%%-----------------------------------------------------------------
+
+init([]) ->
+ SupFlags = {one_for_all, 0, 1},
+
+ Config = {kernel_config,
+ {kernel_config, start_link, []},
+ permanent, 2000, worker, [kernel_config]},
+ Code = {code_server,
+ {code, start_link, get_code_args()},
+ permanent, 2000, worker, [code]},
+ File = {file_server_2,
+ {file_server, start_link, []},
+ permanent, 2000, worker,
+ [file, file_server, file_io_server, prim_file]},
+ User = {user,
+ {user_sup, start, []},
+ temporary, 2000, supervisor, [user_sup]},
+
+ case init:get_argument(mode) of
+ {ok, [["minimal"]]} ->
+
+ SafeSupervisor = {kernel_safe_sup,
+ {supervisor, start_link,
+ [{local, kernel_safe_sup}, ?MODULE, safe]},
+ permanent, infinity, supervisor, [?MODULE]},
+
+ {ok, {SupFlags,
+ [File, Code, User,
+ Config, SafeSupervisor]}};
+ _ ->
+ Rpc = {rex, {rpc, start_link, []},
+ permanent, 2000, worker, [rpc]},
+ Global = {global_name_server, {global, start_link, []},
+ permanent, 2000, worker, [global]},
+ Glo_grp = {global_group, {global_group,start_link,[]},
+ permanent, 2000, worker, [global_group]},
+ InetDb = {inet_db, {inet_db, start_link, []},
+ permanent, 2000, worker, [inet_db]},
+ NetSup = {net_sup, {erl_distribution, start_link, []},
+ permanent, infinity, supervisor,[erl_distribution]},
+ DistAC = start_dist_ac(),
+
+ GProc = {gproc, {gproc, go_global, []},
+ permanent, 3000, worker, [gproc]},
+
+ Timer = start_timer(),
+
+ SafeSupervisor = {kernel_safe_sup,
+ {supervisor, start_link,
+ [{local, kernel_safe_sup}, ?MODULE, safe]},
+ permanent, infinity, supervisor, [?MODULE]},
+
+ {ok, {SupFlags,
+ [Rpc, Global, InetDb | DistAC] ++
+ [NetSup, Glo_grp, File, Code,
+ User, Config, GProc, SafeSupervisor] ++ Timer}}
+ end;
+
+init(safe) ->
+ SupFlags = {one_for_one, 4, 3600},
+ Boot = start_boot_server(),
+ DiskLog = start_disk_log(),
+ Pg2 = start_pg2(),
+ {ok, {SupFlags, Boot ++ DiskLog ++ Pg2}}.
+
+get_code_args() ->
+ case init:get_argument(nostick) of
+ {ok, [[]]} -> [[nostick]];
+ _ -> []
+ end.
+
+start_dist_ac() ->
+ Spec = [{dist_ac,{dist_ac,start_link,[]},permanent,2000,worker,[dist_ac]}],
+ case application:get_env(kernel, start_dist_ac) of
+ {ok, true} -> Spec;
+ {ok, false} -> [];
+ undefined ->
+ case application:get_env(kernel, distributed) of
+ {ok, _} -> Spec;
+ _ -> []
+ end
+ end.
+
+start_boot_server() ->
+ case application:get_env(kernel, start_boot_server) of
+ {ok, true} ->
+ Args = get_boot_args(),
+ [{boot_server, {erl_boot_server, start_link, [Args]}, permanent,
+ 1000, worker, [erl_boot_server]}];
+ _ ->
+ []
+ end.
+
+get_boot_args() ->
+ case application:get_env(kernel, boot_server_slaves) of
+ {ok, Slaves} -> Slaves;
+ _ -> []
+ end.
+
+start_disk_log() ->
+ case application:get_env(kernel, start_disk_log) of
+ {ok, true} ->
+ [{disk_log_server,
+ {disk_log_server, start_link, []},
+ permanent, 2000, worker, [disk_log_server]},
+ {disk_log_sup, {disk_log_sup, start_link, []}, permanent,
+ 1000, supervisor, [disk_log_sup]}];
+ _ ->
+ []
+ end.
+
+start_pg2() ->
+ case application:get_env(kernel, start_pg2) of
+ {ok, true} ->
+ [{pg2, {pg2, start_link, []}, permanent, 1000, worker, [pg2]}];
+ _ ->
+ []
+ end.
+
+start_timer() ->
+ case application:get_env(kernel, start_timer) of
+ {ok, true} ->
+ [{timer_server, {timer, start_link, []}, permanent, 1000, worker,
+ [timer]}];
+ _ ->
+ []
+ end.
+
+
+
+
+
+%%-----------------------------------------------------------------
+%% The change of the distributed parameter is taken care of here
+%%-----------------------------------------------------------------
+do_distribution_change(Changed, New, Removed) ->
+ %% check if the distributed parameter is changed. It is not allowed
+ %% to make a local application to a distributed one, or vice versa.
+ case is_dist_changed(Changed, New, Removed) of
+ %%{changed, new, removed}
+ {false, false, false} ->
+ ok;
+ {C, false, false} ->
+ %% At last, update the parameter.
+ gen_server:call(dist_ac, {distribution_changed, C}, infinity);
+ {false, _, false} ->
+ error_logger:error_report("Distribution not changed: "
+ "Not allowed to add the 'distributed' "
+ "parameter."),
+ {error, {distribution_not_changed, "Not allowed to add the "
+ "'distributed' parameter"}};
+ {false, false, _} ->
+ error_logger:error_report("Distribution not changed: "
+ "Not allowed to remove the "
+ "distribution parameter."),
+ {error, {distribution_not_changed, "Not allowed to remove the "
+ "'distributed' parameter"}}
+ end.
+
+%%-----------------------------------------------------------------
+%% Check if distribution is changed in someway.
+%%-----------------------------------------------------------------
+is_dist_changed(Changed, New, Removed) ->
+ C = case lists:keysearch(distributed, 1, Changed) of
+ false ->
+ false;
+ {value, {distributed, NewDistC}} ->
+ NewDistC
+ end,
+ N = case lists:keysearch(distributed, 1, New) of
+ false ->
+ false;
+ {value, {distributed, NewDistN}} ->
+ NewDistN
+ end,
+ R = lists:member(distributed, Removed),
+ {C, N, R}.
+
+
+
+%%-----------------------------------------------------------------
+%% The change of the global_groups parameter is taken care of here
+%%-----------------------------------------------------------------
+do_global_groups_change(Changed, New, Removed) ->
+ %% check if the global_groups parameter is changed.
+
+ case is_gg_changed(Changed, New, Removed) of
+ %%{changed, new, removed}
+ {false, false, false} ->
+ ok;
+ {C, false, false} ->
+ %% At last, update the parameter.
+ global_group:global_groups_changed(C);
+ {false, N, false} ->
+ global_group:global_groups_added(N);
+ {false, false, R} ->
+ global_group:global_groups_removed(R)
+ end.
+
+%%-----------------------------------------------------------------
+%% Check if global_groups is changed in someway.
+%%-----------------------------------------------------------------
+is_gg_changed(Changed, New, Removed) ->
+ C = case lists:keysearch(global_groups, 1, Changed) of
+ false ->
+ false;
+ {value, {global_groups, NewDistC}} ->
+ NewDistC
+ end,
+ N = case lists:keysearch(global_groups, 1, New) of
+ false ->
+ false;
+ {value, {global_groups, NewDistN}} ->
+ NewDistN
+ end,
+ R = lists:member(global_groups, Removed),
+ {C, N, R}.
+
+
+
366 patches/stdlib/gen.erl
@@ -0,0 +1,366 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id$
+%%
+-module(gen).
+
+%%%-----------------------------------------------------------------
+%%% This module implements the really generic stuff of the generic
+%%% standard behaviours (e.g. gen_server, gen_fsm).
+%%%
+%%% The standard behaviour should export init_it/6.
+%%%-----------------------------------------------------------------
+-export([start/5, start/6, debug_options/1,
+ call/3, call/4, reply/2]).
+-export([reg_behaviour/1]).
+-export([init_it/6, init_it/7]).
+
+-define(default_timeout, 5000).
+
+%%-----------------------------------------------------------------
+%% Starts a generic process.
+%% start(GenMod, LinkP, Mod, Args, Options)
+%% start(GenMod, LinkP, Name, Mod, Args, Options)
+%% start_link(Mod, Args, Options)
+%% start_link(Name, Mod, Args, Options) where:
+%% Name = {local, atom()} | {global, atom()}
+%% Mod = atom(), callback module implementing the 'real' fsm
+%% Args = term(), init arguments (to Mod:init/1)
+%% Options = [{debug, [Flag]}]
+%% Flag = trace | log | {logfile, File} | statistics | debug
+%% (debug == log && statistics)
+%% Returns: {ok, Pid} |
+%% {error, {already_started, Pid}} |
+%% {error, Reason}
+%%-----------------------------------------------------------------
+start(GenMod, LinkP, Name, Mod, Args, Options) ->
+ case where(Name) of
+ undefined ->
+ do_spawn(GenMod, LinkP, Name, Mod, Args, Options);
+ Pid ->
+ {error, {already_started, Pid}}
+ end.
+
+start(GenMod, LinkP, Mod, Args, Options) ->
+ do_spawn(GenMod, LinkP, Mod, Args, Options).
+
+%%-----------------------------------------------------------------
+%% Spawn the process (and link) maybe at another node.
+%% If spawn without link, set parent to our selves "self"!!!
+%%-----------------------------------------------------------------
+do_spawn(