Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

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).
  • Loading branch information...
commit 59365c4befa65648b7a7c7416ba19d2fecfd8bed 1 parent fdc3283
Ulf Wiger authored
View
4 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>
View
4 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>
View
143 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()`
View
6 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()`
View
48 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()`
View
4 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>
View
4 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()`
View
BIN  rebar
Binary file not shown
View
235 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
View
40 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) ->
View
39 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}} ->
Please sign in to comment.
Something went wrong with that request. Please try again.