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

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 лет назад
Родитель
Сommit
59365c4bef
11 измененных файлов с 292 добавлено и 235 удалено
  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()`
 


+ 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}} ->