Browse Source

distributed wait (await/3, wide_await/3, et al)

Ulf Wiger 13 years ago
parent
commit
603dd7e323
3 changed files with 200 additions and 12 deletions
  1. 58 5
      doc/gproc.md
  2. 141 6
      src/gproc.erl
  3. 1 1
      src/gproc_lib.erl

+ 58 - 5
doc/gproc.md

@@ -207,12 +207,12 @@ a = aggregate_counter
 ##Function Index##
 
 
-<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="#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_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
+<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 object.</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="#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 object.</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
-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="#next-2">next/2</a></td><td>Behaves as ets:next(Tab,Key) for a given type of registration object.</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 object.</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_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_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 registeration entry 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_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></table>
+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 object.</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 object.</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_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_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 registeration entry 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_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>
 
 
 <a name="functions"></a>
@@ -320,7 +320,19 @@ either an interger > 0 or 'infinity'.
 A small optimization: we first perform a lookup, to see if the name
 is already registered. This way, the cost of the operation will be
 roughly the same as of where/1 in the case where the name is already
-registered (the difference: await/2 also returns the value).<a name="bcast-2"></a>
+registered (the difference: await/2 also returns the value).<a name="await-3"></a>
+
+###await/3##
+
+
+<pre>await(Node::node(), Key::<a href="#type-key">key()</a>, Timeout) -> {pid(), Value}</pre>
+<ul class="definitions"><li><pre>Timeout = integer() | infinity</pre></li></ul>
+
+Wait for a local name to be registered on `Node`.
+This function works exactly like [`await/2`](#await-2), but queries a remote
+node instead. An exception is thrown if `Node` cannot be reached. If gproc
+is not running on a given node, this is treated the same as the node being
+down.<a name="bcast-2"></a>
 
 ###bcast/2##
 
@@ -361,7 +373,20 @@ __See also:__ [send/2](#send-2).<a name="cancel_wait-2"></a>
 Cancels a previous call to nb_wait/1
 
 If `Ref = all`, all wait requests on `Key` from the calling process
-are canceled.<a name="cancel_wait_or_monitor-1"></a>
+are canceled.<a name="cancel_wait-3"></a>
+
+###cancel_wait/3##
+
+
+<pre>cancel_wait(Node::node(), Key::<a href="#type-key">key()</a>, Ref) -> ok</pre>
+<ul class="definitions"><li><pre>Ref = all | reference()</pre></li></ul>
+
+
+
+Cancels a previous call to nb_wait/2
+
+This function works just like [`cancel_wait/2`](#cancel_wait-2), but talks to a remote
+node.<a name="cancel_wait_or_monitor-1"></a>
 
 ###cancel_wait_or_monitor/1##
 
@@ -819,6 +844,17 @@ repeatedly.<a name="nb_wait-1"></a>
 
 Wait for a local name to be registered.
 The caller can expect to receive a message,
+{gproc, Ref, registered, {Key, Pid, Value}}, once the name is registered.<a name="nb_wait-2"></a>
+
+###nb_wait/2##
+
+
+<pre>nb_wait(Node::node(), Key::<a href="#type-key">key()</a>) -> Ref</pre>
+<br></br>
+
+
+Wait for a local name to be registered on `Node`.
+The caller can expect to receive a message,
 {gproc, Ref, registered, {Key, Pid, Value}}, once the name is registered.<a name="next-2"></a>
 
 ###next/2##
@@ -1231,4 +1267,21 @@ cases.<a name="whereis_name-1"></a>
 
 `whereis_name(Key) -> any()`
 
-Equivalent to `where / 1`.
+Equivalent to `where / 1`.<a name="wide_await-3"></a>
+
+###wide_await/3##
+
+
+<pre>wide_await(Nodes::[node()], Key::<a href="#type-key">key()</a>, Timeout) -> {pid(), Value}</pre>
+<ul class="definitions"><li><pre>Timeout = integer() | infinity</pre></li></ul>
+
+
+
+Wait for a local name to be registered on any of `Nodes`.
+This function works rather like [`await/2`](#await-2), but queries all nodes in
+the `Nodes` list at the same time. The first node to respond with a
+process registered as `Key` will provide the result. Other results are
+ignored. `Key` must be a unique name with local scope, i.e. `{n,l,Name}`.
+
+An exception is thrown upon timeout, or if no node can be reached (if gproc is
+not running on a given node, this is treated the same as the node being down).

+ 141 - 6
src/gproc.erl

@@ -78,9 +78,10 @@
          set_value/2,
          get_value/1, get_value/2,
          where/1,
-         await/1, await/2,
-         nb_wait/1,
-         cancel_wait/2,
+         await/1, await/2, await/3,
+	 wide_await/3,
+         nb_wait/1, nb_wait/2,
+         cancel_wait/2, cancel_wait/3,
 	 cancel_wait_or_monitor/1,
 	 monitor/1,
 	 demonitor/2,
@@ -624,6 +625,21 @@ await(Key) ->
 await(Key, Timeout) ->
     ?CATCH_GPROC_ERROR(await1(Key, Timeout), [Key, Timeout]).
 
+%% @spec await(Node::node(), Key::key(), Timeout) -> {pid(),Value}
+%%   Timeout = integer() | infinity
+%%
+%% @doc Wait for a local name to be registered on `Node'.
+%% This function works exactly like {@link await/2}, but queries a remote
+%% node instead. An exception is thrown if `Node' cannot be reached. If gproc
+%% is not running on a given node, this is treated the same as the node being
+%% down.
+%% @end
+%%
+await(Node, Key, Timeout) when Node == node() ->
+    await(Key, Timeout);
+await(Node, Key, Timeout) when is_atom(Node) ->
+    ?CATCH_GPROC_ERROR(await1(Node, Key, Timeout), [Node, Key, Timeout]).
+
 await1({n,g,_} = Key, Timeout) ->
     ?CHK_DIST,
     request_wait(Key, Timeout);
@@ -649,7 +665,16 @@ await1({n,l,_} = Key, Timeout) ->
 await1(_, _) ->
     throw(badarg).
 
-request_wait({n,C,_} = Key, Timeout) when C==l; C==g ->
+await1(N, {n,l,_} = Key, Timeout) when is_atom(N) ->
+    request_wait(N, Key, Timeout);
+await1(_, _, _) ->
+    throw(badarg).
+
+
+request_wait(Key, Timeout) ->
+    request_wait(node(), Key, Timeout).
+
+request_wait(N, {n,C,_} = Key, Timeout) when C==l; C==g ->
     TRef = case Timeout of
                infinity -> no_timer;
                T when is_integer(T), T > 0 ->
@@ -657,7 +682,7 @@ request_wait({n,C,_} = Key, Timeout) when C==l; C==g ->
                _ ->
                    ?THROW_GPROC_ERROR(badarg)
            end,
-    WRef = case {call({await,Key,self()}, C), C} of
+    WRef = case {call(N, {await,Key,self()}, C), C} of
                {{R, {Kg,Pg,Vg}}, g} ->
                    self() ! {gproc, R, registered, {Kg,Pg,Vg}},
                    R;
@@ -672,10 +697,80 @@ request_wait({n,C,_} = Key, Timeout) when C==l; C==g ->
                 end,
             {Pid, V};
         {timeout, TRef, gproc_timeout} ->
-            cancel_wait(Key, WRef),
+            cancel_wait(N, Key, WRef),
             ?THROW_GPROC_ERROR(timeout)
     end.
 
+%% @spec wide_await(Nodes::[node()], Key::key(), Timeout) -> {pid(),Value}
+%%   Timeout = integer() | infinity
+%%
+%% @doc Wait for a local name to be registered on any of `Nodes'.
+%% This function works rather like {@link await/2}, but queries all nodes in
+%% the `Nodes' list at the same time. The first node to respond with a
+%% process registered as `Key' will provide the result. Other results are
+%% ignored. `Key' must be a unique name with local scope, i.e. `{n,l,Name}'.
+%%
+%% An exception is thrown upon timeout, or if no node can be reached (if gproc is
+%% not running on a given node, this is treated the same as the node being down).
+%% @end
+%%
+wide_await(Nodes, Key, Timeout) ->
+    ?CATCH_GPROC_ERROR(wide_await1(Nodes, Key, Timeout), [Nodes, Key, Timeout]).
+
+wide_await1(Nodes, {n,l,_} = Key, Timeout) ->
+    {_, Ref} = spawn_monitor(fun() ->
+				     wide_request_wait(Nodes, Key, Timeout)
+			     end),
+    receive
+	{'DOWN', Ref, _, _, Reason} ->
+	    case Reason of
+		{ok, {gproc,_,registered,{_,Pid,V}}} ->
+		    {Pid, V};
+		Other ->
+		    ?THROW_GPROC_ERROR(Other)
+	    end
+    end;
+wide_await1(_, _, _) ->
+    ?THROW_GPROC_ERROR(badarg).
+
+
+wide_request_wait(Nodes, {n,l,_} = Key, Timeout) ->
+        TRef = case Timeout of
+               infinity -> no_timer;
+               T when is_integer(T), T > 0 ->
+                   erlang:start_timer(T, self(), gproc_timeout);
+               _ ->
+                   exit(badarg)
+           end,
+    Req = {await, Key, self()},
+    Refs = lists:map(
+	     fun(Node) ->
+		     S = {?MODULE, Node},
+		     Ref = erlang:monitor(process, S),
+		     catch erlang:send(S, {'$gen_call', {self(), Ref}, Req},
+				       [noconnect]),
+		     {Node, Ref}
+	     end, Nodes),
+    collect_replies(Refs, Key, TRef).
+
+collect_replies(Refs, Key, TRef) ->
+    receive
+	{gproc, _Ref, registered, {_, _, _}} = Result ->
+	    exit({ok, Result});
+	{'DOWN', Ref, _, _, _} ->
+	    case lists:keydelete(Ref, 2, Refs) of
+		[] ->
+		    exit(nodedown);
+		Refs1 ->
+		    collect_replies(Refs1, Key, TRef)
+	    end;
+	{timeout, TRef, gproc_timeout} ->
+	    exit(timeout);
+	{Ref, Ref} ->
+	    %% ignore
+	    collect_replies(Refs, Key, TRef)
+    end.
+
 
 %% @spec nb_wait(Key::key()) -> Ref
 %%
@@ -687,6 +782,16 @@ request_wait({n,C,_} = Key, Timeout) when C==l; C==g ->
 nb_wait(Key) ->
     ?CATCH_GPROC_ERROR(nb_wait1(Key), [Key]).
 
+%% @spec nb_wait(Node::node(), Key::key()) -> Ref
+%%
+%% @doc Wait for a local name to be registered on `Node'.
+%% The caller can expect to receive a message,
+%% {gproc, Ref, registered, {Key, Pid, Value}}, once the name is registered.
+%% @end
+%%
+nb_wait(Node, {n,l,_} = Key) when is_atom(Node) ->
+    ?CATCH_GPROC_ERROR(nb_wait1(Node, Key), [Node, Key]).
+
 nb_wait1({n,g,_} = Key) ->
     ?CHK_DIST,
     call({await, Key, self()}, g);
@@ -695,6 +800,12 @@ nb_wait1({n,l,_} = Key) ->
 nb_wait1(_) ->
     ?THROW_GPROC_ERROR(badarg).
 
+nb_wait1(Node, {n,l,_} = Key) when is_atom(Node) ->
+    call(Node, {await, Key, self()}, l);
+nb_wait1(_, _) ->
+    ?THROW_GPROC_ERROR(badarg).
+
+
 %% @spec cancel_wait(Key::key(), Ref) -> ok
 %%    Ref = all | reference()
 %%
@@ -707,6 +818,21 @@ nb_wait1(_) ->
 cancel_wait(Key, Ref) ->
     ?CATCH_GPROC_ERROR(cancel_wait1(Key, Ref), [Key, Ref]).
 
+%% @spec cancel_wait(Node::node(), Key::key(), Ref) -> ok
+%%    Ref = all | reference()
+%%
+%% @doc Cancels a previous call to nb_wait/2
+%%
+%% This function works just like {@link cancel_wait/2}, but talks to a remote
+%% node.
+%% @end
+%%
+cancel_wait(N, Key, Ref) when N == node() ->
+    cancel_wait(Key, Ref);
+cancel_wait(N, Key, Ref) ->
+    ?CATCH_GPROC_ERROR(cancel_wait1(N, Key, Ref), [N, Key, Ref]).
+
+
 cancel_wait1({_,g,_} = Key, Ref) ->
     ?CHK_DIST,
     cast({cancel_wait, self(), Key, Ref}, g),
@@ -715,6 +841,9 @@ cancel_wait1({_,l,_} = Key, Ref) ->
     cast({cancel_wait, self(), Key, Ref}, l),
     ok.
 
+cancel_wait1(N, {_,l,_} = Key, Ref) ->
+    cast(N, {cancel_wait, self(), Key, Ref}, l).
+
 cancel_wait_or_monitor(Key) ->
     ?CATCH_GPROC_ERROR(cancel_wait_or_monitor1(Key), [Key]).
 
@@ -1770,6 +1899,9 @@ call(Req, l) ->
 call(Req, g) ->
     chk_reply(gproc_dist:leader_call(Req)).
 
+call(N, Req, l) ->
+    chk_reply(gen_server:call({?MODULE, N}, Req)).
+
 chk_reply(Reply) ->
     case Reply of
         badarg -> ?THROW_GPROC_ERROR(badarg);
@@ -1785,6 +1917,9 @@ cast(Msg, l) ->
 cast(Msg, g) ->
     gproc_dist:leader_cast(Msg).
 
+cast(N, Msg, l) ->
+    gen_server:cast({?MODULE, N}, Msg).
+
 try_insert_reg({T,l,_} = Key, Val, Pid) ->
     case gproc_lib:insert_reg(Key, Val, Pid, l) of
         false ->

+ 1 - 1
src/gproc_lib.erl

@@ -255,7 +255,7 @@ mk_reg_rev_objs(T, Scope, Pid, L) ->
 ensure_monitor(shared, _) ->
     ok;
 ensure_monitor(Pid, Scope) when Scope==g; Scope==l ->
-    case node(Pid) == node() andalso ets:insert_new(?TAB, {{Pid, Scope}}) of
+    case ets:insert_new(?TAB, {{Pid, Scope}}) of
         false -> ok;
         true  -> erlang:monitor(process, Pid)
     end.