Просмотр исходного кода

first impl. of standby monitors

Ulf Wiger 11 лет назад
Родитель
Сommit
41fe220a4e
9 измененных файлов с 376 добавлено и 128 удалено
  1. 15 15
      README.md
  2. 1 0
      doc/edoc-info
  3. 25 6
      doc/gproc.md
  4. 15 1
      doc/gproc_dist.md
  5. 32 4
      doc/gproc_lib.md
  6. 102 28
      src/gproc.erl
  7. 122 59
      src/gproc_dist.erl
  8. 59 15
      src/gproc_lib.erl
  9. 5 0
      src/gproc_monitor.erl

+ 15 - 15
README.md

@@ -77,8 +77,8 @@ alternative sources, and cache them for efficient lookup. Caching also provides
 a way to see which processes rely on certain configuration values, as well as
 which values they actually ended up using.
 
-See [`gproc:get_env/4`](http://github.com/esl/gproc/blob/master/doc/gproc.md#get_env-4), [`gproc:get_set_env/4`](http://github.com/esl/gproc/blob/master/doc/gproc.md#get_set_env-4) and
-[`gproc:set_env/5`](http://github.com/esl/gproc/blob/master/doc/gproc.md#set_env-5) for details.
+See [`gproc:get_env/4`](http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc.md#get_env-4), [`gproc:get_set_env/4`](http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc.md#get_set_env-4) and
+[`gproc:set_env/5`](http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc.md#set_env-5) for details.
 
 
 ## Testing ##
@@ -98,23 +98,23 @@ global gproc.
 By default, `./rebar doc` generates Github-flavored Markdown files.
 If you want to change this, remove the `edoc_opts` line from `rebar.config`.
 Gproc was first introduced at the ACM SIGPLAN Erlang Workshop in
-Freiburg 2007 ([Paper available here](http://github.com/esl/gproc/blob/master/doc/erlang07-wiger.pdf)).
+Freiburg 2007 ([Paper available here](http://github.com/esl/gproc/blob/uw-standby-monitors/doc/erlang07-wiger.pdf)).
 
 
 ## Modules ##
 
 
 <table width="100%" border="0" summary="list of modules">
-<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc.md" class="module">gproc</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_app.md" class="module">gproc_app</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_bcast.md" class="module">gproc_bcast</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_dist.md" class="module">gproc_dist</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_info.md" class="module">gproc_info</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_init.md" class="module">gproc_init</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_lib.md" class="module">gproc_lib</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_monitor.md" class="module">gproc_monitor</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_pool.md" class="module">gproc_pool</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_ps.md" class="module">gproc_ps</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_pt.md" class="module">gproc_pt</a></td></tr>
-<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_sup.md" class="module">gproc_sup</a></td></tr></table>
+<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc.md" class="module">gproc</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_app.md" class="module">gproc_app</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_bcast.md" class="module">gproc_bcast</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_dist.md" class="module">gproc_dist</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_info.md" class="module">gproc_info</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_init.md" class="module">gproc_init</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_lib.md" class="module">gproc_lib</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_monitor.md" class="module">gproc_monitor</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_pool.md" class="module">gproc_pool</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_ps.md" class="module">gproc_ps</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_pt.md" class="module">gproc_pt</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/uw-standby-monitors/doc/gproc_sup.md" class="module">gproc_sup</a></td></tr></table>
 

+ 1 - 0
doc/edoc-info

@@ -1,3 +1,4 @@
+%% encoding: UTF-8
 {application,gproc}.
 {packages,[]}.
 {modules,[gproc,gproc_app,gproc_bcast,gproc_dist,gproc_info,gproc_init,

+ 25 - 6
doc/gproc.md

@@ -282,8 +282,8 @@ unique_id() = {n | a, <a href="#type-scope">scope()</a>, any()}
 <table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#add_global_aggr_counter-1">add_global_aggr_counter/1</a></td><td>Registers a global (unique) aggregated counter.</td></tr><tr><td valign="top"><a href="#add_global_counter-2">add_global_counter/2</a></td><td>Registers a global (non-unique) counter.</td></tr><tr><td valign="top"><a href="#add_global_name-1">add_global_name/1</a></td><td>Registers a global (unique) name.</td></tr><tr><td valign="top"><a href="#add_global_property-2">add_global_property/2</a></td><td>Registers a global (non-unique) property.</td></tr><tr><td valign="top"><a href="#add_local_aggr_counter-1">add_local_aggr_counter/1</a></td><td>Registers a local (unique) aggregated counter.</td></tr><tr><td valign="top"><a href="#add_local_counter-2">add_local_counter/2</a></td><td>Registers a local (non-unique) counter.</td></tr><tr><td valign="top"><a href="#add_local_name-1">add_local_name/1</a></td><td>Registers a local (unique) name.</td></tr><tr><td valign="top"><a href="#add_local_property-2">add_local_property/2</a></td><td>Registers a local (non-unique) property.</td></tr><tr><td valign="top"><a href="#add_shared_local_counter-2">add_shared_local_counter/2</a></td><td>Registers a local shared (unique) counter.</td></tr><tr><td valign="top"><a href="#audit_process-1">audit_process/1</a></td><td></td></tr><tr><td valign="top"><a href="#await-1">await/1</a></td><td>Equivalent to <a href="#await-2"><tt>await(Key, infinity)</tt></a>.</td></tr><tr><td valign="top"><a href="#await-2">await/2</a></td><td>Wait for a local name to be registered.</td></tr><tr><td valign="top"><a href="#await-3">await/3</a></td><td>Wait for a local name to be registered on <code>Node</code>.</td></tr><tr><td valign="top"><a href="#bcast-2">bcast/2</a></td><td>Equivalent to <a href="#bcast-3"><tt>bcast(nodes(), Key, Msg)</tt></a>.</td></tr><tr><td valign="top"><a href="#bcast-3">bcast/3</a></td><td>Sends a message to processes corresponding to Key on Nodes.</td></tr><tr><td valign="top"><a href="#cancel_wait-2">cancel_wait/2</a></td><td>Cancels a previous call to nb_wait/1.</td></tr><tr><td valign="top"><a href="#cancel_wait-3">cancel_wait/3</a></td><td>Cancels a previous call to nb_wait/2.</td></tr><tr><td valign="top"><a href="#cancel_wait_or_monitor-1">cancel_wait_or_monitor/1</a></td><td></td></tr><tr><td valign="top"><a href="#default-1">default/1</a></td><td></td></tr><tr><td valign="top"><a href="#demonitor-2">demonitor/2</a></td><td>Remove a monitor on a registered name
 This function is the reverse of monitor/1.</td></tr><tr><td valign="top"><a href="#first-1">first/1</a></td><td>Behaves as ets:first(Tab) for a given type of registration.</td></tr><tr><td valign="top"><a href="#get_attribute-2">get_attribute/2</a></td><td>Get attribute value of <code>Attr</code> associated with <code>Key</code> for most likely Pid.</td></tr><tr><td valign="top"><a href="#get_attribute-3">get_attribute/3</a></td><td>Get the attribute value of <code>Attr</code> associated with <code>Key</code> for process Pid.</td></tr><tr><td valign="top"><a href="#get_attribute_shared-2">get_attribute_shared/2</a></td><td>Get the attribute value of <code>Attr</code> associated with the shared <code>Key</code>.</td></tr><tr><td valign="top"><a href="#get_attributes-1">get_attributes/1</a></td><td>Get attributes associated with registration.</td></tr><tr><td valign="top"><a href="#get_attributes-2">get_attributes/2</a></td><td>Returns the list of attributes associated with the registration.</td></tr><tr><td valign="top"><a href="#get_env-3">get_env/3</a></td><td>Equivalent to <a href="#get_env-4"><tt>get_env(Scope, App, Key, [app_env])</tt></a>.</td></tr><tr><td valign="top"><a href="#get_env-4">get_env/4</a></td><td>Read an environment value, potentially cached as a <code>gproc_env</code> property.</td></tr><tr><td valign="top"><a href="#get_set_env-3">get_set_env/3</a></td><td>Equivalent to <a href="#get_set_env-4"><tt>get_set_env(Scope, App, Key, [app_env])</tt></a>.</td></tr><tr><td valign="top"><a href="#get_set_env-4">get_set_env/4</a></td><td>Fetch and cache an environment value, if not already cached.</td></tr><tr><td valign="top"><a href="#get_value-1">get_value/1</a></td><td>Reads the value stored with a key registered to the current process.</td></tr><tr><td valign="top"><a href="#get_value-2">get_value/2</a></td><td>Reads the value stored with a key registered to the process Pid.</td></tr><tr><td valign="top"><a href="#get_value_shared-1">get_value_shared/1</a></td><td>Reads the value stored with a shared key.</td></tr><tr><td valign="top"><a href="#give_away-2">give_away/2</a></td><td>Atomically transfers the key <code>From</code> to the process identified by <code>To</code>.</td></tr><tr><td valign="top"><a href="#goodbye-0">goodbye/0</a></td><td>Unregister all items of the calling process and inform gproc
 to forget about the calling process.</td></tr><tr><td valign="top"><a href="#i-0">i/0</a></td><td>Similar to the built-in shell command <code>i()</code> but inserts information
-about names and properties registered in Gproc, where applicable.</td></tr><tr><td valign="top"><a href="#info-1">info/1</a></td><td>Similar to <code>process_info(Pid)</code> but with additional gproc info.</td></tr><tr><td valign="top"><a href="#info-2">info/2</a></td><td>Similar to process_info(Pid, Item), but with additional gproc info.</td></tr><tr><td valign="top"><a href="#last-1">last/1</a></td><td>Behaves as ets:last(Tab) for a given type of registration.</td></tr><tr><td valign="top"><a href="#lookup_global_aggr_counter-1">lookup_global_aggr_counter/1</a></td><td>Lookup a global (unique) aggregated counter and returns its value.</td></tr><tr><td valign="top"><a href="#lookup_global_counters-1">lookup_global_counters/1</a></td><td>Look up all global (non-unique) instances of a given Counter.</td></tr><tr><td valign="top"><a href="#lookup_global_name-1">lookup_global_name/1</a></td><td>Lookup a global unique name.</td></tr><tr><td valign="top"><a href="#lookup_global_properties-1">lookup_global_properties/1</a></td><td>Look up all global (non-unique) instances of a given Property.</td></tr><tr><td valign="top"><a href="#lookup_local_aggr_counter-1">lookup_local_aggr_counter/1</a></td><td>Lookup a local (unique) aggregated counter and returns its value.</td></tr><tr><td valign="top"><a href="#lookup_local_counters-1">lookup_local_counters/1</a></td><td>Look up all local (non-unique) instances of a given Counter.</td></tr><tr><td valign="top"><a href="#lookup_local_name-1">lookup_local_name/1</a></td><td>Lookup a local unique name.</td></tr><tr><td valign="top"><a href="#lookup_local_properties-1">lookup_local_properties/1</a></td><td>Look up all local (non-unique) instances of a given Property.</td></tr><tr><td valign="top"><a href="#lookup_pid-1">lookup_pid/1</a></td><td>Lookup the Pid stored with a key.</td></tr><tr><td valign="top"><a href="#lookup_pids-1">lookup_pids/1</a></td><td>Returns a list of pids with the published key Key.</td></tr><tr><td valign="top"><a href="#lookup_value-1">lookup_value/1</a></td><td>Lookup the value stored with a key.</td></tr><tr><td valign="top"><a href="#lookup_values-1">lookup_values/1</a></td><td>Retrieve the <code>{Pid,Value}</code> pairs corresponding to Key.</td></tr><tr><td valign="top"><a href="#monitor-1">monitor/1</a></td><td>monitor a registered name
-This function works much like erlang:monitor(process, Pid), but monitors
+about names and properties registered in Gproc, where applicable.</td></tr><tr><td valign="top"><a href="#info-1">info/1</a></td><td>Similar to <code>process_info(Pid)</code> but with additional gproc info.</td></tr><tr><td valign="top"><a href="#info-2">info/2</a></td><td>Similar to process_info(Pid, Item), but with additional gproc info.</td></tr><tr><td valign="top"><a href="#last-1">last/1</a></td><td>Behaves as ets:last(Tab) for a given type of registration.</td></tr><tr><td valign="top"><a href="#lookup_global_aggr_counter-1">lookup_global_aggr_counter/1</a></td><td>Lookup a global (unique) aggregated counter and returns its value.</td></tr><tr><td valign="top"><a href="#lookup_global_counters-1">lookup_global_counters/1</a></td><td>Look up all global (non-unique) instances of a given Counter.</td></tr><tr><td valign="top"><a href="#lookup_global_name-1">lookup_global_name/1</a></td><td>Lookup a global unique name.</td></tr><tr><td valign="top"><a href="#lookup_global_properties-1">lookup_global_properties/1</a></td><td>Look up all global (non-unique) instances of a given Property.</td></tr><tr><td valign="top"><a href="#lookup_local_aggr_counter-1">lookup_local_aggr_counter/1</a></td><td>Lookup a local (unique) aggregated counter and returns its value.</td></tr><tr><td valign="top"><a href="#lookup_local_counters-1">lookup_local_counters/1</a></td><td>Look up all local (non-unique) instances of a given Counter.</td></tr><tr><td valign="top"><a href="#lookup_local_name-1">lookup_local_name/1</a></td><td>Lookup a local unique name.</td></tr><tr><td valign="top"><a href="#lookup_local_properties-1">lookup_local_properties/1</a></td><td>Look up all local (non-unique) instances of a given Property.</td></tr><tr><td valign="top"><a href="#lookup_pid-1">lookup_pid/1</a></td><td>Lookup the Pid stored with a key.</td></tr><tr><td valign="top"><a href="#lookup_pids-1">lookup_pids/1</a></td><td>Returns a list of pids with the published key Key.</td></tr><tr><td valign="top"><a href="#lookup_value-1">lookup_value/1</a></td><td>Lookup the value stored with a key.</td></tr><tr><td valign="top"><a href="#lookup_values-1">lookup_values/1</a></td><td>Retrieve the <code>{Pid,Value}</code> pairs corresponding to Key.</td></tr><tr><td valign="top"><a href="#monitor-1">monitor/1</a></td><td>Equivalent to <a href="#monitor-2"><tt>monitor(Key, info)</tt></a>.</td></tr><tr><td valign="top"><a href="#monitor-2">monitor/2</a></td><td>monitor a registered name
+<code>monitor(Key, info)</code> works much like erlang:monitor(process, Pid), but monitors
 a unique name registered via gproc.</td></tr><tr><td valign="top"><a href="#mreg-3">mreg/3</a></td><td>Register multiple {Key,Value} pairs of a given type and scope.</td></tr><tr><td valign="top"><a href="#munreg-3">munreg/3</a></td><td>Unregister multiple Key items of a given type and scope.</td></tr><tr><td valign="top"><a href="#nb_wait-1">nb_wait/1</a></td><td>Wait for a local name to be registered.</td></tr><tr><td valign="top"><a href="#nb_wait-2">nb_wait/2</a></td><td>Wait for a local name to be registered on <code>Node</code>.</td></tr><tr><td valign="top"><a href="#next-2">next/2</a></td><td>Behaves as ets:next(Tab,Key) for a given type of registration.</td></tr><tr><td valign="top"><a href="#prev-2">prev/2</a></td><td>Behaves as ets:prev(Tab,Key) for a given type of registration.</td></tr><tr><td valign="top"><a href="#reg-1">reg/1</a></td><td>Equivalent to <a href="#reg-2"><tt>reg(Key, default(Key))</tt></a>.</td></tr><tr><td valign="top"><a href="#reg-2">reg/2</a></td><td>Register a name or property for the current process.</td></tr><tr><td valign="top"><a href="#reg_or_locate-1">reg_or_locate/1</a></td><td>Equivalent to <a href="#reg_or_locate-2"><tt>reg_or_locate(Key, default(Key))</tt></a>.</td></tr><tr><td valign="top"><a href="#reg_or_locate-2">reg_or_locate/2</a></td><td>Try registering a unique name, or return existing registration.</td></tr><tr><td valign="top"><a href="#reg_or_locate-3">reg_or_locate/3</a></td><td>Spawn a process with a registered name, or return existing registration.</td></tr><tr><td valign="top"><a href="#reg_shared-1">reg_shared/1</a></td><td>Register a resource, but don't tie it to a particular process.</td></tr><tr><td valign="top"><a href="#reg_shared-2">reg_shared/2</a></td><td>Register a resource, but don't tie it to a particular process.</td></tr><tr><td valign="top"><a href="#register_name-2">register_name/2</a></td><td>Behaviour support callback.</td></tr><tr><td valign="top"><a href="#reset_counter-1">reset_counter/1</a></td><td>Reads and resets a counter in a "thread-safe" way.</td></tr><tr><td valign="top"><a href="#select-1">select/1</a></td><td>Perform a select operation on the process registry.</td></tr><tr><td valign="top"><a href="#select-2">select/2</a></td><td>Perform a select operation with limited context on the process registry.</td></tr><tr><td valign="top"><a href="#select-3">select/3</a></td><td>Like <a href="#select-2"><code>select/2</code></a> but returns Limit objects at a time.</td></tr><tr><td valign="top"><a href="#select_count-1">select_count/1</a></td><td>Equivalent to <a href="#select_count-2"><tt>select_count(all, Pat)</tt></a>.</td></tr><tr><td valign="top"><a href="#select_count-2">select_count/2</a></td><td>Perform a select_count operation on the process registry.</td></tr><tr><td valign="top"><a href="#send-2">send/2</a></td><td>Sends a message to the process, or processes, corresponding to Key.</td></tr><tr><td valign="top"><a href="#set_attributes-2">set_attributes/2</a></td><td>Add/modify <code>{Key, Value}</code> attributes associated with a registration.</td></tr><tr><td valign="top"><a href="#set_attributes_shared-2">set_attributes_shared/2</a></td><td>Add/modify <code>{Key, Value}</code> attributes associated with a shared registration.</td></tr><tr><td valign="top"><a href="#set_env-5">set_env/5</a></td><td>Updates the cached value as well as underlying environment.</td></tr><tr><td valign="top"><a href="#set_value-2">set_value/2</a></td><td>Sets the value of the registration given by Key.</td></tr><tr><td valign="top"><a href="#set_value_shared-2">set_value_shared/2</a></td><td>Sets the value of the shared registration given by Key.</td></tr><tr><td valign="top"><a href="#start_link-0">start_link/0</a></td><td>Starts the gproc server.</td></tr><tr><td valign="top"><a href="#table-0">table/0</a></td><td>Equivalent to <a href="#table-1"><tt>table({all, all})</tt></a>.</td></tr><tr><td valign="top"><a href="#table-1">table/1</a></td><td>Equivalent to <a href="#table-2"><tt>table(Context, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#table-2">table/2</a></td><td>QLC table generator for the gproc registry.</td></tr><tr><td valign="top"><a href="#unreg-1">unreg/1</a></td><td>Unregister a name or property.</td></tr><tr><td valign="top"><a href="#unreg_shared-1">unreg_shared/1</a></td><td>Unregister a shared resource.</td></tr><tr><td valign="top"><a href="#unregister_name-1">unregister_name/1</a></td><td>Equivalent to <tt>unreg / 1</tt>.</td></tr><tr><td valign="top"><a href="#update_counter-2">update_counter/2</a></td><td>Updates the counter registered as Key for the current process.</td></tr><tr><td valign="top"><a href="#update_counter-3">update_counter/3</a></td><td></td></tr><tr><td valign="top"><a href="#update_counters-2">update_counters/2</a></td><td>Update a list of counters.</td></tr><tr><td valign="top"><a href="#update_shared_counter-2">update_shared_counter/2</a></td><td>Updates the shared counter registered as Key.</td></tr><tr><td valign="top"><a href="#where-1">where/1</a></td><td>Returns the pid registered as Key.</td></tr><tr><td valign="top"><a href="#whereis_name-1">whereis_name/1</a></td><td>Equivalent to <tt>where / 1</tt>.</td></tr><tr><td valign="top"><a href="#wide_await-3">wide_await/3</a></td><td>Wait for a local name to be registered on any of <code>Nodes</code>.</td></tr></table>
 
 
@@ -1110,9 +1110,16 @@ object, the return value can be a list of any length.
 
 ### monitor/1 ###
 
+`monitor(Key) -> any()`
+
+Equivalent to [`monitor(Key, info)`](#monitor-2).
+<a name="monitor-2"></a>
+
+### monitor/2 ###
+
 
 <pre><code>
-monitor(Key::<a href="#type-key">key()</a>) -&gt; reference()
+monitor(Key::<a href="#type-key">key()</a>, Type::info | standby) -&gt; reference()
 </code></pre>
 
 <br></br>
@@ -1120,13 +1127,25 @@ monitor(Key::<a href="#type-key">key()</a>) -&gt; reference()
 
 
 monitor a registered name
-This function works much like erlang:monitor(process, Pid), but monitors
+`monitor(Key, info)` works much like erlang:monitor(process, Pid), but monitors
 a unique name registered via gproc. A message, `{gproc, unreg, Ref, Key}`
 will be sent to the requesting process, if the name is unregistered or
-the registered process dies.
+the registered process dies. If there is a standby monitor (see below), a
+message `{gproc, {failover, ToPid}, Ref, Key}` is sent to all monitors.
+If the name is passed to another process using [`give_away/2`](#give_away-2), the event
+`{gproc, {migrated, ToPid}, Ref, Key}` is sent to all monitors.
+
+
+
+`monitor(Key, standby)` sets up the monitoring process as a standby for the
+registered name. If the registered process dies, the first standby process
+inherits the name, and a message `{gproc, {failover, ToPid}, Ref, Key}` is
+sent to all monitors, including the one that inherited the name.
 
 
-If the name is not yet registered, the same message is sent immediately.
+If the name is not yet registered, the unreg event is sent immediately.
+If the calling process in this case tried to start a `standby` monitoring,
+it receives the registered name and the failover event immediately.
 <a name="mreg-3"></a>
 
 ### mreg/3 ###

+ 15 - 1
doc/gproc_dist.md

@@ -23,7 +23,7 @@ For a detailed description, see gproc/doc/erlang07-wiger.pdf.
 ## Function Index ##
 
 
-<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="#elected-3">elected/3</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_leader-0">get_leader/0</a></td><td>Returns the node of the current gproc leader.</td></tr><tr><td valign="top"><a href="#give_away-2">give_away/2</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-4">handle_call/4</a></td><td></td></tr><tr><td valign="top"><a href="#handle_cast-3">handle_cast/3</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_info-3">handle_info/3</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="#init-1">init/1</a></td><td></td></tr><tr><td valign="top"><a href="#leader_call-1">leader_call/1</a></td><td></td></tr><tr><td valign="top"><a href="#leader_cast-1">leader_cast/1</a></td><td></td></tr><tr><td valign="top"><a href="#mreg-2">mreg/2</a></td><td></td></tr><tr><td valign="top"><a href="#munreg-2">munreg/2</a></td><td></td></tr><tr><td valign="top"><a href="#reg-1">reg/1</a></td><td></td></tr><tr><td valign="top"><a href="#reg-2">reg/2</a></td><td>
+<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="#demonitor-2">demonitor/2</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="#elected-3">elected/3</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_leader-0">get_leader/0</a></td><td>Returns the node of the current gproc leader.</td></tr><tr><td valign="top"><a href="#give_away-2">give_away/2</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-4">handle_call/4</a></td><td></td></tr><tr><td valign="top"><a href="#handle_cast-3">handle_cast/3</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_info-3">handle_info/3</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="#init-1">init/1</a></td><td></td></tr><tr><td valign="top"><a href="#leader_call-1">leader_call/1</a></td><td></td></tr><tr><td valign="top"><a href="#leader_cast-1">leader_cast/1</a></td><td></td></tr><tr><td valign="top"><a href="#monitor-2">monitor/2</a></td><td></td></tr><tr><td valign="top"><a href="#mreg-2">mreg/2</a></td><td></td></tr><tr><td valign="top"><a href="#munreg-2">munreg/2</a></td><td></td></tr><tr><td valign="top"><a href="#reg-1">reg/1</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
@@ -42,6 +42,13 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#reg_or_
 `code_change(FromVsn, S, Extra, E) -> any()`
 
 
+<a name="demonitor-2"></a>
+
+### demonitor/2 ###
+
+`demonitor(Key, Ref) -> any()`
+
+
 <a name="elected-2"></a>
 
 ### elected/2 ###
@@ -153,6 +160,13 @@ Returns the node of the current gproc leader.
 `leader_cast(Msg) -> any()`
 
 
+<a name="monitor-2"></a>
+
+### monitor/2 ###
+
+`monitor(Key, Type) -> any()`
+
+
 <a name="mreg-2"></a>
 
 ### mreg/2 ###

+ 32 - 4
doc/gproc_lib.md

@@ -21,18 +21,18 @@ For a detailed description, see gproc/doc/erlang07-wiger.pdf.
 ## Function Index ##
 
 
-<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#add_monitor-3">add_monitor/3</a></td><td></td></tr><tr><td valign="top"><a href="#await-3">await/3</a></td><td></td></tr><tr><td valign="top"><a href="#do_set_counter_value-3">do_set_counter_value/3</a></td><td></td></tr><tr><td valign="top"><a href="#do_set_value-3">do_set_value/3</a></td><td></td></tr><tr><td valign="top"><a href="#ensure_monitor-2">ensure_monitor/2</a></td><td></td></tr><tr><td valign="top"><a href="#insert_attr-4">insert_attr/4</a></td><td></td></tr><tr><td valign="top"><a href="#insert_many-4">insert_many/4</a></td><td></td></tr><tr><td valign="top"><a href="#insert_reg-4">insert_reg/4</a></td><td></td></tr><tr><td valign="top"><a href="#notify-2">notify/2</a></td><td></td></tr><tr><td valign="top"><a href="#notify-3">notify/3</a></td><td></td></tr><tr><td valign="top"><a href="#remove_many-4">remove_many/4</a></td><td></td></tr><tr><td valign="top"><a href="#remove_monitor-3">remove_monitor/3</a></td><td></td></tr><tr><td valign="top"><a href="#remove_monitors-3">remove_monitors/3</a></td><td></td></tr><tr><td valign="top"><a href="#remove_reg-3">remove_reg/3</a></td><td></td></tr><tr><td valign="top"><a href="#remove_reg-4">remove_reg/4</a></td><td></td></tr><tr><td valign="top"><a href="#remove_reverse_mapping-3">remove_reverse_mapping/3</a></td><td></td></tr><tr><td valign="top"><a href="#remove_wait-4">remove_wait/4</a></td><td></td></tr><tr><td valign="top"><a href="#update_aggr_counter-3">update_aggr_counter/3</a></td><td></td></tr><tr><td valign="top"><a href="#update_counter-3">update_counter/3</a></td><td></td></tr><tr><td valign="top"><a href="#valid_opts-2">valid_opts/2</a></td><td></td></tr></table>
+<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#add_monitor-4">add_monitor/4</a></td><td></td></tr><tr><td valign="top"><a href="#await-3">await/3</a></td><td></td></tr><tr><td valign="top"><a href="#do_set_counter_value-3">do_set_counter_value/3</a></td><td></td></tr><tr><td valign="top"><a href="#do_set_value-3">do_set_value/3</a></td><td></td></tr><tr><td valign="top"><a href="#ensure_monitor-2">ensure_monitor/2</a></td><td></td></tr><tr><td valign="top"><a href="#insert_attr-4">insert_attr/4</a></td><td></td></tr><tr><td valign="top"><a href="#insert_many-4">insert_many/4</a></td><td></td></tr><tr><td valign="top"><a href="#insert_reg-4">insert_reg/4</a></td><td></td></tr><tr><td valign="top"><a href="#monitors-1">monitors/1</a></td><td></td></tr><tr><td valign="top"><a href="#notify-2">notify/2</a></td><td></td></tr><tr><td valign="top"><a href="#notify-3">notify/3</a></td><td></td></tr><tr><td valign="top"><a href="#remove_many-4">remove_many/4</a></td><td></td></tr><tr><td valign="top"><a href="#remove_monitor-3">remove_monitor/3</a></td><td></td></tr><tr><td valign="top"><a href="#remove_monitor_pid-2">remove_monitor_pid/2</a></td><td></td></tr><tr><td valign="top"><a href="#remove_monitors-3">remove_monitors/3</a></td><td></td></tr><tr><td valign="top"><a href="#remove_reg-3">remove_reg/3</a></td><td></td></tr><tr><td valign="top"><a href="#remove_reg-4">remove_reg/4</a></td><td></td></tr><tr><td valign="top"><a href="#remove_reverse_mapping-3">remove_reverse_mapping/3</a></td><td></td></tr><tr><td valign="top"><a href="#remove_reverse_mapping-4">remove_reverse_mapping/4</a></td><td></td></tr><tr><td valign="top"><a href="#remove_wait-4">remove_wait/4</a></td><td></td></tr><tr><td valign="top"><a href="#standbys-1">standbys/1</a></td><td></td></tr><tr><td valign="top"><a href="#update_aggr_counter-3">update_aggr_counter/3</a></td><td></td></tr><tr><td valign="top"><a href="#update_counter-3">update_counter/3</a></td><td></td></tr><tr><td valign="top"><a href="#valid_opts-2">valid_opts/2</a></td><td></td></tr></table>
 
 
 <a name="functions"></a>
 
 ## Function Details ##
 
-<a name="add_monitor-3"></a>
+<a name="add_monitor-4"></a>
 
-### add_monitor/3 ###
+### add_monitor/4 ###
 
-`add_monitor(T, Pid, Ref) -> any()`
+`add_monitor(T, Pid, Ref, Type) -> any()`
 
 
 <a name="await-3"></a>
@@ -96,6 +96,13 @@ insert_reg(K::<a href="#type-key">key()</a>, Value::any(), Pid::pid() | shared,
 
 
 
+<a name="monitors-1"></a>
+
+### monitors/1 ###
+
+`monitors(Opts) -> any()`
+
+
 <a name="notify-2"></a>
 
 ### notify/2 ###
@@ -124,6 +131,13 @@ insert_reg(K::<a href="#type-key">key()</a>, Value::any(), Pid::pid() | shared,
 `remove_monitor(T, Pid, Ref) -> any()`
 
 
+<a name="remove_monitor_pid-2"></a>
+
+### remove_monitor_pid/2 ###
+
+`remove_monitor_pid(T, Pid) -> any()`
+
+
 <a name="remove_monitors-3"></a>
 
 ### remove_monitors/3 ###
@@ -152,6 +166,13 @@ insert_reg(K::<a href="#type-key">key()</a>, Value::any(), Pid::pid() | shared,
 `remove_reverse_mapping(Event, Pid, Key) -> any()`
 
 
+<a name="remove_reverse_mapping-4"></a>
+
+### remove_reverse_mapping/4 ###
+
+`remove_reverse_mapping(Event, Pid, Key, Opts) -> any()`
+
+
 <a name="remove_wait-4"></a>
 
 ### remove_wait/4 ###
@@ -159,6 +180,13 @@ insert_reg(K::<a href="#type-key">key()</a>, Value::any(), Pid::pid() | shared,
 `remove_wait(Key, Pid, Ref, Waiters) -> any()`
 
 
+<a name="standbys-1"></a>
+
+### standbys/1 ###
+
+`standbys(Opts) -> any()`
+
+
 <a name="update_aggr_counter-3"></a>
 
 ### update_aggr_counter/3 ###

+ 102 - 28
src/gproc.erl

@@ -87,7 +87,7 @@
          nb_wait/1, nb_wait/2,
          cancel_wait/2, cancel_wait/3,
 	 cancel_wait_or_monitor/1,
-	 monitor/1,
+	 monitor/1, monitor/2,
 	 demonitor/2,
          lookup_pid/1,
          lookup_pids/1,
@@ -875,25 +875,40 @@ cancel_wait_or_monitor1({_,l,_} = Key) ->
     ok.
 
 
-%% @spec monitor(key()) -> reference()
+%% @equiv monitor(Key, info)
+monitor(Key) ->
+    ?CATCH_GPROC_ERROR(monitor1(Key, info), [Key]).
+
+%% @spec monitor(key(), info | standby) -> reference()
 %%
 %% @doc monitor a registered name
-%% This function works much like erlang:monitor(process, Pid), but monitors
+%% `monitor(Key, info)' works much like erlang:monitor(process, Pid), but monitors
 %% a unique name registered via gproc. A message, `{gproc, unreg, Ref, Key}'
 %% will be sent to the requesting process, if the name is unregistered or
-%% the registered process dies.
+%% the registered process dies. If there is a standby monitor (see below), a
+%% message `{gproc, {failover, ToPid}, Ref, Key}' is sent to all monitors.
+%% If the name is passed to another process using {@link give_away/2}, the event
+%% `{gproc, {migrated, ToPid}, Ref, Key}' is sent to all monitors.
 %%
-%% If the name is not yet registered, the same message is sent immediately.
+%% `monitor(Key, standby)' sets up the monitoring process as a standby for the
+%% registered name. If the registered process dies, the first standby process
+%% inherits the name, and a message `{gproc, {failover, ToPid}, Ref, Key}' is
+%% sent to all monitors, including the one that inherited the name.
+%%
+%% If the name is not yet registered, the unreg event is sent immediately.
+%% If the calling process in this case tried to start a `standby' monitoring,
+%% it receives the registered name and the failover event immediately.
 %% @end
-monitor(Key) ->
-    ?CATCH_GPROC_ERROR(monitor1(Key), [Key]).
+monitor(Key, Type) when Type==info;
+                        Type==standby ->
+    ?CATCH_GPROC_ERROR(monitor1(Key, Type), [Key, Type]).
 
-monitor1({T,g,_} = Key) when T==n; T==a ->
+monitor1({T,g,_} = Key, Type) when T==n; T==a ->
     ?CHK_DIST,
-    call({monitor, Key, self()}, g);
-monitor1({T,l,_} = Key) when T==n; T==a ->
-    call({monitor, Key, self()}, l);
-monitor1(_) ->
+    gproc_dist:monitor(Key, Type);
+monitor1({T,l,_} = Key, Type) when T==n; T==a ->
+    call({monitor, Key, self(), Type}, l);
+monitor1(_, _) ->
     ?THROW_GPROC_ERROR(badarg).
 
 %% @spec demonitor(key(), reference()) -> ok
@@ -907,7 +922,7 @@ demonitor(Key, Ref) ->
 
 demonitor1({T,g,_} = Key, Ref) when T==n; T==a ->
     ?CHK_DIST,
-    call({demonitor, Key, Ref, self()}, g);
+    gproc_dist:demonitor(Key, Ref);
 demonitor1({T,l,_} = Key, Ref) when T==n; T==a ->
     call({demonitor, Key, Ref, self()}, l);
 demonitor1(_, _) ->
@@ -2054,18 +2069,22 @@ handle_call({reg_or_locate, {T,l,_} = Key, Val, P}, _, S) ->
 	[{_, OtherPid, OtherValue}] ->
 	    {reply, {OtherPid, OtherValue}, S}
     end;
-handle_call({monitor, {T,l,_} = Key, Pid}, _From, S)
+handle_call({monitor, {T,l,_} = Key, Pid, Type}, _From, S)
   when T==n; T==a ->
     Ref = make_ref(),
-    _ = case where(Key) of
-	    undefined ->
+    _ = case {where(Key), Type} of
+	    {undefined, info} ->
 		Pid ! {gproc, unreg, Ref, Key};
-	    RegPid ->
+	    {RegPid, _} ->
+                _ = gproc_lib:ensure_monitor(Pid, l),
 		case ets:lookup(?TAB, {RegPid, Key}) of
 		    [{K,r}] ->
-			ets:insert(?TAB, {K, [{monitor, [{Pid,Ref}]}]});
+			ets:insert(?TAB, [{K, [{monitor, [{Pid,Ref,Type}]}]},
+                                          {{Pid,Key}, []}]);
 		    [{K, Opts}] ->
-			ets:insert(?TAB, {K, gproc_lib:add_monitor(Opts, Pid, Ref)})
+			ets:insert(?TAB, [{K, gproc_lib:add_monitor(
+                                                Opts, Pid, Ref, Type)},
+                                          {{Pid,Key}, []}])
 		end
 	end,
     {reply, Ref, S};
@@ -2233,8 +2252,8 @@ try_insert_reg({T,l,_} = Key, Val, Pid) ->
                         true ->
                             false;
                         false ->
-                            process_is_down(OtherPid),
-                            true = gproc_lib:insert_reg(Key, Val, Pid, l)
+                            process_is_down(OtherPid), % may result in failover
+                            try_insert_reg(Key, Val, Pid)
                     end;
                 [] ->
                     false
@@ -2274,9 +2293,9 @@ process_is_down(Pid) when is_pid(Pid) ->
               fun({{n,l,_}=K, R}) ->
                       Key = {K,n},
                       case ets:lookup(?TAB, Key) of
-                          [{_, Pid, _}] ->
+                          [{_, Pid, V}] ->
                               ets:delete(?TAB, Key),
-			      opt_notify(R, K);
+			      opt_notify(R, K, Pid, V);
                           [{_, Waiters}] ->
                               case [W || {P,_} = W <- Waiters,
                                          P =/= Pid] of
@@ -2285,6 +2304,15 @@ process_is_down(Pid) when is_pid(Pid) ->
                                   Waiters1 ->
                                       ets:insert(?TAB, {Key, Waiters1})
                               end;
+                          [{_, OtherPid, _}] when Pid =/= OtherPid ->
+                              case ets:lookup(?TAB, {OtherPid, K}) of
+                                  [{RK, Opts}] when is_list(Opts) ->
+                                      Opts1 = gproc_lib:remove_monitor_pid(
+                                                Opts, Pid),
+                                      ets:insert(?TAB, {RK, Opts1});
+                                  _ ->
+                                      true
+                              end;
                           [] ->
                               true
                       end;
@@ -2294,8 +2322,22 @@ process_is_down(Pid) when is_pid(Pid) ->
                       ets:delete(?TAB, Key),
                       gproc_lib:update_aggr_counter(l, C, -Value);
                  ({{a,l,_} = K, R}) ->
-                      ets:delete(?TAB, {K,a}),
-		      opt_notify(R, K);
+                      case ets:lookup(?TAB, {K,a}) of
+                          [{_, Pid, V}] ->
+                              ets:delete(?TAB, {K,a}),
+                              opt_notify(R, K, Pid, V);
+                          [{_, OtherPid, _}] when Pid =/= OtherPid ->
+                              case ets:lookup(?TAB, {OtherPid, K}) of
+                                  [{RK, Opts}] when is_list(Opts) ->
+                                      Opts1 = gproc_lib:remove_monitor_pid(
+                                                Opts, Pid),
+                                      ets:insert(?TAB, {RK, Opts1});
+                                  _ ->
+                                      true
+                              end;
+                          [] ->
+                              opt_notify(R, K, Pid, undefined)
+                      end;
                  ({{p,_,_} = K, _}) ->
                       ets:delete(?TAB, {K, Pid})
               end, Revs),
@@ -2304,10 +2346,42 @@ process_is_down(Pid) when is_pid(Pid) ->
             ok
     end.
 
-opt_notify(r, _) ->
+opt_notify(r, _, _, _) ->
     ok;
-opt_notify(Opts, Key) ->
-    gproc_lib:notify(Key, Opts).
+opt_notify(Opts, {T,_,_} = Key, Pid, Value) ->
+    case gproc_lib:standbys(Opts) of
+        [] ->
+            gproc_lib:notify(unreg, Key, Opts);
+        SBs ->
+            case pick_standby(SBs) of
+                false ->
+                    gproc_lib:notify(unreg, Key, Opts),
+                    ok;
+                {ToPid, Ref} ->
+                    ets:insert(?TAB, [{Key, ToPid, Value},
+                                      {{ToPid, {Key,T}},
+                                       gproc_lib:remove_monitor(
+                                         Opts, ToPid, Ref)}]),
+                    _ = gproc_lib:remove_reverse_mapping(
+                          {failover,ToPid}, Pid, Key),
+                    _ = gproc_lib:ensure_monitor(ToPid, l),
+                    ok
+            end
+    end.
+
+pick_standby([{Pid, Ref, standby}|T]) when node(Pid) =:= node() ->
+    case is_process_alive(Pid) of
+        true ->
+            {Pid, Ref};
+        false ->
+            pick_standby(T)
+    end;
+pick_standby([_|T]) ->
+    pick_standby(T);
+pick_standby([]) ->
+    false.
+
+
 
 
 do_give_away({T,l,_} = K, To, Pid) when T==n; T==a ->

+ 122 - 59
src/gproc_dist.erl

@@ -26,6 +26,8 @@
          reg/1, reg/2, unreg/1,
 	 reg_or_locate/3,
 	 reg_shared/2, unreg_shared/1,
+         monitor/2,
+         demonitor/2,
 	 set_attributes/2,
 	 set_attributes_shared/2,
          mreg/2,
@@ -126,6 +128,18 @@ reg_shared({_,g,_} = Key, Value) ->
 reg_shared(_, _) ->
     ?THROW_GPROC_ERROR(badarg).
 
+monitor({_,g,_} = Key, Type) when Type==info;
+                                  Type==standby ->
+    leader_call({monitor, Key, self(), Type});
+monitor(_, _) ->
+    ?THROW_GPROC_ERROR(badarg).
+
+demonitor({_,g,_} = Key, Ref) ->
+    leader_call({demonitor, Key, self(), Ref});
+demonitor(_, _) ->
+    ?THROW_GPROC_ERROR(badarg).
+
+
 set_attributes({_,g,_} = Key, Attrs) ->
     leader_call({set_attributes, Key, Attrs, self()});
 set_attributes(_, _) ->
@@ -336,6 +350,45 @@ handle_leader_call({reg, {_C,g,_Name} = K, Value, Pid}, _From, S, _E) ->
             %%     end,
             {reply, true, [{insert, Vals}], S}
     end;
+handle_leader_call({monitor, {T,g,_} = K, MPid, Type}, _From, S, _E) when T==n;
+                                                                         T==a ->
+    case ets:lookup(?TAB, {K, T}) of
+        [{_, Pid, _}] ->
+            Opts = get_opts(Pid, K),
+            Ref = make_ref(),
+            Opts1 = gproc_lib:add_monitor(Opts, MPid, Ref, Type),
+            _ = gproc_lib:ensure_monitor(MPid, g),
+            Obj = {{Pid,K}, Opts1},
+            Rev = {{MPid,K}, []},
+            ets:insert(?TAB, [Obj, Rev]),
+            {reply, Ref, [{insert, [Obj, Rev]}], S};
+        [] ->
+            Ref = make_ref(),
+            case Type of
+                standby ->
+                    Obj = {{K,T}, MPid, undefined},
+                    Rev = {{MPid,K}, []},
+                    ets:insert(?TAB, [Obj, Rev]),
+                    MPid ! {gproc, {failover,MPid}, Ref, K},
+                    {reply, Ref, [{insert, [Obj, Rev]}], S};
+                _ ->
+                    MPid ! {gproc, unreg, Ref, K},
+                    {reply, Ref, S}
+            end
+    end;
+handle_leader_call({demonitor, {T,g,_} = K, MPid, Ref}, _From, S, _E) ->
+    case ets:lookup(?TAB, {K,T}) of
+        [{_, Pid, _}] ->
+            Opts = get_opts(Pid, K),
+            Opts1 = gproc_lib:remove_monitors(Opts, MPid, Ref),
+            Obj = {{Pid,K}, Opts1},
+            ets:insert(?TAB, Obj),
+            ets:delete(?TAB, {MPid, K}),
+            {reply, ok, [{delete, [{MPid,K}]},
+                         {insert, [Obj]}], S};
+        _ ->
+            {reply, ok, S}
+    end;
 handle_leader_call({set_attributes, {_,g,_} = K, Attrs, Pid}, _From, S, _E) ->
     case gproc_lib:insert_attr(K, Attrs, Pid, g) of
 	false ->
@@ -369,39 +422,6 @@ handle_leader_call({reg_or_locate, {n,g,_} = K, Value, P},
 	[{_, OtherPid, OtherVal}] ->
 	    {reply, {OtherPid, OtherVal}, S}
     end;
-handle_leader_call({monitor, {T,g,_} = Key, Pid}, _From, S, _E)
-  when T==n; T==a ->
-    Ref = make_ref(),
-    case gproc:where(Key) of
-	undefined ->
-	    Pid ! {gproc, unreg, Ref, Key},
-	    {reply, Ref, [], S};
-	RegPid ->
-	    NewRev =
-		case ets:lookup_element(?TAB, K = {RegPid, Key}, 2) of
-		    r ->
-			{K, [{monitor, [{Pid,Ref}]}]};
-		    Opts ->
-			{K, gproc_lib:add_monitor(Opts, Pid, Ref)}
-		end,
-	    ets:insert(?TAB, NewRev),
-	    {reply, Ref, [{insert, [NewRev]}], S}
-    end;
-handle_leader_call({demonitor, {T,g,_} = Key, Ref, Pid}, _From, S, _E)
-  when T==n; T==a ->
-    case gproc:where(Key) of
-	undefined ->
-	    {reply, ok, [], S};
-	RegPid ->
-	    case ets:lookup_element(?TAB, K = {RegPid, Key}, 2) of
-		r ->
-		    {reply, ok, [], S};
-		Opts ->
-		    NewRev = {K, gproc_lib:remove_monitor(Opts, Pid, Ref)},
-		    ets:insert(?TAB, NewRev),
-		    {reply, Ref, [{insert, [NewRev]}], S}
-	    end
-    end;
 handle_leader_call({update_counter, {T,g,_Ctr} = Key, Incr, Pid}, _From, S, _E)
   when is_integer(Incr), T==c;
        is_integer(Incr), T==n ->
@@ -570,7 +590,7 @@ handle_leader_cast({add_globals, Missing}, S, _E) ->
     Update = insert_globals(Missing),
     {ok, [{insert, Update}], S};
 handle_leader_cast({remove_globals, Globals}, S, _E) ->
-    delete_globals(Globals, []),
+    delete_globals(Globals),
     {ok, S};
 handle_leader_cast({cancel_wait, Pid, {T,_,_} = Key, Ref}, S, _E) ->
     case ets:lookup(?TAB, {Key, T}) of
@@ -619,7 +639,14 @@ mk_broadcast_insert_vals(Objs) ->
 process_globals(Globals) ->
     Modified =
         lists:foldl(
-          fun({{T,_,_} = Key, Pid}, A) ->
+          fun({{T,_,_} = Key, Pid}, A) when T==n; T==a ->
+                  case ets:lookup(?TAB, {Pid,Key}) of
+                      [{_, Opts}] when is_list(Opts) ->
+                          maybe_failover(Key, Pid, Opts, A);
+                      _ ->
+                          A
+                  end;
+             ({{T,_,_} = Key, Pid}, A) ->
                   A1 = case T of
                            c ->
                                Incr = ets:lookup_element(?TAB, {Key,Pid}, 3),
@@ -627,26 +654,64 @@ process_globals(Globals) ->
                            _ ->
                                A
                        end,
-                  K = ets_key(Key, Pid),
-                  ets:delete(?TAB, K),
-                  remove_rev_entry(Pid, Key, unreg),
+                  remove_entry(Key, Pid, unreg),
                   A1
           end, [], Globals),
     [{insert, Modified} || Modified =/= []] ++
-	[{delete, Globals, unreg} || Globals =/= []].
+	[{delete, Globals} || Globals =/= []].
 
-remove_rev_entry(Pid, {T,g,_} = K, Event) when T==n; T==a ->
+maybe_failover({T,_,_} = Key, Pid, Opts, Acc) ->
+    Opts = get_opts(Pid, Key),
+    case filter_standbys(gproc_lib:standbys(Opts)) of
+        [] ->
+            remove_entry(Key, Pid, unreg),
+            Acc;
+        [{ToPid,Ref,_}|_] ->
+            Value = case ets:lookup(?TAB, {Key,T}) of
+                        [{_, _, V}] -> V;
+                        _ -> undefined
+                    end,
+            remove_rev_entry(Opts, Pid, Key, {failover, ToPid}),
+            Opts1 = gproc_lib:remove_monitor(Opts, ToPid, Ref),
+            _ = gproc_lib:ensure_monitor(ToPid, g),
+            NewReg = {{Key,T}, ToPid, Value},
+            NewRev = {{ToPid, Key}, Opts1},
+            ets:insert(?TAB, [NewReg, NewRev]),
+            [NewReg, NewRev | Acc]
+    end.
+
+filter_standbys(SBs) ->
+    filter_standbys(SBs, [node()|nodes()]).
+
+filter_standbys([{Pid,_,_} = H|T], Nodes) ->
+    case lists:member(node(Pid), Nodes) of
+        true ->
+            [H|T];
+        false ->
+            filter_standbys(T, Nodes)
+    end;
+filter_standbys([], _) ->
+    [].
+
+
+remove_entry(Key, Pid, Event) ->
+    K = ets_key(Key, Pid),
+    ets:delete(?TAB, K),
+    remove_rev_entry(get_opts(Pid, Key), Pid, Key, Event).
+
+remove_rev_entry(Opts, Pid, {T,g,_} = K, Event) when T==n; T==a ->
     Key = {Pid, K},
-    _ = case ets:lookup(?TAB, Key) of
-	    []       -> ok;
-	    [{_, r}] -> ok;
-	    [{_, Opts}] when is_list(Opts) ->
-		gproc_lib:notify(Event, K, Opts)
-	end,
+    gproc_lib:notify(Event, K, Opts),
     ets:delete(?TAB, Key);
-remove_rev_entry(Pid, K, _Event) ->
+remove_rev_entry(_, Pid, K, _Event) ->
     ets:delete(?TAB, {Pid, K}).
 
+get_opts(Pid, K) ->
+    case ets:lookup(?TAB, {Pid, K}) of
+        [] -> [];
+        [{_, r}] -> [];
+        [{_, Opts}] -> Opts
+    end.
 
 code_change(_FromVsn, S, _Extra, _E) ->
     {ok, S}.
@@ -659,21 +724,23 @@ from_leader({sync, Ref}, S, _E) ->
     {ok, S};
 from_leader(Ops, S, _E) ->
     lists:foreach(
-      fun({delete, Globals, Event}) ->
-              delete_globals(Globals, Event);
+      fun({delete, Globals}) ->
+              delete_globals(Globals);
          ({insert, Globals}) ->
 	      _ = insert_globals(Globals)
       end, Ops),
     {ok, S}.
 
 insert_globals(Globals) ->
-    ets:insert(?TAB, Globals),
     lists:foldl(
-      fun({{{_,_,_} = Key,Pid}, Pid, _}, A) ->
+      fun({{{_,_,_} = Key,Pid}, Pid, _} = Obj, A) ->
+              ets:insert(?TAB, Obj),
 	      ets:insert_new(?TAB, {{Pid,Key}, []}),
 	      gproc_lib:ensure_monitor(Pid,g),
 	      A;
-	 ({{{_,_,_}, n}, Pid, _}, A) ->
+	 ({{{_,_,_} = Key, n}, Pid, _} = Obj, A) ->
+              ets:insert(?TAB, Obj),
+              ets:insert_new(?TAB, {{Pid,Key}, []}),
 	      gproc_lib:ensure_monitor(Pid,g),
 	      A;
 	 ({{P,_K}, Opts} = Obj, A) when is_pid(P), is_list(Opts),Opts =/= [] ->
@@ -685,18 +752,14 @@ insert_globals(Globals) ->
       end, Globals, Globals).
 
 
-delete_globals(Globals, Event) ->
+delete_globals(Globals) ->
     lists:foreach(
       fun({{_,g,_},T} = K) when is_atom(T) ->
               ets:delete(?TAB, K);
          ({Key, Pid}) when is_pid(Pid); Pid==shared ->
-              K = ets_key(Key,Pid),
-              ets:delete(?TAB, K),
-	      remove_rev_entry(Pid, Key, Event);
+              ets:delete(?TAB, {Pid, Key});
          ({Pid, Key}) when is_pid(Pid); Pid==shared ->
-	      K = ets_key(Key, Pid),
-	      ets:delete(?TAB, K),
-	      remove_rev_entry(Pid, Key, Event)
+	      ets:delete(?TAB, {Pid, Key})
       end, Globals).
 
 ets_key({T,_,_} = K, _) when T==n; T==a ->

+ 59 - 15
src/gproc_lib.erl

@@ -30,10 +30,13 @@
 	 insert_attr/4,
          remove_many/4,
          remove_reg/3, remove_reg/4,
-	 add_monitor/3,
+         monitors/1,
+         standbys/1,
+         remove_monitor_pid/2,
+	 add_monitor/4,
 	 remove_monitor/3,
 	 remove_monitors/3,
-	 remove_reverse_mapping/3,
+	 remove_reverse_mapping/3, remove_reverse_mapping/4,
 	 notify/2, notify/3,
 	 remove_wait/4,
          update_aggr_counter/3,
@@ -303,7 +306,8 @@ remove_reverse_mapping(Event, Pid, Key) ->
     remove_reverse_mapping(Event, Pid, Key, Opts).
 
 remove_reverse_mapping(Event, Pid, Key, Opts) when Event==unreg;
-						   element(1,Event)==migrated ->
+						   element(1,Event)==migrated;
+                                                   element(1,Event)==failover ->
     Rev = {Pid, Key},
     _ = notify(Event, Key, Opts),
     ets:delete(?TAB, Rev),
@@ -312,31 +316,71 @@ remove_reverse_mapping(Event, Pid, Key, Opts) when Event==unreg;
 notify(Key, Opts) ->
     notify(unreg, Key, Opts).
 
-notify([], _, _) ->
-    [];
-notify(Event, Key, Opts) ->
+monitors(Opts) ->
     case lists:keyfind(monitor, 1, Opts) of
 	false ->
 	    [];
 	{_, Mons} ->
-	    [begin P ! {gproc, Event, Ref, Key}, P end || {P, Ref} <- Mons,
-							  node(P) == node()]
+            Mons
     end.
 
-add_monitor([{monitor, Mons}|T], Pid, Ref) ->
-    [{monitor, [{Pid,Ref}|Mons]}|T];
-add_monitor([H|T], Pid, Ref) ->
-    [H|add_monitor(T, Pid, Ref)];
-add_monitor([], Pid, Ref) ->
-    [{monitor, [{Pid, Ref}]}].
+standbys(Opts) ->
+    standbys(monitors(Opts), []).
+
+standbys([{_,_,standby}=H|T], Acc) ->
+    standbys(T, [H|Acc]);
+standbys([_|T], Acc) ->
+    standbys(T, Acc);
+standbys([], Acc) ->
+    Acc.
+
+remove_monitor_pid([{monitor, Mons}|T], Pid) ->
+    [{monitors, [M || M <- Mons,
+                      element(1, M) =/= Pid]}|T];
+remove_monitor_pid([H|T], Pid) ->
+    [H | remove_monitor_pid(T, Pid)];
+remove_monitor_pid([], _) ->
+    [].
+
+
+notify([], _, _) ->
+    ok;
+notify(Event, Key, Opts) ->
+    notify_(monitors(Opts), Event, Key).
+
+%% Also handle old-style monitors
+notify_([{Pid,Ref}|T], Event, Key) ->
+    Pid ! {gproc, Event, Ref, Key},
+    notify_(T, Event, Key);
+notify_([{Pid,Ref,_}|T], Event, Key) ->
+    Pid ! {gproc, Event, Ref, Key},
+    notify_(T, Event, Key);
+notify_([_|T], Event, Key) ->
+    notify_(T, Event, Key);
+notify_([], _, _) ->
+    ok.
+
+
+
+add_monitor([{monitor, Mons}|T], Pid, Ref, Type) ->
+    [{monitor, [{Pid,Ref,Type}|Mons]}|T];
+add_monitor([H|T], Pid, Ref, Type) ->
+    [H|add_monitor(T, Pid, Ref, Type)];
+add_monitor([], Pid, Ref, Type) ->
+    [{monitor, [{Pid, Ref, Type}]}].
 
 remove_monitor([{monitor, Mons}|T], Pid, Ref) ->
-    [{monitor, Mons -- [{Pid, Ref}]}|T];
+    [{monitor, [Mon || Mon <- Mons, not is_mon(Mon,Pid,Ref)]} | T];
 remove_monitor([H|T], Pid, Ref) ->
     [H|remove_monitor(T, Pid, Ref)];
 remove_monitor([], _Pid, _Ref) ->
     [].
 
+is_mon({Pid,Ref,_}, Pid, Ref) -> true;
+is_mon({Pid,Ref},   Pid, Ref) -> true;
+is_mon(_, _, _) ->
+    false.
+
 remove_many(T, Scope, L, Pid) ->
     lists:flatmap(fun(K) ->
                           Key = {T, Scope, K},

+ 5 - 0
src/gproc_monitor.erl

@@ -185,6 +185,11 @@ handle_info({gproc, {migrated,ToPid}, _Ref, Name}, State) ->
     notify(Name, {migrated, ToPid}),
     do_monitor(Name, ToPid),
     {noreply, State};
+handle_info({gproc, {failover,ToPid}, _Ref, Name}, State) ->
+    ets:delete(?TAB, {m, Name}),
+    notify(Name, {failover, ToPid}),
+    do_monitor(Name, ToPid),
+    {noreply, State};
 handle_info({gproc, _, registered, {{T,_,_} = Name, Pid, _}}, State)
   when T==n; T==a ->
     ets:delete(?TAB, {w, Name}),