Browse Source

add gproc:await/2 et al + bug fixes

git-svn-id: http://svn.ulf.wiger.net/gproc/branches/0912/gproc@45 f3948e33-8234-0410-8a80-a07eae3b6c4d
uwiger 15 years ago
parent
commit
3227d54573
10 changed files with 200 additions and 39 deletions
  1. 35 5
      doc/gproc.html
  2. 1 1
      doc/gproc_app.html
  3. 1 1
      doc/gproc_dist.html
  4. 1 1
      doc/gproc_init.html
  5. 1 1
      doc/gproc_lib.html
  6. 1 1
      doc/gproc_sup.html
  7. 1 1
      doc/overview-summary.html
  8. 1 1
      ebin/gproc.app
  9. 90 6
      src/gproc.erl
  10. 68 21
      src/gproc_lib.erl

+ 35 - 5
doc/gproc.html

@@ -67,6 +67,9 @@
 <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_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_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_local_property-2">add_local_property/2</a></td><td>Registers a local (non-unique) property.</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="#cancel_wait-2">cancel_wait/2</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="#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="#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_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="#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="#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-1">info/1</a></td><td>Similar to <code>process_info(Pid)</code> but with additional gproc info.</td></tr>
@@ -84,9 +87,10 @@
 <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_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_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="#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="#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="#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="#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="#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="#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, undefined)</tt></a>.
+<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>
 </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-2">reg/2</a></td><td>Register a name or property for the current process.</td></tr>
 <tr><td valign="top"><a href="#select-1">select/1</a></td><td>Equivalent to <a href="#select-2"><tt>select(all, Pat)</tt></a>.
 <tr><td valign="top"><a href="#select-1">select/1</a></td><td>Equivalent to <a href="#select-2"><tt>select(all, Pat)</tt></a>.
@@ -109,7 +113,7 @@
 <h3 class="function"><a name="add_global_aggr_counter-1">add_global_aggr_counter/1</a></h3>
 <h3 class="function"><a name="add_global_aggr_counter-1">add_global_aggr_counter/1</a></h3>
 <div class="spec">
 <div class="spec">
 <p><tt>add_global_aggr_counter(Name) -&gt; any()</tt></p>
 <p><tt>add_global_aggr_counter(Name) -&gt; any()</tt></p>
-</div><p>Equivalent to <a href="#reg-1"><tt>reg({a, l, Name})</tt></a>.</p>
+</div><p>Equivalent to <a href="#reg-1"><tt>reg({a, g, Name})</tt></a>.</p>
 <p>Registers a global (unique) aggregated counter.</p>
 <p>Registers a global (unique) aggregated counter.</p>
 
 
 <h3 class="function"><a name="add_global_counter-2">add_global_counter/2</a></h3>
 <h3 class="function"><a name="add_global_counter-2">add_global_counter/2</a></h3>
@@ -148,6 +152,25 @@
 <p><tt>add_local_property(Name, Value) -&gt; any()</tt></p>
 <p><tt>add_local_property(Name, Value) -&gt; any()</tt></p>
 </div><p>Registers a local (non-unique) property. @equiv reg({p,l,Name},Value)</p>
 </div><p>Registers a local (non-unique) property. @equiv reg({p,l,Name},Value)</p>
 
 
+<h3 class="function"><a name="await-2">await/2</a></h3>
+<div class="spec">
+<p><tt>await(Key::<a href="#type-key">key()</a>, Timeout) -&gt; {pid(), Value}</tt>
+<ul class="definitions"><li><tt>Timeout = integer() | infinity</tt></li>
+</ul></p>
+</div><p>Wait for a local name to be registered.
+  The function raises an exception if the timeout expires. Timeout must be
+  either an interger &gt; 0 or 'infinity'.</p>
+
+<h3 class="function"><a name="cancel_wait-2">cancel_wait/2</a></h3>
+<div class="spec">
+<p><tt>cancel_wait(Key, Ref) -&gt; any()</tt></p>
+</div>
+
+<h3 class="function"><a name="default-1">default/1</a></h3>
+<div class="spec">
+<p><tt>default(X1) -&gt; any()</tt></p>
+</div>
+
 <h3 class="function"><a name="first-1">first/1</a></h3>
 <h3 class="function"><a name="first-1">first/1</a></h3>
 <div class="spec">
 <div class="spec">
 <p><tt>first(Type::<a href="#type-type">type()</a>) -&gt; <a href="#type-key">key()</a> | '$end_of_table'</tt></p>
 <p><tt>first(Type::<a href="#type-type">type()</a>) -&gt; <a href="#type-key">key()</a> | '$end_of_table'</tt></p>
@@ -201,7 +224,7 @@
 <h3 class="function"><a name="lookup_global_counters-1">lookup_global_counters/1</a></h3>
 <h3 class="function"><a name="lookup_global_counters-1">lookup_global_counters/1</a></h3>
 <div class="spec">
 <div class="spec">
 <p><tt>lookup_global_counters(Counter::any()) -&gt; [{pid(), Value::integer()}]</tt></p>
 <p><tt>lookup_global_counters(Counter::any()) -&gt; [{pid(), Value::integer()}]</tt></p>
-</div><p>Equivalent to <a href="#lookup_values-1"><tt>lookup_values({c, l, Counter})</tt></a>.</p>
+</div><p>Equivalent to <a href="#lookup_values-1"><tt>lookup_values({c, g, Counter})</tt></a>.</p>
 <p>Look up all global (non-unique) instances of a given Counter.
 <p>Look up all global (non-unique) instances of a given Counter.
   Returns a list of {Pid, Value} tuples for all matching objects.</p>
   Returns a list of {Pid, Value} tuples for all matching objects.</p>
 
 
@@ -276,6 +299,13 @@
  
  
   This function is more efficient than calling <a href="#reg-2"><code>reg/2</code></a> repeatedly.</p>
   This function is more efficient than calling <a href="#reg-2"><code>reg/2</code></a> repeatedly.</p>
 
 
+<h3 class="function"><a name="nb_wait-1">nb_wait/1</a></h3>
+<div class="spec">
+<p><tt>nb_wait(Key::<a href="#type-key">key()</a>) -&gt; Ref</tt></p>
+</div><p>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.</p>
+
 <h3 class="function"><a name="next-2">next/2</a></h3>
 <h3 class="function"><a name="next-2">next/2</a></h3>
 <div class="spec">
 <div class="spec">
 <p><tt>next(Context::<a href="#type-context">context()</a>, Key::<a href="#type-key">key()</a>) -&gt; <a href="#type-key">key()</a> | '$end_of_table'</tt></p>
 <p><tt>next(Context::<a href="#type-context">context()</a>, Key::<a href="#type-key">key()</a>) -&gt; <a href="#type-key">key()</a> | '$end_of_table'</tt></p>
@@ -295,7 +325,7 @@
 <h3 class="function"><a name="reg-1">reg/1</a></h3>
 <h3 class="function"><a name="reg-1">reg/1</a></h3>
 <div class="spec">
 <div class="spec">
 <p><tt>reg(Key::<a href="#type-key">key()</a>) -&gt; true</tt></p>
 <p><tt>reg(Key::<a href="#type-key">key()</a>) -&gt; true</tt></p>
-</div><p>Equivalent to <a href="#reg-2"><tt>reg(Key, undefined)</tt></a>.</p>
+</div><p>Equivalent to <a href="#reg-2"><tt>reg(Key, default(Key))</tt></a>.</p>
 
 
 
 
 <h3 class="function"><a name="reg-2">reg/2</a></h3>
 <h3 class="function"><a name="reg-2">reg/2</a></h3>
@@ -393,6 +423,6 @@ If it doesn't, this function will exit.</p>
 <hr>
 <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>
 <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 22 2009, 10:04:07.</i></p>
+<p><i>Generated by EDoc, Feb 11 2010, 05:33:39.</i></p>
 </body>
 </body>
 </html>
 </html>

+ 1 - 1
doc/gproc_app.html

@@ -38,6 +38,6 @@
 <hr>
 <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>
 <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 22 2009, 10:04:07.</i></p>
+<p><i>Generated by EDoc, Feb 11 2010, 05:33:39.</i></p>
 </body>
 </body>
 </html>
 </html>

+ 1 - 1
doc/gproc_dist.html

@@ -149,6 +149,6 @@
 <hr>
 <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>
 <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 22 2009, 10:04:07.</i></p>
+<p><i>Generated by EDoc, Feb 11 2010, 05:33:38.</i></p>
 </body>
 </body>
 </html>
 </html>

+ 1 - 1
doc/gproc_init.html

@@ -31,6 +31,6 @@
 <hr>
 <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>
 <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 22 2009, 10:04:07.</i></p>
+<p><i>Generated by EDoc, Feb 11 2010, 05:33:38.</i></p>
 </body>
 </body>
 </html>
 </html>

+ 1 - 1
doc/gproc_lib.html

@@ -18,6 +18,6 @@
   <p>For a detailed description, see gproc/doc/erlang07-wiger.pdf.</p><hr>
   <p>For a detailed description, see gproc/doc/erlang07-wiger.pdf.</p><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>
 <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 22 2009, 10:04:07.</i></p>
+<p><i>Generated by EDoc, Feb 11 2010, 05:33:39.</i></p>
 </body>
 </body>
 </html>
 </html>

+ 1 - 1
doc/gproc_sup.html

@@ -32,6 +32,6 @@
 <hr>
 <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>
 <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 22 2009, 10:04:07.</i></p>
+<p><i>Generated by EDoc, Feb 11 2010, 05:33:39.</i></p>
 </body>
 </body>
 </html>
 </html>

+ 1 - 1
doc/overview-summary.html

@@ -35,6 +35,6 @@ express a strong urge to use this functionality.</em>
 
 
 <hr>
 <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>
 <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 22 2009, 10:04:07.</i></p>
+<p><i>Generated by EDoc, Feb 11 2010, 05:33:39.</i></p>
 </body>
 </body>
 </html>
 </html>

+ 1 - 1
ebin/gproc.app

@@ -13,7 +13,7 @@
   },
   },
   {registered, [ ] },
   {registered, [ ] },
   %% NOTE: do not list applications which are load-only!
   %% NOTE: do not list applications which are load-only!
-  {applications, [ kernel, stdlib, sasl ] },
+  {applications, [ kernel, stdlib ] },
   {mod, {gproc_app, []} }
   {mod, {gproc_app, []} }
  ]
  ]
 }.
 }.

+ 90 - 6
src/gproc.erl

@@ -44,6 +44,9 @@
          set_value/2,
          set_value/2,
          get_value/1,
          get_value/1,
          where/1,
          where/1,
+         await/2,
+         nb_wait/1,
+         cancel_wait/2,
          lookup_pid/1,
          lookup_pid/1,
          lookup_pids/1,
          lookup_pids/1,
          lookup_values/1,
          lookup_values/1,
@@ -170,7 +173,7 @@ add_local_aggr_counter(Name)  -> reg({a,l,Name}).
 %% spec(Name::any()) -> true
 %% spec(Name::any()) -> true
 %%
 %%
 %% @doc Registers a global (unique) aggregated counter.
 %% @doc Registers a global (unique) aggregated counter.
-%% @equiv reg({a,l,Name})
+%% @equiv reg({a,g,Name})
 %% @end
 %% @end
 %%
 %%
 add_global_aggr_counter(Name) -> reg({a,g,Name}).
 add_global_aggr_counter(Name) -> reg({a,g,Name}).
@@ -190,7 +193,7 @@ lookup_local_name(Name)   -> where({n,l,Name}).
 %% @equiv where({n,g,Name})
 %% @equiv where({n,g,Name})
 %% @end
 %% @end
 %%
 %%
-lookup_global_name(Name)  -> where({g,l,Name}).
+lookup_global_name(Name)  -> where({n,g,Name}).
 
 
 %% @spec (Name::any()) -> integer()
 %% @spec (Name::any()) -> integer()
 %%
 %%
@@ -244,7 +247,7 @@ lookup_local_counters(P)    -> lookup_values({c,l,P}).
 %%
 %%
 %% @doc Look up all global (non-unique) instances of a given Counter.
 %% @doc Look up all global (non-unique) instances of a given Counter.
 %% Returns a list of {Pid, Value} tuples for all matching objects.
 %% Returns a list of {Pid, Value} tuples for all matching objects.
-%% @equiv lookup_values({c, l, Counter})
+%% @equiv lookup_values({c, g, Counter})
 %% @end
 %% @end
 %%
 %%
 lookup_global_counters(P)   -> lookup_values({c,g,P}).
 lookup_global_counters(P)   -> lookup_values({c,g,P}).
@@ -263,6 +266,50 @@ default({T,_,_}) when T==c; T==a -> 0;
 default(_) -> undefined.
 default(_) -> undefined.
 
 
 
 
+%% @spec await(Key::key(), Timeout) -> {pid(),Value}
+%%   Timeout = integer() | infinity
+%%
+%% @doc Wait for a local name to be registered.
+%% The function raises an exception if the timeout expires. Timeout must be 
+%% either an interger &gt; 0 or 'infinity'.
+%% @end
+%%
+await({n,l,_} = Key, Timeout) ->
+    TRef = case Timeout of
+               infinity -> no_timer;
+               T when is_integer(T), T > 0 ->
+                   erlang:start_timer(T, self(), timeout);
+               _ ->
+                   erlang:error(badarg, [Key, Timeout])
+           end,
+    WRef = call({await,Key}),
+    receive
+        {gproc, WRef, registered, {_K, Pid, V}} ->
+            {Pid, V};
+        {timeout, TRef, timeout} ->
+            cancel_wait(Key, WRef),
+            erlang:error(timeout, [Key, Timeout])
+    end;
+await(K, T) ->
+    erlang:error(badarg, [K, T]).
+
+%% @spec nb_wait(Key::key()) -> Ref
+%%
+%% @doc 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.
+%% @end
+%%
+nb_wait({n,l,_} = Key) ->
+    call({await, Key});
+nb_wait(Key) ->
+    erlang:error(badarg, [Key]).
+
+cancel_wait(Key, Ref) ->
+    cast({cancel_wait, self(), Key, Ref}),
+    ok.
+            
+
 %% @spec reg(Key::key(), Value) -> true
 %% @spec reg(Key::key(), Value) -> true
 %%
 %%
 %% @doc Register a name or property for the current process
 %% @doc Register a name or property for the current process
@@ -675,6 +722,20 @@ info(Pid, I) ->
 %% @hidden
 %% @hidden
 handle_cast({monitor_me, Pid}, S) ->
 handle_cast({monitor_me, Pid}, S) ->
     erlang:monitor(process, Pid),
     erlang:monitor(process, Pid),
+    {noreply, S};
+handle_cast({cancel_wait, Pid, {T,_,_} = Key, Ref}, S) ->
+    case ets:lookup(?TAB, {Key,T}) of
+        [{K, Waiters}] ->
+            NewWaiters = Waiters -- [{Pid,Ref}],
+            %% for now, we don't remove the reverse entry. If we should do
+            %% that, we have to make sure that Pid doesn't have another
+            %% waiter (which it shouldn't have, given that the wait is 
+            %% synchronous). Keeping it is not problematic - worst case, we
+            %% will get an unnecessary cleanup.
+            ets:insert(?TAB, {K, NewWaiters});
+        _ ->
+            ignore
+    end,
     {noreply, S}.
     {noreply, S}.
 
 
 %% @hidden
 %% @hidden
@@ -694,6 +755,24 @@ handle_call({unreg, {_,l,_} = Key}, {Pid,_}, S) ->
         false ->
         false ->
             {reply, badarg, S}
             {reply, badarg, S}
     end;
     end;
+handle_call({await, {T,l,_} = Key}, {Pid,Ref} = From, S) ->
+    Rev = {{Pid,Key}, r},
+    case ets:lookup(?TAB, {Key,T}) of
+        [{_, P, Value}] ->
+            %% for symmetry, we always reply with Ref and then send a message
+            gen_server:reply(From, Ref),
+            Pid ! {gproc, Ref, registered, {Key, P, Value}},
+            {noreply, S};
+        [{K, Waiters}] ->
+            NewWaiters = [{Pid,Ref} | Waiters],
+            ets:insert(?TAB, [{K, NewWaiters}, Rev]),
+            gproc_lib:ensure_monitor(Pid),
+            {reply, Ref, S};
+        [] ->
+            ets:insert(?TAB, [{{Key,T}, [{Pid,Ref}]}, Rev]),
+            gproc_lib:ensure_monitor(Pid),
+            {reply, Ref, S}
+    end;
 handle_call({mreg, T, l, L}, {Pid,_}, S) ->
 handle_call({mreg, T, l, L}, {Pid,_}, S) ->
     try gproc_lib:insert_many(T, l, L, Pid) of
     try gproc_lib:insert_many(T, l, L, Pid) of
         {true,_} -> {reply, true, S};
         {true,_} -> {reply, true, S};
@@ -736,6 +815,7 @@ call(Req) ->
         Reply  -> Reply
         Reply  -> Reply
     end.
     end.
 
 
+
 cast(Msg) ->
 cast(Msg) ->
     gen_server:cast(?MODULE, Msg).
     gen_server:cast(?MODULE, Msg).
 
 
@@ -762,11 +842,15 @@ try_insert_reg({T,l,_} = Key, Val, Pid) ->
     end.
     end.
 
 
 process_is_down(Pid) ->
 process_is_down(Pid) ->
-    Keys = ets:select(?TAB, [{{{Pid,'$1'}},
-                              [{'==',{element,2,'$1'},l}], ['$1']}]),
+    Keys = ets:select(?TAB, [{{{Pid,'$1'},'$2'},
+                              [{'==',{element,2,'$1'},l}], [{{'$1','$2'}}]}]),
     ets:select_delete(?TAB, [{{{Pid,{'_',l,'_'}}}, [], [true]}]),
     ets:select_delete(?TAB, [{{{Pid,{'_',l,'_'}}}, [], [true]}]),
     ets:delete(?TAB, Pid),
     ets:delete(?TAB, Pid),
-    lists:foreach(fun(Key) -> gproc_lib:remove_reg_1(Key, Pid) end, Keys).
+    lists:foreach(fun({Key,r}) ->
+                          gproc_lib:remove_reg_1(Key, Pid);
+                     ({Key,w}) ->
+                          gproc_lib:remove_waiter(Key, Pid)
+                  end, Keys).
 
 
 
 
 create_tabs() ->
 create_tabs() ->

+ 68 - 21
src/gproc_lib.erl

@@ -24,38 +24,45 @@
 
 
 -include("gproc.hrl").
 -include("gproc.hrl").
 
 
+%% We want to store names and aggregated counters with the same
+%% structure as properties, but at the same time, we must ensure
+%% that the key is unique. We replace the Pid in the key part
+%% with an atom. To know which Pid owns the object, we lug the
+%% Pid around as payload as well. This is a bit redundant, but
+%% symmetric.
+%%
 insert_reg({T,_,Name} = K, Value, Pid, C) when T==a; T==n ->
 insert_reg({T,_,Name} = K, Value, Pid, C) when T==a; T==n ->
-%%% We want to store names and aggregated counters with the same
-%%% structure as properties, but at the same time, we must ensure
-%%% that the key is unique. We replace the Pid in the key part
-%%% with an atom. To know which Pid owns the object, we lug the
-%%% Pid around as payload as well. This is a bit redundant, but
-%%% symmetric.
-%%%
-    case ets:insert_new(?TAB, [{{K, T}, Pid, Value}, {{Pid,K}}]) of
+    MaybeScan = fun() ->
+                        if T==a ->
+                                Initial = scan_existing_counters(C, Name),
+                                ets:insert(?TAB, {{K,a}, Pid, Initial});
+                           true ->
+                                true
+                        end
+                end,
+    Info = [{{K, T}, Pid, Value}, {{Pid,K},r}],
+    case ets:insert_new(?TAB, Info) of
         true ->
         true ->
-            if T==a ->
-                    Initial = scan_existing_counters(C, Name),
-                    ets:insert(?TAB, {{K,a}, Pid, Initial});
-               true ->
-                    true
-            end,
-            true;
+            MaybeScan();
         false ->
         false ->
-            false
+            if T==n ->
+                    maybe_waiters(K, Pid, Value, T, Info);
+               true ->
+                    false
+            end
     end;
     end;
 insert_reg({c,l,Ctr} = Key, Value, Pid, _C) ->
 insert_reg({c,l,Ctr} = Key, Value, Pid, _C) ->
     %% Non-unique keys; store Pid in the key part
     %% Non-unique keys; store Pid in the key part
     K = {Key, Pid},
     K = {Key, Pid},
     Kr = {Pid, Key},
     Kr = {Pid, Key},
-    Res = ets:insert_new(?TAB, [{K, Pid, Value}, {Kr}]),
+    Res = ets:insert_new(?TAB, [{K, Pid, Value}, {Kr,r}]),
     update_aggr_counter(l, Ctr, Value),
     update_aggr_counter(l, Ctr, Value),
     Res;
     Res;
 insert_reg(Key, Value, Pid, _C) ->
 insert_reg(Key, Value, Pid, _C) ->
     %% Non-unique keys; store Pid in the key part
     %% Non-unique keys; store Pid in the key part
     K = {Key, Pid},
     K = {Key, Pid},
     Kr = {Pid, Key},
     Kr = {Pid, Key},
-    ets:insert_new(?TAB, [{K, Pid, Value}, {Kr}]).
+    ets:insert_new(?TAB, [{K, Pid, Value}, {Kr,r}]).
 
 
 insert_many(T, C, KVL, Pid) ->
 insert_many(T, C, KVL, Pid) ->
     Objs = mk_reg_objs(T, C, Pid, KVL),
     Objs = mk_reg_objs(T, C, Pid, KVL),
@@ -65,9 +72,47 @@ insert_many(T, C, KVL, Pid) ->
             ets:insert(?TAB, RevObjs),
             ets:insert(?TAB, RevObjs),
             {true, Objs};
             {true, Objs};
         false ->
         false ->
+            Existing = [{Obj, ets:lookup(?TAB, K)} || {K,_,_} = Obj <- Objs],
+            case lists:any(fun({_, [{_,L}]}) -> is_list(L);
+                              (_) -> false
+                           end, Existing) of
+                [_|_] ->
+                    insert_objects(Existing);
+                [] ->
+                    false
+            end
+    end.
+
+insert_objects(Objs) ->
+    lists:map(
+      fun({{K, Pid, V} = Obj, Existing}) ->
+              ets:insert(?TAB, [Obj, {{Pid, K}, r}]),
+              case Existing of
+                  [] -> ok;
+                  [{_, Waiters}] ->
+                      notify_waiters(Waiters, K, Pid, V)
+              end,
+              Obj
+      end, Objs).
+
+
+maybe_waiters(K, Pid, Value, T, Info) ->
+    case ets:lookup(?TAB, {K,T}) of
+        [{_, Waiters}] when is_list(Waiters) ->
+            ets:insert(?TAB, Info),
+            notify_waiters(Waiters, K, Pid, Value),
+            true;
+        [_] ->
             false
             false
     end.
     end.
 
 
+notify_waiters(Waiters, K, Pid, V) ->
+    [begin
+         P ! {gproc, Ref, registered, {K, Pid, V}},
+         ets:delete(?TAB, {P,K}) 
+     end || {P, Ref} <- Waiters].
+
+
 
 
 mk_reg_objs(T, C, _, L) when T==n; T==a ->
 mk_reg_objs(T, C, _, L) when T==n; T==a ->
     lists:map(fun({K,V}) ->
     lists:map(fun({K,V}) ->
@@ -83,7 +128,7 @@ mk_reg_objs(p = T, C, Pid, L) ->
               end, L).
               end, L).
 
 
 mk_reg_rev_objs(T, C, Pid, L) ->
 mk_reg_rev_objs(T, C, Pid, L) ->
-    [{Pid,{T,C,K}} || {K,_} <- L].
+    [{{Pid,{T,C,K}},r} || {K,_} <- L].
 
 
 
 
 
 
@@ -101,8 +146,10 @@ remove_reg(Key, Pid) ->
 
 
 remove_reg_1({c,_,_} = Key, Pid) ->
 remove_reg_1({c,_,_} = Key, Pid) ->
     remove_counter_1(Key, ets:lookup_element(?TAB, {Key,Pid}, 3), Pid);
     remove_counter_1(Key, ets:lookup_element(?TAB, {Key,Pid}, 3), Pid);
-remove_reg_1({T,_,_} = Key, _Pid) when T==a; T==n ->
-    ets:delete(?TAB, {Key,T});
+remove_reg_1({a,_,_} = Key, _Pid) ->
+    ets:delete(?TAB, {Key,a});
+remove_reg_1({n,_,_} = Key, _Pid) ->
+    ets:delete(?TAB, {Key,n});
 remove_reg_1({_,_,_} = Key, Pid) ->
 remove_reg_1({_,_,_} = Key, Pid) ->
     ets:delete(?TAB, {Key, Pid}).
     ets:delete(?TAB, {Key, Pid}).