Browse Source

Fixed select/qlc scope (default: all); fixed dist eunit test; new rebar

gproc:select/1 now defaults to global+local scope (BW incompatible change)
Type specs have been updated to reflect that {Scope, Type} is a valid first
argument to select/2, first/1, next/2 et al.

The gproc_dist_tests module has been improved, and now works (most of the time,
although it can still time out occasionally).
Ulf Wiger 14 years ago
parent
commit
59365c4bef
11 changed files with 292 additions and 235 deletions
  1. 2 2
      README.md
  2. 2 2
      doc/README.md
  3. 80 63
      doc/gproc.md
  4. 3 3
      doc/gproc_app.md
  5. 24 24
      doc/gproc_dist.md
  6. 2 2
      doc/gproc_init.md
  7. 2 2
      doc/gproc_sup.md
  8. BIN
      rebar
  9. 123 112
      src/gproc.erl
  10. 15 25
      test/gproc_dist_tests.erl
  11. 39 0
      test/gproc_tests.erl

+ 2 - 2
README.md

@@ -47,11 +47,11 @@ An interesting application of gproc is building publish/subscribe patterns.
 Example:
 
 <pre>
-subscribe(EventType) -&gt;
+subscribe(EventType) ->
     %% Gproc notation: {p, l, Name} means {(p)roperty, (l)ocal, Name}
     gproc:reg({p, l, {?MODULE, EventType}}).
 
-notify(EventType, Msg) -&gt;
+notify(EventType, Msg) ->
     Key = {?MODULE, EventType},
     gproc:send({p, l, Key}, {self(), Key, Msg}).
 </pre>

+ 2 - 2
doc/README.md

@@ -47,11 +47,11 @@ An interesting application of gproc is building publish/subscribe patterns.
 Example:
 
 <pre>
-subscribe(EventType) -&gt;
+subscribe(EventType) ->
     %% Gproc notation: {p, l, Name} means {(p)roperty, (l)ocal, Name}
     gproc:reg({p, l, {?MODULE, EventType}}).
 
-notify(EventType, Msg) -&gt;
+notify(EventType, Msg) ->
     Key = {?MODULE, EventType},
     gproc:send({p, l, Key}, {self(), Key, Msg}).
 </pre>

+ 80 - 63
doc/gproc.md

@@ -28,6 +28,8 @@ This module implements an extended process registry
 For a detailed description, see
 [erlang07-wiger.pdf](erlang07-wiger.pdf).
 
+Type and scope for registration and lookup:
+
 
 
 <h2><a name="types">Data Types</a></h2>
@@ -41,10 +43,11 @@ For a detailed description, see
 
 
 
-`context() = {[scope()](#type-scope), [type()](#type-type)} | [type()](#type-type)`
+<pre>context() = {<a href="#type-scope">scope()</a>, <a href="#type-type">type()</a>} | <a href="#type-type">type()</a></pre>
+
 
+{'all','all'} is the default
 
-Local scope is the default
 
 
 <h3 class="typedecl"><a name="type-headpat">headpat()</a></h3>
@@ -52,7 +55,7 @@ Local scope is the default
 
 
 
-`headpat() = {[keypat()](#type-keypat), [pidpat()](#type-pidpat), ValPat}`
+<pre>headpat() = {<a href="#type-keypat">keypat()</a>, <a href="#type-pidpat">pidpat()</a>, ValPat}</pre>
 
 
 
@@ -61,7 +64,7 @@ Local scope is the default
 
 
 
-`key() = {[type()](#type-type), [scope()](#type-scope), any()}`
+<pre>key() = {<a href="#type-type">type()</a>, <a href="#type-scope">scope()</a>, any()}</pre>
 
 
 
@@ -70,7 +73,7 @@ Local scope is the default
 
 
 
-`keypat() = {[sel_type()](#type-sel_type) | [sel_var()](#type-sel_var), l | g | [sel_var()](#type-sel_var), any()}`
+<pre>keypat() = {<a href="#type-sel_type">sel_type()</a> | <a href="#type-sel_var">sel_var()</a>, l | g | <a href="#type-sel_var">sel_var()</a>, any()}</pre>
 
 
 
@@ -79,7 +82,7 @@ Local scope is the default
 
 
 
-`pidpat() = pid() | [sel_var()](#type-sel_var)`
+<pre>pidpat() = pid() | <a href="#type-sel_var">sel_var()</a></pre>
 
 
 sel_var() = DollarVar | '_'.
@@ -90,18 +93,32 @@ sel_var() = DollarVar | '_'.
 
 
 
-`scope() = l | g`
+<pre>scope() = l | g</pre>
+
+
 
 
 l = local registration; g = global registration
 
+Type and scope for select(), qlc() and stepping:
+
+
 
 <h3 class="typedecl"><a name="type-sel_pattern">sel_pattern()</a></h3>
 
 
 
 
-`sel_pattern() = [{[headpat()](#type-headpat), Guards, Prod}]`
+<pre>sel_pattern() = [{<a href="#type-headpat">headpat()</a>, Guards, Prod}]</pre>
+
+
+
+<h3 class="typedecl"><a name="type-sel_scope">sel_scope()</a></h3>
+
+
+
+
+<pre>sel_scope() = scope | all | global | local</pre>
 
 
 
@@ -110,7 +127,7 @@ l = local registration; g = global registration
 
 
 
-`sel_type() = n | p | c | a | names | props | counters | aggr_counters`
+<pre>sel_type() = <a href="#type-type">type()</a> | names | props | counters | aggr_counters</pre>
 
 
 
@@ -119,7 +136,7 @@ l = local registration; g = global registration
 
 
 
-`type() = n | p | c | a`
+<pre>type() = n | p | c | a</pre>
 
 
 n = name; p = property; c = counter;
@@ -145,7 +162,7 @@ a = aggregate_counter
 
 
 
-`add_global_aggr_counter(Name) -&gt; any()`
+`add_global_aggr_counter(Name) -> any()`
 
 
 
@@ -159,7 +176,7 @@ Registers a global (unique) aggregated counter.<a name="add_global_counter-2"></
 
 
 
-`add_global_counter(Name, Initial) -&gt; any()`
+`add_global_counter(Name, Initial) -> any()`
 
 
 
@@ -171,7 +188,7 @@ Registers a global (non-unique) counter. @equiv reg({c,g,Name},Value)<a name="ad
 
 
 
-`add_global_name(Name) -&gt; any()`
+`add_global_name(Name) -> any()`
 
 
 
@@ -183,7 +200,7 @@ Registers a global (unique) name. @equiv reg({n,g,Name})<a name="add_global_prop
 
 
 
-`add_global_property(Name, Value) -&gt; any()`
+`add_global_property(Name, Value) -> any()`
 
 
 
@@ -195,7 +212,7 @@ Registers a global (non-unique) property. @equiv reg({p,g,Name},Value)<a name="a
 
 
 
-`add_local_aggr_counter(Name) -&gt; any()`
+`add_local_aggr_counter(Name) -> any()`
 
 
 
@@ -209,7 +226,7 @@ Registers a local (unique) aggregated counter.<a name="add_local_counter-2"></a>
 
 
 
-`add_local_counter(Name, Initial) -&gt; any()`
+`add_local_counter(Name, Initial) -> any()`
 
 
 
@@ -221,7 +238,7 @@ Registers a local (non-unique) counter. @equiv reg({c,l,Name},Value)<a name="add
 
 
 
-`add_local_name(Name) -&gt; any()`
+`add_local_name(Name) -> any()`
 
 
 
@@ -233,7 +250,7 @@ Registers a local (unique) name. @equiv reg({n,l,Name})<a name="add_local_proper
 
 
 
-`add_local_property(Name, Value) -&gt; any()`
+`add_local_property(Name, Value) -> any()`
 
 
 
@@ -245,7 +262,7 @@ Registers a local (non-unique) property. @equiv reg({p,l,Name},Value)<a name="au
 
 
 
-<pre>audit_process(Pid::pid()) -&gt; ok</pre>
+<pre>audit_process(Pid::pid()) -> ok</pre>
 <br></br>
 
 
@@ -257,7 +274,7 @@ Registers a local (non-unique) property. @equiv reg({p,l,Name},Value)<a name="au
 
 
 
-<pre>await(Key::<a href="#type-key">key()</a>) -&gt; {pid(), Value}</pre>
+<pre>await(Key::<a href="#type-key">key()</a>) -> {pid(), Value}</pre>
 <br></br>
 
 
@@ -271,14 +288,14 @@ Equivalent to [`await(Key, infinity)`](#await-2).<a name="await-2"></a>
 
 
 
-<pre>await(Key::<a href="#type-key">key()</a>, Timeout) -&gt; {pid(), Value}</pre>
-<ul class="definitions"><li><tt>Timeout = integer() | infinity</tt></li></ul>
+<pre>await(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.
 The function raises an exception if the timeout expires. Timeout must be
-either an interger &gt; 0 or 'infinity'.
+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
@@ -290,7 +307,7 @@ registered (the difference: await/2 also returns the value).<a name="cancel_wait
 
 
 
-`cancel_wait(Key, Ref) -&gt; any()`
+`cancel_wait(Key, Ref) -> any()`
 
 <a name="default-1"></a>
 
@@ -300,7 +317,7 @@ registered (the difference: await/2 also returns the value).<a name="cancel_wait
 
 
 
-`default(X1) -&gt; any()`
+`default(X1) -> any()`
 
 <a name="first-1"></a>
 
@@ -310,7 +327,7 @@ registered (the difference: await/2 also returns the value).<a name="cancel_wait
 
 
 
-<pre>first(Type::<a href="#type-type">type()</a>) -&gt; <a href="#type-key">key()</a> | '$end_of_table'</pre>
+<pre>first(Context::<a href="#type-context">context()</a>) -> <a href="#type-key">key()</a> | '$end_of_table'</pre>
 <br></br>
 
 
@@ -329,7 +346,7 @@ The registry behaves as an ordered_set table.<a name="get_value-1"></a>
 
 
 
-<pre>get_value(Key) -&gt; Value</pre>
+<pre>get_value(Key) -> Value</pre>
 <br></br>
 
 
@@ -347,7 +364,7 @@ If no such key is registered to the current process, this function exits.<a name
 
 
 
-<pre>give_away(From::<a href="#type-key">key()</a>, To::pid() | <a href="#type-key">key()</a>) -&gt; undefined | pid()</pre>
+<pre>give_away(From::<a href="#type-key">key()</a>, To::pid() | <a href="#type-key">key()</a>) -> undefined | pid()</pre>
 <br></br>
 
 
@@ -383,8 +400,8 @@ registered.<a name="info-1"></a>
 
 
 
-<pre>info(Pid::pid()) -&gt; ProcessInfo</pre>
-<ul class="definitions"><li><tt>ProcessInfo = [{gproc, [{Key, Value}]} | ProcessInfo]</tt></li></ul>
+<pre>info(Pid::pid()) -> ProcessInfo</pre>
+<ul class="definitions"><li><pre>ProcessInfo = [{gproc, [{Key, Value}]} | ProcessInfo]</pre></li></ul>
 
 
 
@@ -402,7 +419,7 @@ pairs registered to the process.<a name="info-2"></a>
 
 
 
-<pre>info(Pid::pid(), Item::atom()) -&gt; {Item, Info}</pre>
+<pre>info(Pid::pid(), Item::atom()) -> {Item, Info}</pre>
 <br></br>
 
 
@@ -422,7 +439,7 @@ same as [`http://www.erlang.org/doc/man/erlang.html#process_info-2`](http://www.
 
 
 
-<pre>last(Context::<a href="#type-context">context()</a>) -&gt; <a href="#type-key">key()</a> | '$end_of_table'</pre>
+<pre>last(Context::<a href="#type-context">context()</a>) -> <a href="#type-key">key()</a> | '$end_of_table'</pre>
 <br></br>
 
 
@@ -441,7 +458,7 @@ The registry behaves as an ordered_set table.<a name="lookup_global_aggr_counter
 
 
 
-<pre>lookup_global_aggr_counter(Name::any()) -&gt; integer()</pre>
+<pre>lookup_global_aggr_counter(Name::any()) -> integer()</pre>
 <br></br>
 
 
@@ -458,7 +475,7 @@ Fails if there is no such object.<a name="lookup_global_counters-1"></a>
 
 
 
-<pre>lookup_global_counters(Counter::any()) -&gt; [{pid(), Value::integer()}]</pre>
+<pre>lookup_global_counters(Counter::any()) -> [{pid(), Value::integer()}]</pre>
 <br></br>
 
 
@@ -475,7 +492,7 @@ Returns a list of {Pid, Value} tuples for all matching objects.<a name="lookup_g
 
 
 
-<pre>lookup_global_name(Name::any()) -&gt; pid()</pre>
+<pre>lookup_global_name(Name::any()) -> pid()</pre>
 <br></br>
 
 
@@ -491,7 +508,7 @@ Lookup a global unique name. Fails if there is no such name.<a name="lookup_glob
 
 
 
-<pre>lookup_global_properties(Property::any()) -&gt; [{pid(), Value}]</pre>
+<pre>lookup_global_properties(Property::any()) -> [{pid(), Value}]</pre>
 <br></br>
 
 
@@ -508,7 +525,7 @@ Returns a list of {Pid, Value} tuples for all matching objects.<a name="lookup_l
 
 
 
-<pre>lookup_local_aggr_counter(Name::any()) -&gt; integer()</pre>
+<pre>lookup_local_aggr_counter(Name::any()) -> integer()</pre>
 <br></br>
 
 
@@ -525,7 +542,7 @@ Fails if there is no such object.<a name="lookup_local_counters-1"></a>
 
 
 
-<pre>lookup_local_counters(Counter::any()) -&gt; [{pid(), Value::integer()}]</pre>
+<pre>lookup_local_counters(Counter::any()) -> [{pid(), Value::integer()}]</pre>
 <br></br>
 
 
@@ -542,7 +559,7 @@ Returns a list of {Pid, Value} tuples for all matching objects.<a name="lookup_l
 
 
 
-<pre>lookup_local_name(Name::any()) -&gt; pid()</pre>
+<pre>lookup_local_name(Name::any()) -> pid()</pre>
 <br></br>
 
 
@@ -558,7 +575,7 @@ Lookup a local unique name. Fails if there is no such name.<a name="lookup_local
 
 
 
-<pre>lookup_local_properties(Property::any()) -&gt; [{pid(), Value}]</pre>
+<pre>lookup_local_properties(Property::any()) -> [{pid(), Value}]</pre>
 <br></br>
 
 
@@ -575,7 +592,7 @@ Returns a list of {Pid, Value} tuples for all matching objects.<a name="lookup_p
 
 
 
-<pre>lookup_pid(Key) -&gt; Pid</pre>
+<pre>lookup_pid(Key) -> Pid</pre>
 <br></br>
 
 
@@ -590,7 +607,7 @@ Lookup the Pid stored with a key.
 
 
 
-<pre>lookup_pids(Key::<a href="#type-key">key()</a>) -&gt; [pid()]</pre>
+<pre>lookup_pids(Key::<a href="#type-key">key()</a>) -> [pid()]</pre>
 <br></br>
 
 
@@ -610,7 +627,7 @@ For non-unique types, the return value can be a list of any length.<a name="look
 
 
 
-<pre>lookup_value(Key) -&gt; Value</pre>
+<pre>lookup_value(Key) -> Value</pre>
 <br></br>
 
 
@@ -625,7 +642,7 @@ Lookup the value stored with a key.
 
 
 
-<pre>lookup_values(Key::<a href="#type-key">key()</a>) -&gt; [{pid(), Value}]</pre>
+<pre>lookup_values(Key::<a href="#type-key">key()</a>) -> [{pid(), Value}]</pre>
 <br></br>
 
 
@@ -645,7 +662,7 @@ object, the return value can be a list of any length.<a name="mreg-3"></a>
 
 
 
-<pre>mreg(T::<a href="#type-type">type()</a>, X2::<a href="#type-scope">scope()</a>, KVL::[{Key::any(), Value::any()}]) -&gt; true</pre>
+<pre>mreg(T::<a href="#type-type">type()</a>, X2::<a href="#type-scope">scope()</a>, KVL::[{Key::any(), Value::any()}]) -> true</pre>
 <br></br>
 
 
@@ -663,7 +680,7 @@ This function is more efficient than calling [`reg/2`](#reg-2) repeatedly.<a nam
 
 
 
-<pre>nb_wait(Key::<a href="#type-key">key()</a>) -&gt; Ref</pre>
+<pre>nb_wait(Key::<a href="#type-key">key()</a>) -> Ref</pre>
 <br></br>
 
 
@@ -679,7 +696,7 @@ The caller can expect to receive a message,
 
 
 
-<pre>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'</pre>
+<pre>next(Context::<a href="#type-context">context()</a>, Key::<a href="#type-key">key()</a>) -> <a href="#type-key">key()</a> | '$end_of_table'</pre>
 <br></br>
 
 
@@ -698,7 +715,7 @@ The registry behaves as an ordered_set table.<a name="prev-2"></a>
 
 
 
-<pre>prev(Context::<a href="#type-context">context()</a>, Key::<a href="#type-key">key()</a>) -&gt; <a href="#type-key">key()</a> | '$end_of_table'</pre>
+<pre>prev(Context::<a href="#type-context">context()</a>, Key::<a href="#type-key">key()</a>) -> <a href="#type-key">key()</a> | '$end_of_table'</pre>
 <br></br>
 
 
@@ -717,7 +734,7 @@ The registry behaves as an ordered_set table.<a name="reg-1"></a>
 
 
 
-<pre>reg(Key::<a href="#type-key">key()</a>) -&gt; true</pre>
+<pre>reg(Key::<a href="#type-key">key()</a>) -> true</pre>
 <br></br>
 
 
@@ -731,7 +748,7 @@ Equivalent to [`reg(Key, default(Key))`](#reg-2).<a name="reg-2"></a>
 
 
 
-<pre>reg(Key::<a href="#type-key">key()</a>, Value) -&gt; true</pre>
+<pre>reg(Key::<a href="#type-key">key()</a>, Value) -> true</pre>
 <br></br>
 
 
@@ -749,7 +766,7 @@ Register a name or property for the current process
 
 
 
-<pre>select(Pat::<a href="#type-select_pattern">select_pattern()</a>) -&gt; [<a href="#type-sel_object">sel_object()</a>]</pre>
+<pre>select(Pat::<a href="#type-select_pattern">select_pattern()</a>) -> [<a href="#type-sel_object">sel_object()</a>]</pre>
 <br></br>
 
 
@@ -763,7 +780,7 @@ Equivalent to [`select(all, Pat)`](#select-2).<a name="select-2"></a>
 
 
 
-<pre>select(Type::<a href="#type-sel_type">sel_type()</a>, Pat::<a href="#type-sel_pattern">sel_pattern()</a>) -&gt; [{Key, Pid, Value}]</pre>
+<pre>select(Context::<a href="#type-context">context()</a>, Pat::<a href="#type-sel_pattern">sel_pattern()</a>) -> [{Key, Pid, Value}]</pre>
 <br></br>
 
 
@@ -782,7 +799,7 @@ but the select patterns are transformed appropriately.<a name="select-3"></a>
 
 
 
-<pre>select(Type::<a href="#type-sel_type">sel_type()</a>, Pat::<a href="#type-sel_patten">sel_patten()</a>, Limit::integer()) -&gt; [{Key, Pid, Value}]</pre>
+<pre>select(Context::<a href="#type-context">context()</a>, Pat::<a href="#type-sel_patten">sel_patten()</a>, Limit::integer()) -> [{Key, Pid, Value}]</pre>
 <br></br>
 
 
@@ -800,7 +817,7 @@ See [`http://www.erlang.org/doc/man/ets.html#select-3`](http://www.erlang.org/do
 
 
 
-<pre>send(Key::<a href="#type-key">key()</a>, Msg::any()) -&gt; Msg</pre>
+<pre>send(Key::<a href="#type-key">key()</a>, Msg::any()) -> Msg</pre>
 <br></br>
 
 
@@ -821,7 +838,7 @@ property), Msg will be send to all processes that have such an object.<a name="s
 
 
 
-<pre>set_value(Key::<a href="#type-key">key()</a>, Value) -&gt; true</pre>
+<pre>set_value(Key::<a href="#type-key">key()</a>, Value) -> true</pre>
 <br></br>
 
 
@@ -845,7 +862,7 @@ it must be an integer.<a name="start_link-0"></a>
 
 
 
-<pre>start_link() -&gt; {ok, pid()}</pre>
+<pre>start_link() -> {ok, pid()}</pre>
 <br></br>
 
 
@@ -864,7 +881,7 @@ starting the gproc application.<a name="table-1"></a>
 
 
 
-<pre>table(Context::<a href="#type-context">context()</a>) -&gt; any()</pre>
+<pre>table(Context::<a href="#type-context">context()</a>) -> any()</pre>
 <br></br>
 
 
@@ -878,7 +895,7 @@ Equivalent to [`table(Context, [])`](#table-2).<a name="table-2"></a>
 
 
 
-<pre>table(Context::<a href="#type-context">context()</a>, Opts) -&gt; any()</pre>
+<pre>table(Context::<a href="#type-context">context()</a>, Opts) -> any()</pre>
 <br></br>
 
 
@@ -894,7 +911,7 @@ See [`http://www.erlang.org/doc/man/qlc.html`](http://www.erlang.org/doc/man/qlc
 
 
 
-<pre>unreg(Key::<a href="#type-key">key()</a>) -&gt; true</pre>
+<pre>unreg(Key::<a href="#type-key">key()</a>) -> true</pre>
 <br></br>
 
 
@@ -908,7 +925,7 @@ Unregister a name or property.<a name="unregister_name-1"></a>
 
 
 
-`unregister_name(Key) -&gt; any()`
+`unregister_name(Key) -> any()`
 
 
 
@@ -920,7 +937,7 @@ Equivalent to `unreg / 1`.<a name="update_counter-2"></a>
 
 
 
-<pre>update_counter(Key::<a href="#type-key">key()</a>, Incr::integer()) -&gt; integer()</pre>
+<pre>update_counter(Key::<a href="#type-key">key()</a>, Incr::integer()) -> integer()</pre>
 <br></br>
 
 
@@ -940,7 +957,7 @@ will fail if the type of object referred to by Key is not a counter.<a name="whe
 
 
 
-<pre>where(Key::<a href="#type-key">key()</a>) -&gt; pid()</pre>
+<pre>where(Key::<a href="#type-key">key()</a>) -> pid()</pre>
 <br></br>
 
 
@@ -960,7 +977,7 @@ cases.<a name="whereis_name-1"></a>
 
 
 
-`whereis_name(Key) -&gt; any()`
+`whereis_name(Key) -> any()`
 
 
 

+ 3 - 3
doc/gproc_app.md

@@ -34,7 +34,7 @@ __Behaviours:__ [`application`](application.md).
 
 
 
-`start() -&gt; any()`
+`start() -> any()`
 
 <a name="start-2"></a>
 
@@ -44,7 +44,7 @@ __Behaviours:__ [`application`](application.md).
 
 
 
-`start(Type, StartArgs) -&gt; any()`
+`start(Type, StartArgs) -> any()`
 
 <a name="stop-1"></a>
 
@@ -54,5 +54,5 @@ __Behaviours:__ [`application`](application.md).
 
 
 
-`stop(State) -&gt; any()`
+`stop(State) -> any()`
 

+ 24 - 24
doc/gproc_dist.md

@@ -51,7 +51,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`code_change(FromVsn, S, Extra, E) -&gt; any()`
+`code_change(FromVsn, S, Extra, E) -> any()`
 
 <a name="elected-2"></a>
 
@@ -61,7 +61,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`elected(S, E) -&gt; any()`
+`elected(S, E) -> any()`
 
 <a name="elected-3"></a>
 
@@ -71,7 +71,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`elected(S, E, Node) -&gt; any()`
+`elected(S, E, Node) -> any()`
 
 <a name="from_leader-3"></a>
 
@@ -81,7 +81,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`from_leader(Ops, S, E) -&gt; any()`
+`from_leader(Ops, S, E) -> any()`
 
 <a name="give_away-2"></a>
 
@@ -91,7 +91,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`give_away(Key, To) -&gt; any()`
+`give_away(Key, To) -> any()`
 
 <a name="handle_DOWN-3"></a>
 
@@ -101,7 +101,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`handle_DOWN(Node, S, E) -&gt; any()`
+`handle_DOWN(Node, S, E) -> any()`
 
 <a name="handle_call-4"></a>
 
@@ -111,7 +111,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`handle_call(X1, X2, S, X4) -&gt; any()`
+`handle_call(X1, X2, S, X4) -> any()`
 
 <a name="handle_cast-3"></a>
 
@@ -121,7 +121,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`handle_cast(Msg, S, X3) -&gt; any()`
+`handle_cast(Msg, S, X3) -> any()`
 
 <a name="handle_info-2"></a>
 
@@ -131,7 +131,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`handle_info(X1, S) -&gt; any()`
+`handle_info(X1, S) -> any()`
 
 <a name="handle_leader_call-4"></a>
 
@@ -141,7 +141,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`handle_leader_call(X1, From, S, E) -&gt; any()`
+`handle_leader_call(X1, From, S, E) -> any()`
 
 <a name="handle_leader_cast-3"></a>
 
@@ -151,7 +151,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`handle_leader_cast(X1, S, E) -&gt; any()`
+`handle_leader_cast(X1, S, E) -> any()`
 
 <a name="init-1"></a>
 
@@ -161,7 +161,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`init(Opts) -&gt; any()`
+`init(Opts) -> any()`
 
 <a name="leader_call-1"></a>
 
@@ -171,7 +171,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`leader_call(Req) -&gt; any()`
+`leader_call(Req) -> any()`
 
 <a name="leader_cast-1"></a>
 
@@ -181,7 +181,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`leader_cast(Msg) -&gt; any()`
+`leader_cast(Msg) -> any()`
 
 <a name="mreg-2"></a>
 
@@ -191,7 +191,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`mreg(T, KVL) -&gt; any()`
+`mreg(T, KVL) -> any()`
 
 <a name="reg-1"></a>
 
@@ -201,7 +201,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`reg(Key) -&gt; any()`
+`reg(Key) -> any()`
 
 <a name="reg-2"></a>
 
@@ -211,7 +211,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`reg(Key, Value) -&gt; any()`
+`reg(Key, Value) -> any()`
 
 
 
@@ -229,7 +229,7 @@ Scope = l | g (global or local)
 
 
 
-`set_value(Key, Value) -&gt; any()`
+`set_value(Key, Value) -> any()`
 
 <a name="start_link-0"></a>
 
@@ -239,7 +239,7 @@ Scope = l | g (global or local)
 
 
 
-`start_link() -&gt; any()`
+`start_link() -> any()`
 
 <a name="start_link-1"></a>
 
@@ -249,7 +249,7 @@ Scope = l | g (global or local)
 
 
 
-`start_link(Nodes) -&gt; any()`
+`start_link(Nodes) -> any()`
 
 <a name="surrendered-3"></a>
 
@@ -259,7 +259,7 @@ Scope = l | g (global or local)
 
 
 
-`surrendered(S, X2, E) -&gt; any()`
+`surrendered(S, X2, E) -> any()`
 
 <a name="terminate-2"></a>
 
@@ -269,7 +269,7 @@ Scope = l | g (global or local)
 
 
 
-`terminate(Reason, S) -&gt; any()`
+`terminate(Reason, S) -> any()`
 
 <a name="unreg-1"></a>
 
@@ -279,7 +279,7 @@ Scope = l | g (global or local)
 
 
 
-`unreg(Key) -&gt; any()`
+`unreg(Key) -> any()`
 
 <a name="update_counter-2"></a>
 
@@ -289,5 +289,5 @@ Scope = l | g (global or local)
 
 
 
-`update_counter(Key, Incr) -&gt; any()`
+`update_counter(Key, Incr) -> any()`
 

+ 2 - 2
doc/gproc_init.md

@@ -32,7 +32,7 @@ Module gproc_init
 
 
 
-<pre>hard_reset() -&gt; ok</pre>
+<pre>hard_reset() -> ok</pre>
 <br></br>
 
 
@@ -44,7 +44,7 @@ Module gproc_init
 
 
 
-<pre>soft_reset() -&gt; ok</pre>
+<pre>soft_reset() -> ok</pre>
 <br></br>
 
 

+ 2 - 2
doc/gproc_sup.md

@@ -34,7 +34,7 @@ __Behaviours:__ [`supervisor`](supervisor.md).
 
 
 
-`init(Args) -&gt; any()`
+`init(Args) -> any()`
 
 
 
@@ -46,5 +46,5 @@ The main GPROC supervisor.<a name="start_link-1"></a>
 
 
 
-`start_link(Args) -&gt; any()`
+`start_link(Args) -> any()`
 

BIN
rebar


+ 123 - 112
src/gproc.erl

@@ -20,12 +20,18 @@
 %% <p>For a detailed description, see
 %% <a href="erlang07-wiger.pdf">erlang07-wiger.pdf</a>.</p>
 %%
+%% Type and scope for registration and lookup:
+%%
 %% @type type()  = n | p | c | a. n = name; p = property; c = counter; 
 %%                                a = aggregate_counter
 %% @type scope() = l | g. l = local registration; g = global registration
-%% @type context() = {scope(), type()} | type(). Local scope is the default
-%% @type sel_type() = n | p | c | a |
-%%                    names | props | counters | aggr_counters.
+%%
+%% Type and scope for select(), qlc() and stepping:
+%%
+%% @type sel_scope() = scope | all | global | local.
+%% @type sel_type() = type() | names | props | counters | aggr_counters.
+%% @type context() = {scope(), type()} | type(). {'all','all'} is the default
+%%
 %% @type headpat() = {keypat(),pidpat(),ValPat}.
 %% @type keypat() = {sel_type() | sel_var(),
 %%                   l | g | sel_var(),
@@ -49,10 +55,10 @@
          cancel_wait/2,
          lookup_pid/1,
          lookup_pids/1,
-	 lookup_value/1,
+         lookup_value/1,
          lookup_values/1,
          update_counter/2,
-	 give_away/2,
+         give_away/2,
          send/2,
          info/1, info/2,
          select/1, select/2, select/3,
@@ -82,7 +88,7 @@
 
 %% Callbacks for behaviour support
 -export([whereis_name/1,
-	 unregister_name/1]).
+         unregister_name/1]).
 
 -export([default/1]).
 
@@ -123,7 +129,7 @@
 %% starting the gproc application.
 %% @end
 start_link() ->
-    create_tabs(),
+    _ = create_tabs(),
     gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
 
 %% spec(Name::any()) -> true
@@ -316,18 +322,18 @@ request_wait({n,C,_} = Key, Timeout) when C==l; C==g ->
                    erlang:error(badarg, [Key, Timeout])
            end,
     WRef = case {call({await,Key,self()}, C), C} of
-	       {{R, {Kg,Pg,Vg}}, g} ->
-		   self() ! {gproc, R, registered, {Kg,Pg,Vg}},
-		   R;
-	       {R,_} ->
-		   R
-	   end,
+               {{R, {Kg,Pg,Vg}}, g} ->
+                   self() ! {gproc, R, registered, {Kg,Pg,Vg}},
+                   R;
+               {R,_} ->
+                   R
+           end,
     receive
         {gproc, WRef, registered, {_K, Pid, V}} ->
-	    case TRef of
-		no_timer -> ignore;
-		_ -> erlang:cancel_timer(TRef)
-	    end,
+            case TRef of
+                no_timer -> ignore;
+                _ -> erlang:cancel_timer(TRef)
+            end,
             {Pid, V};
         {timeout, TRef, gproc_timeout} ->
             cancel_wait(Key, WRef),
@@ -430,24 +436,24 @@ unregister_name(Key) ->
 select(Pat) ->
     select(all, Pat).
 
-%% @spec (Type::sel_type(), Pat::sel_pattern()) -> [{Key, Pid, Value}]
+%% @spec (Context::context(), Pat::sel_pattern()) -> [{Key, Pid, Value}]
 %%
 %% @doc Perform a select operation on the process registry.
 %%
 %% The physical representation in the registry may differ from the above,
 %% but the select patterns are transformed appropriately.
 %% @end
-select(Type, Pat) ->
-    ets:select(?TAB, pattern(Pat, Type)).
+select(Context, Pat) ->
+    ets:select(?TAB, pattern(Pat, Context)).
 
-%% @spec (Type::sel_type(), Pat::sel_patten(), Limit::integer()) ->
+%% @spec (Context::context(), Pat::sel_patten(), Limit::integer()) ->
 %%          [{Key, Pid, Value}]
 %% @doc Like {@link select/2} but returns Limit objects at a time.
 %%
 %% See [http://www.erlang.org/doc/man/ets.html#select-3].
 %% @end
-select(Type, Pat, Limit) ->
-    ets:select(?TAB, pattern(Pat, Type), Limit).
+select(Context, Pat, Limit) ->
+    ets:select(?TAB, pattern(Pat, Context), Limit).
 
 
 %%% Local properties can be registered in the local process, since
@@ -558,10 +564,10 @@ where({T,_,_}=Key) ->
             case ets:lookup(?TAB, {Key,T}) of
                 [{_, P, _Value}] ->
                     case my_is_process_alive(P) of
-			true -> P;
-			false ->
-			    undefined
-		    end;
+                        true -> P;
+                        false ->
+                            undefined
+                    end;
                 _ ->  % may be [] or [{Key,Waiters}]
                     undefined
             end;
@@ -584,10 +590,10 @@ whereis_name(Key) ->
 %%
 lookup_pids({T,_,_} = Key) ->
     L = if T==n orelse T==a ->
-		ets:select(?TAB, [{{{Key,T}, '$1', '_'},[],['$1']}]);
-	   true ->
-		ets:select(?TAB, [{{{Key,'_'}, '$1', '_'},[],['$1']}])
-	end,
+                ets:select(?TAB, [{{{Key,T}, '$1', '_'},[],['$1']}]);
+           true ->
+                ets:select(?TAB, [{{{Key,'_'}, '$1', '_'},[],['$1']}])
+        end,
     [P || P <- L, my_is_process_alive(P)].
 
 
@@ -613,10 +619,10 @@ my_is_process_alive(_) ->
 %%
 lookup_values({T,_,_} = Key) ->
     L = if T==n orelse T==a ->
-		ets:select(?TAB, [{{{Key,T}, '$1', '$2'},[],[{{'$1','$2'}}]}]);
-	   true ->
-		ets:select(?TAB, [{{{Key,'_'}, '$1', '$2'},[],[{{'$1','$2'}}]}])
-	end,
+                ets:select(?TAB, [{{{Key,T}, '$1', '$2'},[],[{{'$1','$2'}}]}]);
+           true ->
+                ets:select(?TAB, [{{{Key,'_'}, '$1', '$2'},[],[{{'$1','$2'}}]}])
+        end,
     [Pair || {P,_} = Pair <- L, my_is_process_alive(P)].
 
 
@@ -696,7 +702,7 @@ send(_, _) ->
     erlang:error(badarg).
 
 
-%% @spec (Type :: type()) -> key() | '$end_of_table'
+%% @spec (Context :: context()) -> key() | '$end_of_table'
 %%
 %% @doc Behaves as ets:first(Tab) for a given type of registration object.
 %%
@@ -704,8 +710,9 @@ send(_, _) ->
 %%  The registry behaves as an ordered_set table.
 %% @end
 %%
-first(Type) ->
-    {HeadPat,_} = headpat(Type, '_', '_', '_'),
+first(Context) ->
+    {S, T} = get_s_t(Context),
+    {HeadPat,_} = headpat({S, T}, '_', '_', '_'),
     case ets:select(?TAB, [{HeadPat,[],[{element,1,'$_'}]}], 1) of
         {[First], _} ->
             First;
@@ -723,8 +730,8 @@ first(Type) ->
 %%
 last(Context) ->
     {S, T} = get_s_t(Context),
-    S1 = if S == '_'; S == l -> m;
-            S == g -> h
+    S1 = if S == '_'; S == l -> m;              % 'm' comes after 'l'
+            S == g -> h                         % 'h' comes between 'g' & 'l'
          end,
     Beyond = {{T,S1,[]},[]},
     step(ets:prev(?TAB, Beyond), S, T).
@@ -825,20 +832,20 @@ handle_cast({cancel_wait, Pid, {T,_,_} = Key, Ref}, S) ->
     case ets:lookup(?TAB, {Key,T}) of
         [{K, Waiters}] ->
             case Waiters -- [{Pid,Ref}] of
-		[] ->
-		    ets:delete(?TAB, K),
-		    ets:delete(?TAB, Rev);
-		NewWaiters ->
-		    ets:insert(?TAB, {K, NewWaiters}),
-		    case lists:keymember(Pid, 1, NewWaiters) of
-			true -> 
-			    %% should be extremely unlikely
-			    ok;
-			false ->
-			    %% delete the reverse entry
-			    ets:delete(?TAB, Rev)
-		    end
-	    end;
+                [] ->
+                    ets:delete(?TAB, K),
+                    ets:delete(?TAB, Rev);
+                NewWaiters ->
+                    ets:insert(?TAB, {K, NewWaiters}),
+                    case lists:keymember(Pid, 1, NewWaiters) of
+                        true -> 
+                            %% should be extremely unlikely
+                            ok;
+                        false ->
+                            %% delete the reverse entry
+                            ets:delete(?TAB, Rev)
+                    end
+            end;
         _ ->
             ignore
     end,
@@ -887,10 +894,10 @@ handle_call({set, {_,l,_} = Key, Value}, {Pid,_}, S) ->
     end;
 handle_call({audit_process, Pid}, _, S) ->
     case is_process_alive(Pid) of
-	false ->
-	    process_is_down(Pid);
-	true ->
-	    ignore
+        false ->
+            process_is_down(Pid);
+        true ->
+            ignore
     end,
     {reply, ok, S};
 handle_call({give_away, Key, To}, {Pid,_}, S) ->
@@ -1020,69 +1027,69 @@ process_is_down(Pid) ->
 do_give_away({T,l,_} = K, To, Pid) when T==n; T==a ->
     Key = {K, T},
     case ets:lookup(?TAB, Key) of
-	[{_, Pid, Value}] ->
-	    %% Pid owns the reg; allowed to give_away
-	    case pid_to_give_away_to(To) of
-		Pid ->
-		    %% Give away to ourselves? Why not? We'll allow it,
-		    %% but nothing needs to be done.
-		    Pid;
-		ToPid when is_pid(ToPid) ->
-		    ets:insert(?TAB, [{Key, ToPid, Value},
-				      {{ToPid, K}, r}]),
-		    ets:delete(?TAB, {Pid, K}),
-		    gproc_lib:ensure_monitor(ToPid, l),
-		    ToPid;
-		undefined ->
-		    gproc_lib:remove_reg(K, Pid),
-		    undefined
-	    end;
-	_ ->
-	    badarg
+        [{_, Pid, Value}] ->
+            %% Pid owns the reg; allowed to give_away
+            case pid_to_give_away_to(To) of
+                Pid ->
+                    %% Give away to ourselves? Why not? We'll allow it,
+                    %% but nothing needs to be done.
+                    Pid;
+                ToPid when is_pid(ToPid) ->
+                    ets:insert(?TAB, [{Key, ToPid, Value},
+                                      {{ToPid, K}, r}]),
+                    ets:delete(?TAB, {Pid, K}),
+                    gproc_lib:ensure_monitor(ToPid, l),
+                    ToPid;
+                undefined ->
+                    gproc_lib:remove_reg(K, Pid),
+                    undefined
+            end;
+        _ ->
+            badarg
     end;
 do_give_away({T,l,_} = K, To, Pid) when T==c; T==p ->
     Key = {K, Pid},
     case ets:lookup(?TAB, Key) of
-	[{_, Pid, Value}] ->
-	    case pid_to_give_away_to(To) of
-		ToPid when is_pid(ToPid) ->
-		    ToKey = {K, ToPid},
-		    case ets:member(?TAB, ToKey) of
-			true ->
-			    badarg;
-			false ->
-			    ets:insert(?TAB, [{ToKey, ToPid, Value},
-					      {{ToPid, K}, r}]),
-			    ets:delete(?TAB, {Pid, K}),
-			    ets:delete(?TAB, Key),
-			    gproc_lib:ensure_monitor(ToPid, l),
-			    ToPid
-		    end;
-		undefined ->
-		    gproc_lib:remove_reg(K, Pid),
-		    undefined
-	    end;
-	_ ->
-	    badarg
+        [{_, Pid, Value}] ->
+            case pid_to_give_away_to(To) of
+                ToPid when is_pid(ToPid) ->
+                    ToKey = {K, ToPid},
+                    case ets:member(?TAB, ToKey) of
+                        true ->
+                            badarg;
+                        false ->
+                            ets:insert(?TAB, [{ToKey, ToPid, Value},
+                                              {{ToPid, K}, r}]),
+                            ets:delete(?TAB, {Pid, K}),
+                            ets:delete(?TAB, Key),
+                            gproc_lib:ensure_monitor(ToPid, l),
+                            ToPid
+                    end;
+                undefined ->
+                    gproc_lib:remove_reg(K, Pid),
+                    undefined
+            end;
+        _ ->
+            badarg
     end.
-			
+                        
 
-pid_to_give_away_to(P) when is_pid(P), node(P) == node() ->		    
+pid_to_give_away_to(P) when is_pid(P), node(P) == node() ->                 
     P;
 pid_to_give_away_to({T,l,_} = Key) when T==n; T==a ->
     case ets:lookup(?TAB, {Key, T}) of
-	[{_, Pid, _}] ->
-	    Pid;
-	_ ->
-	    undefined
+        [{_, Pid, _}] ->
+            Pid;
+        _ ->
+            undefined
     end.
 
 create_tabs() ->
     case ets:info(?TAB, name) of
-	undefined ->
-	    ets:new(?TAB, [ordered_set, public, named_table]);
-	_ ->
-	    ok
+        undefined ->
+            ets:new(?TAB, [ordered_set, public, named_table]);
+        _ ->
+            ok
     end.
 
 
@@ -1150,7 +1157,7 @@ obj_prod_l() ->
       {element,3,'$_'} ].
 
 
-headpat({S, T}, V1,V2,V3) when S==global; S==local; S==all ->
+headpat({S, T}, V1,V2,V3) ->
     headpat(type(T), scope(S), V1,V2,V3);
 headpat(T, V1, V2, V3) when is_atom(T) ->
     headpat(type(T), l, V1, V2, V3);
@@ -1198,10 +1205,13 @@ subst(X, V, F, Vs) ->
             {V, Vs}
     end.
 
+scope('_')    -> '_';
 scope(all)    -> '_';
 scope(global) -> g;
-scope(local)  -> l.
+scope(local)  -> l;
+scope(S) when S==l; S==g -> S.
 
+type('_')   -> '_';
 type(all)   -> '_';
 type(T) when T==n; T==p; T==c; T==a -> T;
 type(names) -> n;
@@ -1217,7 +1227,7 @@ keypat(Context) ->
 
 get_s_t({S,T}) -> {scope(S), type(T)};
 get_s_t(T) when is_atom(T) ->
-    {l, type(T)}.
+    {scope(all), type(T)}.
 
 is_var('$1') -> {true,1};
 is_var('$2') -> {true,2};
@@ -1285,7 +1295,8 @@ table(Context) ->
 %% Context specifies which subset of the registry should be queried.
 %% See [http://www.erlang.org/doc/man/qlc.html].
 %% @end
-table(Ctxt, Opts) ->
+table(Context, Opts) ->
+    Ctxt = get_s_t(Context),
     [Traverse, NObjs] = [proplists:get_value(K,Opts,Def) ||
                             {K,Def} <- [{traverse,select}, {n_objects,100}]],
     TF = case Traverse of

+ 15 - 25
test/gproc_dist_tests.erl

@@ -22,16 +22,12 @@
 -export([t_spawn/1, t_spawn_reg/2]).
 
 dist_test_() ->
-    {timeout, 90,
+    {timeout, 120,
      [{setup,
        fun() ->
-	       Ns = start_slaves([n1, n2]),
+	       Ns = start_slaves([dist_test_n1, dist_test_n2]),
 	       ?assertMatch({[ok,ok],[]},
 			    rpc:multicall(Ns, application, start, [gproc])),
-	       %% Without this trace output, the test times out on my Mac...
-	       dbg:tracer(),
-	       dbg:tpl(?MODULE, x),
-	       dbg:p(all,[c]),
 	       ?debugVal(Ns)
        end,
        fun(Ns) ->
@@ -40,8 +36,9 @@ dist_test_() ->
        fun(Ns) ->
 	       {inorder,
 		[
-		 {inparallel, [fun() ->
-				       ?debugVal(t_simple_reg(Ns))
+		 {inparallel, [
+			       fun() ->
+			       	       ?debugVal(t_simple_reg(Ns))
 			       end,
 			       fun() ->
 			       	       ?debugVal(t_await_reg(Ns))
@@ -54,9 +51,9 @@ dist_test_() ->
 			       end
 			      ]
 		 },
-		 {timeout, 60, [fun() ->
-					?debugVal(t_fail_node(Ns))
-				end]}
+		 {timeout, 90, [fun() ->
+		 			?debugVal(t_fail_node(Ns))
+		 		end]}
 		]}
        end
       }]}.
@@ -64,7 +61,6 @@ dist_test_() ->
 -define(T_NAME, {n, g, {?MODULE, ?LINE}}).
 
 t_simple_reg([H|_] = Ns) ->
-    ?debugMsg(t_simple_reg),
     Name = ?T_NAME,
     P = t_spawn_reg(H, Name),
     ?assertMatch(ok, t_lookup_everywhere(Name, Ns, P)),
@@ -73,7 +69,6 @@ t_simple_reg([H|_] = Ns) ->
     ?assertMatch(ok, t_call(P, die)).
 
 t_await_reg([A,B|_]) ->
-    ?debugMsg(t_await_reg),
     Name = ?T_NAME,
     P = t_spawn(A),
     Ref = erlang:monitor(process, P),
@@ -92,15 +87,9 @@ t_await_reg([A,B|_]) ->
     ?assertMatch(ok, t_call(P1, die)).
 
 t_await_reg_exists([A,B|_]) ->
-    ?debugMsg(t_await_reg_exists),
     Name = ?T_NAME,
     P = t_spawn(A),
     Ref = erlang:monitor(process, P),
-     %% dbg:tracer(),
-     %% [dbg:n(N) || N <- Ns],
-     %% dbg:tpl(gproc_dist,x),
-     %% dbg:tpl(gproc_lib,await,x),
-     %% dbg:p(all,[c]),
     P1 = t_spawn_reg(B, Name),
     P ! {self(), Ref, {apply, gproc, await, [Name]}},
     ?assert(P1 == receive
@@ -115,22 +104,20 @@ t_await_reg_exists([A,B|_]) ->
     ?assertMatch(ok, t_call(P1, die)).
 
 t_give_away([A,B|_] = Ns) ->
-    ?debugMsg(t_give_away),
     Na = ?T_NAME,
     Nb = ?T_NAME,
     Pa = t_spawn_reg(A, Na),
     Pb = t_spawn_reg(B, Nb),
     ?assertMatch(ok, t_lookup_everywhere(Na, Ns, Pa)),
     ?assertMatch(ok, t_lookup_everywhere(Nb, Ns, Pb)),
-    ?assertMatch(Pb, t_call(Pa, {apply, {gproc, give_away, [Na, Nb]}})),
+    ?assertMatch(Pb, t_call(Pa, {apply, gproc, give_away, [Na, Nb]})),
     ?assertMatch(ok, t_lookup_everywhere(Na, Ns, Pb)),
-    ?assertMatch(Pa, t_call(Pa, {apply, {gproc, give_away, [Na, Pa]}})),
+    ?assertMatch(Pa, t_call(Pb, {apply, gproc, give_away, [Na, Pa]})),
     ?assertMatch(ok, t_lookup_everywhere(Na, Ns, Pa)),
     ?assertMatch(ok, t_call(Pa, die)),
     ?assertMatch(ok, t_call(Pb, die)).
 
 t_fail_node([A,B|_] = Ns) ->
-    ?debugMsg(t_fail_node),
     Na = ?T_NAME,
     Nb = ?T_NAME,
     Pa = t_spawn_reg(A, Na),
@@ -203,12 +190,15 @@ t_loop() ->
 	    From ! {self(), Ref, ok};
 	{From, Ref, {apply, M, F, A}} ->
 	    From ! {self(), Ref, apply(M, F, A)},
-	    t_loop()
+	    t_loop();
+	Other ->
+	    ?debugFmt("got unknown msg: ~p~n", [Other]),
+	    exit({unknown_msg, Other})
     end.
 
 start_slaves(Ns) ->
     [H|T] = Nodes = [start_slave(N) || N <- Ns],
-    _ = [{N, rpc:call(H, net, ping, [N])} || N <- T],
+    _ = [rpc:call(H, net, ping, [N]) || N <- T],
     Nodes.
 	       
 start_slave(Name) ->

+ 39 - 0
test/gproc_tests.erl

@@ -52,6 +52,8 @@ reg_test_() ->
       , ?_test(t_is_clean())
       , {spawn, ?_test(t_give_away_and_back())}
       , ?_test(t_is_clean())
+      , {spawn, ?_test(t_select())}
+      , ?_test(t_is_clean())
      ]}.
 
 t_simple_reg() ->
@@ -185,6 +187,43 @@ t_give_away_and_back() ->
     ?assertEqual(Me, gproc:where(From)),
     ?assertEqual(ok, t_call(P, die)).
 
+t_select() ->
+    ?assertEqual(true, gproc:reg({n, l, {n,1}}, x)),
+    ?assertEqual(true, gproc:reg({n, l, {n,2}}, y)),
+    ?assertEqual(true, gproc:reg({p, l, {p,1}}, x)),
+    ?assertEqual(true, gproc:reg({p, l, {p,2}}, y)),
+    ?assertEqual(true, gproc:reg({c, l, {c,1}}, 1)),
+    ?assertEqual(true, gproc:reg({a, l, {c,1}}, undefined)),
+    %% local names
+    ?assertEqual(
+       [{{n,l,{n,1}},self(),x},
+	{{n,l,{n,2}},self(),y}], gproc:select(
+				   {local,names},
+				   [{{{n,l,'_'},'_','_'},[],['$_']}])),
+    %% mactch local names on value
+    ?assertEqual(
+       [{{n,l,{n,1}},self(),x}], gproc:select(
+				   {local,names},
+				   [{{{n,l,'_'},'_',x},[],['$_']}])),
+    %% match all on value
+    ?assertEqual(
+       [{{n,l,{n,1}},self(),x},
+	{{p,l,{p,1}},self(),x}], gproc:select(
+				   {all,all},
+				   [{{{'_',l,'_'},'_',x},[],['$_']}])),
+    %% match all on pid
+    ?assertEqual(
+       [{{a,l,{c,1}},self(),1},
+	{{c,l,{c,1}},self(),1},
+	{{n,l,{n,1}},self(),x},
+	{{n,l,{n,2}},self(),y},
+	{{p,l,{p,1}},self(),x},
+	{{p,l,{p,2}},self(),y}
+       ], gproc:select(
+	    {all,all},
+	    [{{'_',self(),'_'},[],['$_']}])).
+
+
 t_loop() ->
     receive
 	{From, {give_away, Key}} ->