Browse Source

Added gproc:give_away/2 and introduced eunit tests for gproc_dist

EUnit tests have been moved out of the gproc and gproc_dist modules,
and put into the test/ directory.

The contents of README.md have been moved to doc/overview.edoc, and
edown is instructed to automatically update the top-level README.md.

Abecciu's gen_leader added to the rebar dependency list.
Ulf Wiger 14 years ago
parent
commit
2393bf45ee
14 changed files with 869 additions and 426 deletions
  1. 81 11
      README.md
  2. 51 18
      doc/README.md
  3. 90 164
      doc/gproc.md
  4. 3 6
      doc/gproc_app.md
  5. 36 39
      doc/gproc_dist.md
  6. 5 4
      doc/gproc_init.md
  7. 1 3
      doc/gproc_lib.md
  8. 2 5
      doc/gproc_sup.md
  9. 41 19
      doc/overview.edoc
  10. 4 1
      rebar.config
  11. 149 147
      src/gproc_dist.erl
  12. 7 9
      src/gproc_init.erl
  13. 173 0
      test/gproc_dist_tests.erl
  14. 226 0
      test/gproc_tests.erl

+ 81 - 11
README.md

@@ -1,25 +1,95 @@
-Gproc - Extended Process Dictionary for Erlang
-==============================================
+
+
+<h1>The gproc application</h1>
+
+The gproc application
+=====================
+Extended process dictionary.
+
+__Authors:__ Ulf Wiger ([`ulf.wiger@erlang-solutions.com`](mailto:ulf.wiger@erlang-solutions.com)), Joseph Wayne Norton ([`norton@geminimobile.com`](mailto:norton@geminimobile.com)).
+
+Extended process dictionary
+
+
+
+<h3><a name="Introduction">Introduction</a></h3>
+
+
+
+
 
 Gproc is a process dictionary for Erlang, which provides a number of useful features beyond what the built-in dictionary has:
 
+
 * Use any term as a process alias
+
 * Register a process under several aliases
+
 * Non-unique properties can be registered simultaneously by many processes
+
 * QLC and match specification interface for efficient queries on the 
   dictionary
+
 * Await registration, let's you wait until a process registers itself
+
+* Atomically give away registered names and properties to another process
+
 * Counters, and aggregated counters, which automatically maintain the 
-  total of all counters with a given name.
-* Global registry, with all the above functions applied to a network of nodes.
+  total of all counters with a given name
+
+* Global registry, with all the above functions applied to a network of nodes
+
+
+
+
+
+An interesting application of gproc is building publish/subscribe patterns.
+Example:
+
+<pre>
+subscribe(EventType) -&gt;
+    %% Gproc notation: {p, l, Name} means {(p)roperty, (l)ocal, Name}
+    gproc:reg({p, l, {?MODULE, EventType}}).
+
+notify(EventType, Msg) -&gt;
+    Key = {?MODULE, EventType},
+    gproc:send({p, l, Key}, {self(), Key, Msg}).
+</pre>
+
+
+
+Gproc has a QuickCheck test suite, covering a fairly large part of the local 
+gproc functionality, although none of the global registry. It requires a 
+commercial EQC license, but rebar is smart enough to detect whether EQC is 
+available, and if it isn't, the code in gproc_eqc.erl will be "defined away".
+
+
+
+There is also an eunit suite, covering the basic operations for local and 
+global gproc.
+
+
+
+<h3><a name="Building_Edoc">Building Edoc</a></h3>
+
+
+
+
+By default, `./rebar doc` generates Github-flavored Markdown files.If you want to change this, remove the `edoc_opts` line from `rebar.config`.
+
+Gproc was first introduced at the ACM SIGPLAN Erlang Workshop in
+Freiburg 2007 ([Paper available here](http://github.com/esl/gproc/blob/master/doc/erlang07-wiger.pdf)).
+
+
+<h2 class="indextitle">Modules</h2>
 
-Gproc has a QuickCheck test suite, covering a fairly large part of the local gproc functionality, although none of the global registry. It requires a commercial EQC license, but rebar is smart enough to detect whether EQC is available, and if it isn't, the code in gproc_eqc.erl will be "defined away".
 
-There is also an eunit suite in gproc.erl, but it covers only some of the most basic functions (local only). Lots more tests need to be written... some day. Contributions are most welcome.
 
-Building Edoc
-=============
-By default, `./rebar doc` generates Github-flavored Markdown files.
-If you want to change this, remove this line from `rebar.config`.
+<table width="100%" border="0" summary="list of modules">
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc.md" class="module">gproc</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_app.md" class="module">gproc_app</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_dist.md" class="module">gproc_dist</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_init.md" class="module">gproc_init</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_lib.md" class="module">gproc_lib</a></td></tr>
+<tr><td><a href="http://github.com/esl/gproc/blob/master/doc/gproc_sup.md" class="module">gproc_sup</a></td></tr></table>
 
-`{edoc_opts, [{doclet, edown_doclet}]}.`

+ 51 - 18
doc/README.md

@@ -4,48 +4,81 @@
 
 The gproc application
 =====================
+Extended process dictionary.
+
+__Authors:__ Ulf Wiger ([`ulf.wiger@erlang-solutions.com`](mailto:ulf.wiger@erlang-solutions.com)), Joseph Wayne Norton ([`norton@geminimobile.com`](mailto:norton@geminimobile.com)).
+
 Extended process dictionary
 
 
-<h2>Introduction</h2>
 
-.
+<h3><a name="Introduction">Introduction</a></h3>
 
-__Authors:__ Ulf Wiger ([`ulf.wiger@erlang-consulting.com`](mailto:ulf.wiger@erlang-consulting.com)), Joseph Wayne Norton ([`norton@geminimobile.com`](mailto:norton@geminimobile.com)).Extended process dictionary
 
 
-<h2>Introduction</h2>
 
 
+Gproc is a process dictionary for Erlang, which provides a number of useful features beyond what the built-in dictionary has:
 
 
+* Use any term as a process alias
+
+* Register a process under several aliases
+
+* Non-unique properties can be registered simultaneously by many processes
+
+* QLC and match specification interface for efficient queries on the 
+  dictionary
+
+* Await registration, let's you wait until a process registers itself
+
+* Atomically give away registered names and properties to another process
+
+* Counters, and aggregated counters, which automatically maintain the 
+  total of all counters with a given name
+
+* Global registry, with all the above functions applied to a network of nodes
 
-Gproc was first introduced at the ACM SIGPLAN Erlang Workshop in
-Freiburg 2007 ([Paper available here](erlang07-wiger.pdf)).
 
 
 
-This application was designed to meet the following requirements:
 
+An interesting application of gproc is building publish/subscribe patterns.
+Example:
 
+<pre>
+subscribe(EventType) -&gt;
+    %% Gproc notation: {p, l, Name} means {(p)roperty, (l)ocal, Name}
+    gproc:reg({p, l, {?MODULE, EventType}}).
 
+notify(EventType, Msg) -&gt;
+    Key = {?MODULE, EventType},
+    gproc:send({p, l, Key}, {self(), Key, Msg}).
+</pre>
 
-<li>
-A process can register itself using any term.
-A process can register more than one name
-A process can publish non-unique {Key,Value} 'properties' 
-The registry must be efficiently searchable
-</li>
 
 
+Gproc has a QuickCheck test suite, covering a fairly large part of the local 
+gproc functionality, although none of the global registry. It requires a 
+commercial EQC license, but rebar is smart enough to detect whether EQC is 
+available, and if it isn't, the code in gproc_eqc.erl will be "defined away".
 
 
-As additional features, the registry was designed to allow global
-registration, and a special {Key,Value} property called a counter.
-It is also possible to create an 'aggregate counter', which will
-continuously reflect the sum of all counters with the same name.
 
-_In its current state, the global registration facility is brokenand should not be used. It will be migrated over to a new version of gen_leader. This work will be done with low priority unless peopleexpress a strong urge to use this functionality._
+There is also an eunit suite, covering the basic operations for local and 
+global gproc.
+
+
+
+<h3><a name="Building_Edoc">Building Edoc</a></h3>
+
+
+
+
+By default, `./rebar doc` generates Github-flavored Markdown files.If you want to change this, remove the `edoc_opts` line from `rebar.config`.
+
+Gproc was first introduced at the ACM SIGPLAN Erlang Workshop in
+Freiburg 2007 ([Paper available here](erlang07-wiger.pdf)).
 
 
 <h2 class="indextitle">Modules</h2>

+ 90 - 164
doc/gproc.md

@@ -129,7 +129,7 @@ a = aggregate_counter
 
 
 
-<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#add_global_aggr_counter-1">add_global_aggr_counter/1</a></td><td>Registers a global (unique) aggregated counter.</td></tr><tr><td valign="top"><a href="#add_global_counter-2">add_global_counter/2</a></td><td>Registers a global (non-unique) counter.</td></tr><tr><td valign="top"><a href="#add_global_name-1">add_global_name/1</a></td><td>Registers a global (unique) name.</td></tr><tr><td valign="top"><a href="#add_global_property-2">add_global_property/2</a></td><td>Registers a global (non-unique) property.</td></tr><tr><td valign="top"><a href="#add_local_aggr_counter-1">add_local_aggr_counter/1</a></td><td>Registers a local (unique) aggregated counter.</td></tr><tr><td valign="top"><a href="#add_local_counter-2">add_local_counter/2</a></td><td>Registers a local (non-unique) counter.</td></tr><tr><td valign="top"><a href="#add_local_name-1">add_local_name/1</a></td><td>Registers a local (unique) name.</td></tr><tr><td valign="top"><a href="#add_local_property-2">add_local_property/2</a></td><td>Registers a local (non-unique) property.</td></tr><tr><td valign="top"><a href="#audit_process-1">audit_process/1</a></td><td></td></tr><tr><td valign="top"><a href="#await-1">await/1</a></td><td>Equivalent to <a href="#await-2"><tt>await(Key, infinity)</tt></a>.</td></tr><tr><td valign="top"><a href="#await-2">await/2</a></td><td>Wait for a local name to be registered.</td></tr><tr><td valign="top"><a href="#cancel_wait-2">cancel_wait/2</a></td><td></td></tr><tr><td valign="top"><a href="#default-1">default/1</a></td><td></td></tr><tr><td valign="top"><a href="#first-1">first/1</a></td><td>Behaves as ets:first(Tab) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#get_value-1">get_value/1</a></td><td>Read the value stored with a key registered to the current process.</td></tr><tr><td valign="top"><a href="#info-1">info/1</a></td><td>Similar to <code>process_info(Pid)</code> but with additional gproc info.</td></tr><tr><td valign="top"><a href="#info-2">info/2</a></td><td>Similar to process_info(Pid, Item), but with additional gproc info.</td></tr><tr><td valign="top"><a href="#last-1">last/1</a></td><td>Behaves as ets:last(Tab) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#lookup_global_aggr_counter-1">lookup_global_aggr_counter/1</a></td><td>Lookup a global (unique) aggregated counter and returns its value.</td></tr><tr><td valign="top"><a href="#lookup_global_counters-1">lookup_global_counters/1</a></td><td>Look up all global (non-unique) instances of a given Counter.</td></tr><tr><td valign="top"><a href="#lookup_global_name-1">lookup_global_name/1</a></td><td>Lookup a global unique name.</td></tr><tr><td valign="top"><a href="#lookup_global_properties-1">lookup_global_properties/1</a></td><td>Look up all global (non-unique) instances of a given Property.</td></tr><tr><td valign="top"><a href="#lookup_local_aggr_counter-1">lookup_local_aggr_counter/1</a></td><td>Lookup a local (unique) aggregated counter and returns its value.</td></tr><tr><td valign="top"><a href="#lookup_local_counters-1">lookup_local_counters/1</a></td><td>Look up all local (non-unique) instances of a given Counter.</td></tr><tr><td valign="top"><a href="#lookup_local_name-1">lookup_local_name/1</a></td><td>Lookup a local unique name.</td></tr><tr><td valign="top"><a href="#lookup_local_properties-1">lookup_local_properties/1</a></td><td>Look up all local (non-unique) instances of a given Property.</td></tr><tr><td valign="top"><a href="#lookup_pid-1">lookup_pid/1</a></td><td>Lookup the Pid stored with a key.</td></tr><tr><td valign="top"><a href="#lookup_pids-1">lookup_pids/1</a></td><td>Returns a list of pids with the published key Key.</td></tr><tr><td valign="top"><a href="#lookup_value-1">lookup_value/1</a></td><td>Lookup the value stored with a key.</td></tr><tr><td valign="top"><a href="#lookup_values-1">lookup_values/1</a></td><td>Retrieve the <code>{Pid,Value}</code> pairs corresponding to Key.</td></tr><tr><td valign="top"><a href="#mreg-3">mreg/3</a></td><td>Register multiple {Key,Value} pairs of a given type and scope.</td></tr><tr><td valign="top"><a href="#nb_wait-1">nb_wait/1</a></td><td>Wait for a local name to be registered.</td></tr><tr><td valign="top"><a href="#next-2">next/2</a></td><td>Behaves as ets:next(Tab,Key) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#prev-2">prev/2</a></td><td>Behaves as ets:prev(Tab,Key) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#reg-1">reg/1</a></td><td>Equivalent to <a href="#reg-2"><tt>reg(Key, default(Key))</tt></a>.</td></tr><tr><td valign="top"><a href="#reg-2">reg/2</a></td><td>Register a name or property for the current process.</td></tr><tr><td valign="top"><a href="#select-1">select/1</a></td><td>Equivalent to <a href="#select-2"><tt>select(all, Pat)</tt></a>.</td></tr><tr><td valign="top"><a href="#select-2">select/2</a></td><td>Perform a select operation on the process registry.</td></tr><tr><td valign="top"><a href="#select-3">select/3</a></td><td>Like <a href="#select-2"><code>select/2</code></a> but returns Limit objects at a time.</td></tr><tr><td valign="top"><a href="#send-2">send/2</a></td><td>Sends a message to the process, or processes, corresponding to Key.</td></tr><tr><td valign="top"><a href="#set_value-2">set_value/2</a></td><td>Sets the value of the registeration entry given by Key.</td></tr><tr><td valign="top"><a href="#start_link-0">start_link/0</a></td><td>Starts the gproc server.</td></tr><tr><td valign="top"><a href="#surrender-2">surrender/2</a></td><td>Atomically transfers the key <code>From</code> to the process identified by <code>To</code>.</td></tr><tr><td valign="top"><a href="#table-1">table/1</a></td><td>Equivalent to <a href="#table-2"><tt>table(Context, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#table-2">table/2</a></td><td>QLC table generator for the gproc registry.</td></tr><tr><td valign="top"><a href="#unreg-1">unreg/1</a></td><td>Unregister a name or property.</td></tr><tr><td valign="top"><a href="#unregister_name-1">unregister_name/1</a></td><td>Equivalent to <tt>unreg / 1</tt>.</td></tr><tr><td valign="top"><a href="#update_counter-2">update_counter/2</a></td><td>Updates the counter registered as Key for the current process.</td></tr><tr><td valign="top"><a href="#where-1">where/1</a></td><td>Returns the pid registered as Key.</td></tr><tr><td valign="top"><a href="#whereis_name-1">whereis_name/1</a></td><td>Equivalent to <tt>where / 1</tt>.</td></tr></table>
+<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#add_global_aggr_counter-1">add_global_aggr_counter/1</a></td><td>Registers a global (unique) aggregated counter.</td></tr><tr><td valign="top"><a href="#add_global_counter-2">add_global_counter/2</a></td><td>Registers a global (non-unique) counter.</td></tr><tr><td valign="top"><a href="#add_global_name-1">add_global_name/1</a></td><td>Registers a global (unique) name.</td></tr><tr><td valign="top"><a href="#add_global_property-2">add_global_property/2</a></td><td>Registers a global (non-unique) property.</td></tr><tr><td valign="top"><a href="#add_local_aggr_counter-1">add_local_aggr_counter/1</a></td><td>Registers a local (unique) aggregated counter.</td></tr><tr><td valign="top"><a href="#add_local_counter-2">add_local_counter/2</a></td><td>Registers a local (non-unique) counter.</td></tr><tr><td valign="top"><a href="#add_local_name-1">add_local_name/1</a></td><td>Registers a local (unique) name.</td></tr><tr><td valign="top"><a href="#add_local_property-2">add_local_property/2</a></td><td>Registers a local (non-unique) property.</td></tr><tr><td valign="top"><a href="#audit_process-1">audit_process/1</a></td><td></td></tr><tr><td valign="top"><a href="#await-1">await/1</a></td><td>Equivalent to <a href="#await-2"><tt>await(Key, infinity)</tt></a>.</td></tr><tr><td valign="top"><a href="#await-2">await/2</a></td><td>Wait for a local name to be registered.</td></tr><tr><td valign="top"><a href="#cancel_wait-2">cancel_wait/2</a></td><td></td></tr><tr><td valign="top"><a href="#default-1">default/1</a></td><td></td></tr><tr><td valign="top"><a href="#first-1">first/1</a></td><td>Behaves as ets:first(Tab) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#get_value-1">get_value/1</a></td><td>Read the value stored with a key registered to the current process.</td></tr><tr><td valign="top"><a href="#give_away-2">give_away/2</a></td><td>Atomically transfers the key <code>From</code> to the process identified by <code>To</code>.</td></tr><tr><td valign="top"><a href="#info-1">info/1</a></td><td>Similar to <code>process_info(Pid)</code> but with additional gproc info.</td></tr><tr><td valign="top"><a href="#info-2">info/2</a></td><td>Similar to process_info(Pid, Item), but with additional gproc info.</td></tr><tr><td valign="top"><a href="#last-1">last/1</a></td><td>Behaves as ets:last(Tab) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#lookup_global_aggr_counter-1">lookup_global_aggr_counter/1</a></td><td>Lookup a global (unique) aggregated counter and returns its value.</td></tr><tr><td valign="top"><a href="#lookup_global_counters-1">lookup_global_counters/1</a></td><td>Look up all global (non-unique) instances of a given Counter.</td></tr><tr><td valign="top"><a href="#lookup_global_name-1">lookup_global_name/1</a></td><td>Lookup a global unique name.</td></tr><tr><td valign="top"><a href="#lookup_global_properties-1">lookup_global_properties/1</a></td><td>Look up all global (non-unique) instances of a given Property.</td></tr><tr><td valign="top"><a href="#lookup_local_aggr_counter-1">lookup_local_aggr_counter/1</a></td><td>Lookup a local (unique) aggregated counter and returns its value.</td></tr><tr><td valign="top"><a href="#lookup_local_counters-1">lookup_local_counters/1</a></td><td>Look up all local (non-unique) instances of a given Counter.</td></tr><tr><td valign="top"><a href="#lookup_local_name-1">lookup_local_name/1</a></td><td>Lookup a local unique name.</td></tr><tr><td valign="top"><a href="#lookup_local_properties-1">lookup_local_properties/1</a></td><td>Look up all local (non-unique) instances of a given Property.</td></tr><tr><td valign="top"><a href="#lookup_pid-1">lookup_pid/1</a></td><td>Lookup the Pid stored with a key.</td></tr><tr><td valign="top"><a href="#lookup_pids-1">lookup_pids/1</a></td><td>Returns a list of pids with the published key Key.</td></tr><tr><td valign="top"><a href="#lookup_value-1">lookup_value/1</a></td><td>Lookup the value stored with a key.</td></tr><tr><td valign="top"><a href="#lookup_values-1">lookup_values/1</a></td><td>Retrieve the <code>{Pid,Value}</code> pairs corresponding to Key.</td></tr><tr><td valign="top"><a href="#mreg-3">mreg/3</a></td><td>Register multiple {Key,Value} pairs of a given type and scope.</td></tr><tr><td valign="top"><a href="#nb_wait-1">nb_wait/1</a></td><td>Wait for a local name to be registered.</td></tr><tr><td valign="top"><a href="#next-2">next/2</a></td><td>Behaves as ets:next(Tab,Key) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#prev-2">prev/2</a></td><td>Behaves as ets:prev(Tab,Key) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#reg-1">reg/1</a></td><td>Equivalent to <a href="#reg-2"><tt>reg(Key, default(Key))</tt></a>.</td></tr><tr><td valign="top"><a href="#reg-2">reg/2</a></td><td>Register a name or property for the current process.</td></tr><tr><td valign="top"><a href="#select-1">select/1</a></td><td>Equivalent to <a href="#select-2"><tt>select(all, Pat)</tt></a>.</td></tr><tr><td valign="top"><a href="#select-2">select/2</a></td><td>Perform a select operation on the process registry.</td></tr><tr><td valign="top"><a href="#select-3">select/3</a></td><td>Like <a href="#select-2"><code>select/2</code></a> but returns Limit objects at a time.</td></tr><tr><td valign="top"><a href="#send-2">send/2</a></td><td>Sends a message to the process, or processes, corresponding to Key.</td></tr><tr><td valign="top"><a href="#set_value-2">set_value/2</a></td><td>Sets the value of the registeration entry given by Key.</td></tr><tr><td valign="top"><a href="#start_link-0">start_link/0</a></td><td>Starts the gproc server.</td></tr><tr><td valign="top"><a href="#table-1">table/1</a></td><td>Equivalent to <a href="#table-2"><tt>table(Context, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#table-2">table/2</a></td><td>QLC table generator for the gproc registry.</td></tr><tr><td valign="top"><a href="#unreg-1">unreg/1</a></td><td>Unregister a name or property.</td></tr><tr><td valign="top"><a href="#unregister_name-1">unregister_name/1</a></td><td>Equivalent to <tt>unreg / 1</tt>.</td></tr><tr><td valign="top"><a href="#update_counter-2">update_counter/2</a></td><td>Updates the counter registered as Key for the current process.</td></tr><tr><td valign="top"><a href="#where-1">where/1</a></td><td>Returns the pid registered as Key.</td></tr><tr><td valign="top"><a href="#whereis_name-1">whereis_name/1</a></td><td>Equivalent to <tt>where / 1</tt>.</td></tr></table>
 
 
 
@@ -145,7 +145,7 @@ a = aggregate_counter
 
 
 
-`add_global_aggr_counter(Name) -> any()`
+`add_global_aggr_counter(Name) -&gt; any()`
 
 
 
@@ -159,7 +159,7 @@ Registers a global (unique) aggregated counter.<a name="add_global_counter-2"></
 
 
 
-`add_global_counter(Name, Initial) -> any()`
+`add_global_counter(Name, Initial) -&gt; any()`
 
 
 
@@ -171,7 +171,7 @@ Registers a global (non-unique) counter. @equiv reg({c,g,Name},Value)<a name="ad
 
 
 
-`add_global_name(Name) -> any()`
+`add_global_name(Name) -&gt; any()`
 
 
 
@@ -183,7 +183,7 @@ Registers a global (unique) name. @equiv reg({n,g,Name})<a name="add_global_prop
 
 
 
-`add_global_property(Name, Value) -> any()`
+`add_global_property(Name, Value) -&gt; any()`
 
 
 
@@ -195,7 +195,7 @@ Registers a global (non-unique) property. @equiv reg({p,g,Name},Value)<a name="a
 
 
 
-`add_local_aggr_counter(Name) -> any()`
+`add_local_aggr_counter(Name) -&gt; any()`
 
 
 
@@ -209,7 +209,7 @@ Registers a local (unique) aggregated counter.<a name="add_local_counter-2"></a>
 
 
 
-`add_local_counter(Name, Initial) -> any()`
+`add_local_counter(Name, Initial) -&gt; any()`
 
 
 
@@ -221,7 +221,7 @@ Registers a local (non-unique) counter. @equiv reg({c,l,Name},Value)<a name="add
 
 
 
-`add_local_name(Name) -> any()`
+`add_local_name(Name) -&gt; any()`
 
 
 
@@ -233,7 +233,7 @@ Registers a local (unique) name. @equiv reg({n,l,Name})<a name="add_local_proper
 
 
 
-`add_local_property(Name, Value) -> any()`
+`add_local_property(Name, Value) -&gt; any()`
 
 
 
@@ -245,7 +245,9 @@ Registers a local (non-unique) property. @equiv reg({p,l,Name},Value)<a name="au
 
 
 
-`audit_process(Pid) -> any()`
+<pre>audit_process(Pid::pid()) -&gt; ok</pre>
+<br></br>
+
 
 <a name="await-1"></a>
 
@@ -255,9 +257,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>) -> {pid(), Value}</pre>
-
+<pre>await(Key::<a href="#type-key">key()</a>) -&gt; {pid(), Value}</pre>
 <br></br>
 
 
@@ -271,16 +271,14 @@ Equivalent to [`await(Key, infinity)`](#await-2).<a name="await-2"></a>
 
 
 
-
-<pre>await(Key::<a href="#type-key">key()</a>, Timeout) -> {pid(), Value}</pre>
-
+<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>
 
 
 
 Wait for a local name to be registered.
 The function raises an exception if the timeout expires. Timeout must be
-either an interger > 0 or 'infinity'.
+either an interger &gt; 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
@@ -292,7 +290,7 @@ registered (the difference: await/2 also returns the value).<a name="cancel_wait
 
 
 
-`cancel_wait(Key, Ref) -> any()`
+`cancel_wait(Key, Ref) -&gt; any()`
 
 <a name="default-1"></a>
 
@@ -302,7 +300,7 @@ registered (the difference: await/2 also returns the value).<a name="cancel_wait
 
 
 
-`default(X1) -> any()`
+`default(X1) -&gt; any()`
 
 <a name="first-1"></a>
 
@@ -312,9 +310,7 @@ registered (the difference: await/2 also returns the value).<a name="cancel_wait
 
 
 
-
-<pre>first(Type::<a href="#type-type">type()</a>) -> <a href="#type-key">key()</a> | '$end_of_table'</pre>
-
+<pre>first(Type::<a href="#type-type">type()</a>) -&gt; <a href="#type-key">key()</a> | '$end_of_table'</pre>
 <br></br>
 
 
@@ -333,9 +329,25 @@ The registry behaves as an ordered_set table.<a name="get_value-1"></a>
 
 
 
+<pre>get_value(Key) -&gt; Value</pre>
+<br></br>
+
+
+
+
+
+
+Read the value stored with a key registered to the current process.
+
+If no such key is registered to the current process, this function exits.<a name="give_away-2"></a>
+
+<h3>give_away/2</h3>
+
+
 
-<pre>get_value(Key) -> Value</pre>
 
+
+<pre>give_away(From::<a href="#type-key">key()</a>, To::pid() | <a href="#type-key">key()</a>) -&gt; undefined | pid()</pre>
 <br></br>
 
 
@@ -343,19 +355,35 @@ The registry behaves as an ordered_set table.<a name="get_value-1"></a>
 
 
 
-Read the value stored with a key registered to the current process.
+Atomically transfers the key `From` to the process identified by `To`.
 
-If no such key is registered to the current process, this function exits.<a name="info-1"></a>
 
-<h3>info/1</h3>
+
+This function transfers any gproc key (name, property, counter, aggr. counter)  
+from one process to another, and returns the pid of the new owner.
+
+
+
+`To` must be either a pid or a unique name (name or aggregated counter), but
+does not necessarily have to resolve to an existing process. If there is
+no process registered with the `To` key, `give_away/2` returns `undefined`,
+and the `From` key is effectively unregistered.
+
 
 
+It is allowed to give away a key to oneself, but of course, this operation  
+will have no effect.
+
+Fails with `badarg` if the calling process does not have a `From` key
+registered.<a name="info-1"></a>
+
+<h3>info/1</h3>
 
 
 
 
-<pre>info(Pid::pid()) -> ProcessInfo</pre>
 
+<pre>info(Pid::pid()) -&gt; ProcessInfo</pre>
 <ul class="definitions"><li><tt>ProcessInfo = [{gproc, [{Key, Value}]} | ProcessInfo]</tt></li></ul>
 
 
@@ -374,9 +402,7 @@ pairs registered to the process.<a name="info-2"></a>
 
 
 
-
-<pre>info(Pid::pid(), Item::atom()) -> {Item, Info}</pre>
-
+<pre>info(Pid::pid(), Item::atom()) -&gt; {Item, Info}</pre>
 <br></br>
 
 
@@ -396,9 +422,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>) -> <a href="#type-key">key()</a> | '$end_of_table'</pre>
-
+<pre>last(Context::<a href="#type-context">context()</a>) -&gt; <a href="#type-key">key()</a> | '$end_of_table'</pre>
 <br></br>
 
 
@@ -417,9 +441,7 @@ The registry behaves as an ordered_set table.<a name="lookup_global_aggr_counter
 
 
 
-
-<pre>lookup_global_aggr_counter(Name::any()) -> integer()</pre>
-
+<pre>lookup_global_aggr_counter(Name::any()) -&gt; integer()</pre>
 <br></br>
 
 
@@ -436,9 +458,7 @@ Fails if there is no such object.<a name="lookup_global_counters-1"></a>
 
 
 
-
-<pre>lookup_global_counters(Counter::any()) -> [{pid(), Value::integer()}]</pre>
-
+<pre>lookup_global_counters(Counter::any()) -&gt; [{pid(), Value::integer()}]</pre>
 <br></br>
 
 
@@ -455,9 +475,7 @@ Returns a list of {Pid, Value} tuples for all matching objects.<a name="lookup_g
 
 
 
-
-<pre>lookup_global_name(Name::any()) -> pid()</pre>
-
+<pre>lookup_global_name(Name::any()) -&gt; pid()</pre>
 <br></br>
 
 
@@ -473,9 +491,7 @@ Lookup a global unique name. Fails if there is no such name.<a name="lookup_glob
 
 
 
-
-<pre>lookup_global_properties(Property::any()) -> [{pid(), Value}]</pre>
-
+<pre>lookup_global_properties(Property::any()) -&gt; [{pid(), Value}]</pre>
 <br></br>
 
 
@@ -492,9 +508,7 @@ Returns a list of {Pid, Value} tuples for all matching objects.<a name="lookup_l
 
 
 
-
-<pre>lookup_local_aggr_counter(Name::any()) -> integer()</pre>
-
+<pre>lookup_local_aggr_counter(Name::any()) -&gt; integer()</pre>
 <br></br>
 
 
@@ -511,9 +525,7 @@ Fails if there is no such object.<a name="lookup_local_counters-1"></a>
 
 
 
-
-<pre>lookup_local_counters(Counter::any()) -> [{pid(), Value::integer()}]</pre>
-
+<pre>lookup_local_counters(Counter::any()) -&gt; [{pid(), Value::integer()}]</pre>
 <br></br>
 
 
@@ -530,9 +542,7 @@ Returns a list of {Pid, Value} tuples for all matching objects.<a name="lookup_l
 
 
 
-
-<pre>lookup_local_name(Name::any()) -> pid()</pre>
-
+<pre>lookup_local_name(Name::any()) -&gt; pid()</pre>
 <br></br>
 
 
@@ -548,9 +558,7 @@ Lookup a local unique name. Fails if there is no such name.<a name="lookup_local
 
 
 
-
-<pre>lookup_local_properties(Property::any()) -> [{pid(), Value}]</pre>
-
+<pre>lookup_local_properties(Property::any()) -&gt; [{pid(), Value}]</pre>
 <br></br>
 
 
@@ -567,9 +575,7 @@ Returns a list of {Pid, Value} tuples for all matching objects.<a name="lookup_p
 
 
 
-
-<pre>lookup_pid(Key) -> Pid</pre>
-
+<pre>lookup_pid(Key) -&gt; Pid</pre>
 <br></br>
 
 
@@ -584,9 +590,7 @@ Lookup the Pid stored with a key.
 
 
 
-
-<pre>lookup_pids(Key::<a href="#type-key">key()</a>) -> [pid()]</pre>
-
+<pre>lookup_pids(Key::<a href="#type-key">key()</a>) -&gt; [pid()]</pre>
 <br></br>
 
 
@@ -606,9 +610,7 @@ For non-unique types, the return value can be a list of any length.<a name="look
 
 
 
-
-<pre>lookup_value(Key) -> Value</pre>
-
+<pre>lookup_value(Key) -&gt; Value</pre>
 <br></br>
 
 
@@ -623,9 +625,7 @@ Lookup the value stored with a key.
 
 
 
-
-<pre>lookup_values(Key::<a href="#type-key">key()</a>) -> [{pid(), Value}]</pre>
-
+<pre>lookup_values(Key::<a href="#type-key">key()</a>) -&gt; [{pid(), Value}]</pre>
 <br></br>
 
 
@@ -645,9 +645,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()}]) -> true</pre>
-
+<pre>mreg(T::<a href="#type-type">type()</a>, X2::<a href="#type-scope">scope()</a>, KVL::[{Key::any(), Value::any()}]) -&gt; true</pre>
 <br></br>
 
 
@@ -665,9 +663,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>) -> Ref</pre>
-
+<pre>nb_wait(Key::<a href="#type-key">key()</a>) -&gt; Ref</pre>
 <br></br>
 
 
@@ -683,9 +679,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>) -> <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>) -&gt; <a href="#type-key">key()</a> | '$end_of_table'</pre>
 <br></br>
 
 
@@ -704,9 +698,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>) -> <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>) -&gt; <a href="#type-key">key()</a> | '$end_of_table'</pre>
 <br></br>
 
 
@@ -725,9 +717,7 @@ The registry behaves as an ordered_set table.<a name="reg-1"></a>
 
 
 
-
-<pre>reg(Key::<a href="#type-key">key()</a>) -> true</pre>
-
+<pre>reg(Key::<a href="#type-key">key()</a>) -&gt; true</pre>
 <br></br>
 
 
@@ -741,9 +731,7 @@ Equivalent to [`reg(Key, default(Key))`](#reg-2).<a name="reg-2"></a>
 
 
 
-
-<pre>reg(Key::<a href="#type-key">key()</a>, Value) -> true</pre>
-
+<pre>reg(Key::<a href="#type-key">key()</a>, Value) -&gt; true</pre>
 <br></br>
 
 
@@ -761,9 +749,7 @@ Register a name or property for the current process
 
 
 
-
-<pre>select(Pat::<a href="#type-select_pattern">select_pattern()</a>) -> list(<a href="#type-sel_object">sel_object()</a>)</pre>
-
+<pre>select(Pat::<a href="#type-select_pattern">select_pattern()</a>) -&gt; [<a href="#type-sel_object">sel_object()</a>]</pre>
 <br></br>
 
 
@@ -777,9 +763,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>) -> [{Key, Pid, Value}]</pre>
-
+<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>
 <br></br>
 
 
@@ -798,9 +782,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()) -> [{Key, Pid, Value}]</pre>
-
+<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>
 <br></br>
 
 
@@ -818,9 +800,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()) -> Msg</pre>
-
+<pre>send(Key::<a href="#type-key">key()</a>, Msg::any()) -&gt; Msg</pre>
 <br></br>
 
 
@@ -841,9 +821,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) -> true</pre>
-
+<pre>set_value(Key::<a href="#type-key">key()</a>, Value) -&gt; true</pre>
 <br></br>
 
 
@@ -867,9 +845,7 @@ it must be an integer.<a name="start_link-0"></a>
 
 
 
-
-<pre>start_link() -> {ok, pid()}</pre>
-
+<pre>start_link() -&gt; {ok, pid()}</pre>
 <br></br>
 
 
@@ -880,45 +856,7 @@ it must be an integer.<a name="start_link-0"></a>
 Starts the gproc server.
 
 This function is intended to be called from gproc_sup, as part of
-starting the gproc application.<a name="surrender-2"></a>
-
-<h3>surrender/2</h3>
-
-
-
-
-
-
-<pre>surrender(From::<a href="#type-key">key()</a>, To::pid() | <a href="#type-key">key()</a>) -> undefined | pid()</pre>
-
-<br></br>
-
-
-
-
-
-
-Atomically transfers the key `From` to the process identified by `To`.
-
-
-
-This function transfers any gproc key (name, property, counter, aggr. counter)  
-from one process to another, and returns the pid of the new owner.
-
-
-
-`To` must be either a pid or a unique name (name or aggregated counter), but
-does not necessarily have to resolve to an existing process. If there is
-no process registered with the `To` key, `surrender/2` returns `undefined`,
-and the `From` key is effectively unregistered.
-
-
-
-It is allowed to surrender a key to oneself, but of course, this operation  
-will have no effect.
-
-Fails with `badarg` if the calling process does not have a `From` key
-registered.<a name="table-1"></a>
+starting the gproc application.<a name="table-1"></a>
 
 <h3>table/1</h3>
 
@@ -926,9 +864,7 @@ registered.<a name="table-1"></a>
 
 
 
-
-<pre>table(Context::<a href="#type-context">context()</a>) -> any()</pre>
-
+<pre>table(Context::<a href="#type-context">context()</a>) -&gt; any()</pre>
 <br></br>
 
 
@@ -942,9 +878,7 @@ Equivalent to [`table(Context, [])`](#table-2).<a name="table-2"></a>
 
 
 
-
-<pre>table(Context::<a href="#type-context">context()</a>, Opts) -> any()</pre>
-
+<pre>table(Context::<a href="#type-context">context()</a>, Opts) -&gt; any()</pre>
 <br></br>
 
 
@@ -960,9 +894,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>) -> true</pre>
-
+<pre>unreg(Key::<a href="#type-key">key()</a>) -&gt; true</pre>
 <br></br>
 
 
@@ -976,7 +908,7 @@ Unregister a name or property.<a name="unregister_name-1"></a>
 
 
 
-`unregister_name(Key) -> any()`
+`unregister_name(Key) -&gt; any()`
 
 
 
@@ -988,9 +920,7 @@ Equivalent to `unreg / 1`.<a name="update_counter-2"></a>
 
 
 
-
-<pre>update_counter(Key::<a href="#type-key">key()</a>, Incr::integer()) -> integer()</pre>
-
+<pre>update_counter(Key::<a href="#type-key">key()</a>, Incr::integer()) -&gt; integer()</pre>
 <br></br>
 
 
@@ -1010,9 +940,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>) -> pid()</pre>
-
+<pre>where(Key::<a href="#type-key">key()</a>) -&gt; pid()</pre>
 <br></br>
 
 
@@ -1032,10 +960,8 @@ cases.<a name="whereis_name-1"></a>
 
 
 
-`whereis_name(Key) -> any()`
-
+`whereis_name(Key) -&gt; any()`
 
 
-Equivalent to `where / 1`.
 
-_Generated by EDoc, Mar 29 2011, 14:24:55._
+Equivalent to `where / 1`.

+ 3 - 6
doc/gproc_app.md

@@ -34,7 +34,7 @@ __Behaviours:__ [`application`](application.md).
 
 
 
-`start() -> any()`
+`start() -&gt; any()`
 
 <a name="start-2"></a>
 
@@ -44,7 +44,7 @@ __Behaviours:__ [`application`](application.md).
 
 
 
-`start(Type, StartArgs) -> any()`
+`start(Type, StartArgs) -&gt; any()`
 
 <a name="stop-1"></a>
 
@@ -54,8 +54,5 @@ __Behaviours:__ [`application`](application.md).
 
 
 
-`stop(State) -> any()`
+`stop(State) -&gt; any()`
 
-
-
-_Generated by EDoc, Mar 29 2011, 14:24:55._

+ 36 - 39
doc/gproc_dist.md

@@ -15,7 +15,7 @@ Extended process registry.
 
 __Behaviours:__ [`gen_leader`](/Users/uwiger/ETC/git/gproc/deps/gen_leader/doc/gen_leader.md).
 
-__Authors:__ Ulf Wiger ([`ulf.wiger@ericsson.com`](mailto:ulf.wiger@ericsson.com)).
+__Authors:__ Ulf Wiger ([`ulf.wiger@erlang-solutions.com`](mailto:ulf.wiger@erlang-solutions.com)).
 
 <h2><a name="description">Description</a></h2>
 
@@ -30,12 +30,12 @@ For a detailed description, see gproc/doc/erlang07-wiger.pdf.
 
 
 
-<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#code_change-4">code_change/4</a></td><td></td></tr><tr><td valign="top"><a href="#elected-2">elected/2</a></td><td></td></tr><tr><td valign="top"><a href="#elected-3">elected/3</a></td><td></td></tr><tr><td valign="top"><a href="#from_leader-3">from_leader/3</a></td><td></td></tr><tr><td valign="top"><a href="#handle_DOWN-3">handle_DOWN/3</a></td><td></td></tr><tr><td valign="top"><a href="#handle_call-4">handle_call/4</a></td><td></td></tr><tr><td valign="top"><a href="#handle_cast-3">handle_cast/3</a></td><td></td></tr><tr><td valign="top"><a href="#handle_info-2">handle_info/2</a></td><td></td></tr><tr><td valign="top"><a href="#handle_leader_call-4">handle_leader_call/4</a></td><td></td></tr><tr><td valign="top"><a href="#handle_leader_cast-3">handle_leader_cast/3</a></td><td></td></tr><tr><td valign="top"><a href="#init-1">init/1</a></td><td></td></tr><tr><td valign="top"><a href="#leader_call-1">leader_call/1</a></td><td></td></tr><tr><td valign="top"><a href="#leader_cast-1">leader_cast/1</a></td><td></td></tr><tr><td valign="top"><a href="#mreg-2">mreg/2</a></td><td></td></tr><tr><td valign="top"><a href="#reg-1">reg/1</a></td><td></td></tr><tr><td valign="top"><a href="#reg-2">reg/2</a></td><td>
+<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#code_change-4">code_change/4</a></td><td></td></tr><tr><td valign="top"><a href="#elected-2">elected/2</a></td><td></td></tr><tr><td valign="top"><a href="#elected-3">elected/3</a></td><td></td></tr><tr><td valign="top"><a href="#from_leader-3">from_leader/3</a></td><td></td></tr><tr><td valign="top"><a href="#give_away-2">give_away/2</a></td><td></td></tr><tr><td valign="top"><a href="#handle_DOWN-3">handle_DOWN/3</a></td><td></td></tr><tr><td valign="top"><a href="#handle_call-4">handle_call/4</a></td><td></td></tr><tr><td valign="top"><a href="#handle_cast-3">handle_cast/3</a></td><td></td></tr><tr><td valign="top"><a href="#handle_info-2">handle_info/2</a></td><td></td></tr><tr><td valign="top"><a href="#handle_leader_call-4">handle_leader_call/4</a></td><td></td></tr><tr><td valign="top"><a href="#handle_leader_cast-3">handle_leader_cast/3</a></td><td></td></tr><tr><td valign="top"><a href="#init-1">init/1</a></td><td></td></tr><tr><td valign="top"><a href="#leader_call-1">leader_call/1</a></td><td></td></tr><tr><td valign="top"><a href="#leader_cast-1">leader_cast/1</a></td><td></td></tr><tr><td valign="top"><a href="#mreg-2">mreg/2</a></td><td></td></tr><tr><td valign="top"><a href="#reg-1">reg/1</a></td><td></td></tr><tr><td valign="top"><a href="#reg-2">reg/2</a></td><td>
 Class = n  - unique name
 | p  - non-unique property
 | c  - counter
 | a  - aggregated counter
-Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_value-2">set_value/2</a></td><td></td></tr><tr><td valign="top"><a href="#start_link-0">start_link/0</a></td><td></td></tr><tr><td valign="top"><a href="#start_link-1">start_link/1</a></td><td></td></tr><tr><td valign="top"><a href="#surrender-2">surrender/2</a></td><td></td></tr><tr><td valign="top"><a href="#surrendered-3">surrendered/3</a></td><td></td></tr><tr><td valign="top"><a href="#terminate-2">terminate/2</a></td><td></td></tr><tr><td valign="top"><a href="#unreg-1">unreg/1</a></td><td></td></tr><tr><td valign="top"><a href="#update_counter-2">update_counter/2</a></td><td></td></tr></table>
+Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_value-2">set_value/2</a></td><td></td></tr><tr><td valign="top"><a href="#start_link-0">start_link/0</a></td><td></td></tr><tr><td valign="top"><a href="#start_link-1">start_link/1</a></td><td></td></tr><tr><td valign="top"><a href="#surrendered-3">surrendered/3</a></td><td></td></tr><tr><td valign="top"><a href="#terminate-2">terminate/2</a></td><td></td></tr><tr><td valign="top"><a href="#unreg-1">unreg/1</a></td><td></td></tr><tr><td valign="top"><a href="#update_counter-2">update_counter/2</a></td><td></td></tr></table>
 
 
 
@@ -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) -> any()`
+`code_change(FromVsn, S, Extra, E) -&gt; 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) -> any()`
+`elected(S, E) -&gt; 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) -> any()`
+`elected(S, E, Node) -&gt; any()`
 
 <a name="from_leader-3"></a>
 
@@ -81,7 +81,17 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`from_leader(Ops, S, E) -> any()`
+`from_leader(Ops, S, E) -&gt; any()`
+
+<a name="give_away-2"></a>
+
+<h3>give_away/2</h3>
+
+
+
+
+
+`give_away(Key, To) -&gt; any()`
 
 <a name="handle_DOWN-3"></a>
 
@@ -91,7 +101,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`handle_DOWN(Node, S, E) -> any()`
+`handle_DOWN(Node, S, E) -&gt; any()`
 
 <a name="handle_call-4"></a>
 
@@ -101,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) -> any()`
+`handle_call(X1, X2, S, X4) -&gt; any()`
 
 <a name="handle_cast-3"></a>
 
@@ -111,7 +121,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`handle_cast(Msg, S, X3) -> any()`
+`handle_cast(Msg, S, X3) -&gt; any()`
 
 <a name="handle_info-2"></a>
 
@@ -121,7 +131,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`handle_info(X1, S) -> any()`
+`handle_info(X1, S) -&gt; any()`
 
 <a name="handle_leader_call-4"></a>
 
@@ -131,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) -> any()`
+`handle_leader_call(X1, From, S, E) -&gt; any()`
 
 <a name="handle_leader_cast-3"></a>
 
@@ -141,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) -> any()`
+`handle_leader_cast(X1, S, E) -&gt; any()`
 
 <a name="init-1"></a>
 
@@ -151,7 +161,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`init(Opts) -> any()`
+`init(Opts) -&gt; any()`
 
 <a name="leader_call-1"></a>
 
@@ -161,7 +171,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`leader_call(Req) -> any()`
+`leader_call(Req) -&gt; any()`
 
 <a name="leader_cast-1"></a>
 
@@ -171,7 +181,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`leader_cast(Msg) -> any()`
+`leader_cast(Msg) -&gt; any()`
 
 <a name="mreg-2"></a>
 
@@ -181,7 +191,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`mreg(T, KVL) -> any()`
+`mreg(T, KVL) -&gt; any()`
 
 <a name="reg-1"></a>
 
@@ -191,7 +201,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`reg(Key) -> any()`
+`reg(Key) -&gt; any()`
 
 <a name="reg-2"></a>
 
@@ -201,7 +211,7 @@ Scope = l | g (global or local).</td></tr><tr><td valign="top"><a href="#set_val
 
 
 
-`reg(Key, Value) -> any()`
+`reg(Key, Value) -&gt; any()`
 
 
 
@@ -219,7 +229,7 @@ Scope = l | g (global or local)
 
 
 
-`set_value(Key, Value) -> any()`
+`set_value(Key, Value) -&gt; any()`
 
 <a name="start_link-0"></a>
 
@@ -229,7 +239,7 @@ Scope = l | g (global or local)
 
 
 
-`start_link() -> any()`
+`start_link() -&gt; any()`
 
 <a name="start_link-1"></a>
 
@@ -239,17 +249,7 @@ Scope = l | g (global or local)
 
 
 
-`start_link(Nodes) -> any()`
-
-<a name="surrender-2"></a>
-
-<h3>surrender/2</h3>
-
-
-
-
-
-`surrender(Key, To) -> any()`
+`start_link(Nodes) -&gt; any()`
 
 <a name="surrendered-3"></a>
 
@@ -259,7 +259,7 @@ Scope = l | g (global or local)
 
 
 
-`surrendered(S, X2, E) -> any()`
+`surrendered(S, X2, E) -&gt; any()`
 
 <a name="terminate-2"></a>
 
@@ -269,7 +269,7 @@ Scope = l | g (global or local)
 
 
 
-`terminate(Reason, S) -> any()`
+`terminate(Reason, S) -&gt; any()`
 
 <a name="unreg-1"></a>
 
@@ -279,7 +279,7 @@ Scope = l | g (global or local)
 
 
 
-`unreg(Key) -> any()`
+`unreg(Key) -&gt; any()`
 
 <a name="update_counter-2"></a>
 
@@ -289,8 +289,5 @@ Scope = l | g (global or local)
 
 
 
-`update_counter(Key, Incr) -> any()`
-
-
+`update_counter(Key, Incr) -&gt; any()`
 
-_Generated by EDoc, Mar 29 2011, 14:24:55._

+ 5 - 4
doc/gproc_init.md

@@ -32,7 +32,9 @@ Module gproc_init
 
 
 
-`hard_reset() -> any()`
+<pre>hard_reset() -&gt; ok</pre>
+<br></br>
+
 
 <a name="soft_reset-0"></a>
 
@@ -42,8 +44,7 @@ Module gproc_init
 
 
 
-`soft_reset() -> any()`
-
+<pre>soft_reset() -&gt; ok</pre>
+<br></br>
 
 
-_Generated by EDoc, Mar 29 2011, 14:24:55._

+ 1 - 3
doc/gproc_lib.md

@@ -20,6 +20,4 @@ __Authors:__ Ulf Wiger ([`ulf.wiger@ericsson.com`](mailto:ulf.wiger@ericsson.com
 This module implements an extended process registry
 
 
-For a detailed description, see gproc/doc/erlang07-wiger.pdf.
-
-_Generated by EDoc, Mar 29 2011, 14:24:55._
+For a detailed description, see gproc/doc/erlang07-wiger.pdf.

+ 2 - 5
doc/gproc_sup.md

@@ -34,7 +34,7 @@ __Behaviours:__ [`supervisor`](supervisor.md).
 
 
 
-`init(Args) -> any()`
+`init(Args) -&gt; any()`
 
 
 
@@ -46,8 +46,5 @@ The main GPROC supervisor.<a name="start_link-1"></a>
 
 
 
-`start_link(Args) -> any()`
+`start_link(Args) -&gt; any()`
 
-
-
-_Generated by EDoc, Mar 29 2011, 14:24:55._

+ 41 - 19
doc/overview.edoc

@@ -1,29 +1,51 @@
-@author Ulf Wiger <ulf.wiger@erlang-consulting.com>
+@author Ulf Wiger <ulf.wiger@erlang-solutions.com>
 @author Joseph Wayne Norton <norton@geminimobile.com>
 
 @doc Extended process dictionary
-<h2>Introduction</h2>
 
-Gproc was first introduced at the ACM SIGPLAN Erlang Workshop in
-Freiburg 2007 (<a href="erlang07-wiger.pdf">Paper available here</a>).
+== Introduction ==
+
+Gproc is a process dictionary for Erlang, which provides a number of useful features beyond what the built-in dictionary has:
+
+<ul>
+<li>Use any term as a process alias</li>
+<li>Register a process under several aliases</li>
+<li>Non-unique properties can be registered simultaneously by many processes</li>
+<li>QLC and match specification interface for efficient queries on the 
+  dictionary</li>
+<li>Await registration, let's you wait until a process registers itself</li>
+<li>Atomically give away registered names and properties to another process</li>
+<li>Counters, and aggregated counters, which automatically maintain the 
+  total of all counters with a given name</li>
+<li>Global registry, with all the above functions applied to a network of nodes</li>
+</ul>
+
+An interesting application of gproc is building publish/subscribe patterns.
+Example:
 
-This application was designed to meet the following requirements:
+<pre>
+subscribe(EventType) ->
+    %% Gproc notation: {p, l, Name} means {(p)roperty, (l)ocal, Name}
+    gproc:reg({p, l, {?MODULE, EventType}}).
 
-<li>
-  <ul>A process can register itself using any term.</ul>
-  <ul>A process can register more than one name</ul>
-  <ul>A process can publish non-unique {Key,Value} 'properties' </ul>
-  <ul>The registry must be efficiently searchable</ul>
-</li>
+notify(EventType, Msg) ->
+    Key = {?MODULE, EventType},
+    gproc:send({p, l, Key}, {self(), Key, Msg}).
+</pre>
 
-As additional features, the registry was designed to allow global
-registration, and a special {Key,Value} property called a counter.
-It is also possible to create an 'aggregate counter', which will
-continuously reflect the sum of all counters with the same name.
+Gproc has a QuickCheck test suite, covering a fairly large part of the local 
+gproc functionality, although none of the global registry. It requires a 
+commercial EQC license, but rebar is smart enough to detect whether EQC is 
+available, and if it isn't, the code in gproc_eqc.erl will be "defined away".
 
-<em>In its current state, the global registration facility is broken
-and should not be used. It will be migrated over to a new version of 
-gen_leader. This work will be done with low priority unless people
-express a strong urge to use this functionality.</em>
+There is also an eunit suite, covering the basic operations for local and 
+global gproc.
+
+== Building Edoc ==
+By default, `./rebar doc` generates Github-flavored Markdown files.
+If you want to change this, remove the `edoc_opts' line from `rebar.config'.
+
+Gproc was first introduced at the ACM SIGPLAN Erlang Workshop in
+Freiburg 2007 (<a href="erlang07-wiger.pdf">Paper available here</a>).
 
 @end

+ 4 - 1
rebar.config

@@ -5,4 +5,7 @@
 	{gen_leader, ".*",
 	 {git, "git://github.com/abecciu/gen_leader_revival.git", "HEAD"}}
        ]}.
-{edoc_opts, [{doclet, edown_doclet}]}.
+{edoc_opts, [{doclet, edown_doclet},
+	     {top_level_readme,
+	      {"./README.md",
+	       "http://github.com/esl/gproc"}}]}.

+ 149 - 147
src/gproc_dist.erl

@@ -13,7 +13,7 @@
 %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
 %% AB. All Rights Reserved.''
 %%
-%% @author Ulf Wiger <ulf.wiger@ericsson.com>
+%% @author Ulf Wiger <ulf.wiger@erlang-solutions.com>
 %% 
 %% @doc Extended process registry
 %% <p>This module implements an extended process registry</p>
@@ -48,11 +48,6 @@
 
 -include("gproc.hrl").
 
--ifdef(TEST).
--include_lib("eunit/include/eunit.hrl").
--export([t_spawn/1, t_spawn_reg/2]).
--endif.
-
 -define(SERVER, ?MODULE).
 
 -record(state, {
@@ -74,6 +69,8 @@ start_link({Nodes, Opts}) ->
 %%       ?SERVER, Nodes, [],?MODULE, [], [{debug,[trace]}]).
 
 
+%% {@see gproc:reg/1}
+%%
 reg(Key) ->
     reg(Key, gproc:default(Key)).
 
@@ -474,148 +471,153 @@ pid_to_give_away_to({T,g,_} = Key) when T==n; T==a ->
             undefined
     end.
 
--ifdef(TEST).
-
-dist_test_() ->
-    {foreach,
-     fun() ->
-	     Ns = start_slaves([n1, n2, n3]),
-	     %% dbg:tracer(),
-	     %% [dbg:n(N) || N <- Ns],
-	     %% dbg:tpl(gproc_dist, x),
-	     %% dbg:p(all,[c]),
-	     Ns
-     end,
-     fun(Ns) ->
-	     ?debugVal([rpc:call(N, init, stop, []) || N <- Ns])
-     end,
-     [
-      {with, [fun t_simple_reg/1,
-	      fun t_await_reg/1,
-	      fun t_give_away/1]}
-     ]}.
-
-t_simple_reg([H|_] = Ns) ->
-    ?debugMsg(t_simple_reg),
-    Name = {n, g, foo},
-    P = t_spawn_reg(H, Name),
-    ?assertMatch(ok, t_lookup_everywhere(Name, Ns, P)),
-    ?assertMatch(true, t_call(P, {apply, gproc, unreg, [Name]})),
-    ?assertMatch(ok, t_lookup_everywhere(Name, Ns, undefined)),
-    ?assertMatch(ok, t_call(P, die)).
-
-t_await_reg([A,B|_]) ->
-    ?debugMsg(t_await_reg),
-    Name = {n, g, foo},
-    P = t_spawn(A),
-    P ! {self(), {apply, gproc, await, [Name]}},
-    P1 = t_spawn_reg(B, Name),
-    ?assert(P1 == receive
-		      {P, Res} ->
-			  element(1, Res)
-		  end),
-    ?assertMatch(ok, t_call(P, die)),
-    ?assertMatch(ok, t_call(P1, die)).
-
-t_give_away([A,B|_] = Ns) ->
-    ?debugMsg(t_give_away),
-    Na = {n, g, a},
-    Nb = {n, g, b},
-    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)),
-    ?debugHere,
-    ?assertMatch(Pb, ?debugVal(
-			t_call(Pa, {apply, {gproc, give_away, [Na, Nb]}}))),
-    ?assertMatch(ok, t_lookup_everywhere(Na, Ns, Pb)),
-    ?debugHere,
-    ?assertMatch(Pa, t_call(Pa, {apply, {gproc, give_away, [Na, Pa]}})),
-    ?assertMatch(ok, t_lookup_everywhere(Na, Ns, Pa)),
-    ?debugHere,
-    ?assertMatch(ok, t_call(Pa, die)),
-    ?assertMatch(ok, t_call(Pb, die)).
+%% -ifdef(TEST).
+
+%% dist_test_() ->
+%%     {timeout, 60,
+%%      [{foreach,
+%%        fun() ->
+%% 	       Ns = start_slaves([n1, n2]),
+%% 	       %% dbg:tracer(),
+%% 	       %% [dbg:n(N) || N <- Ns],
+%% 	       %% dbg:tpl(gproc_dist, x),
+%% 	       %% dbg:p(all,[c]),
+%% 	       Ns
+%%        end,
+%%        fun(Ns) ->
+%% 	       [rpc:call(N, init, stop, []) || N <- Ns]
+%%        end,
+%%        [
+%% 	{with, [fun(Ns) -> {in_parallel, [fun(X) -> t_simple_reg(X) end,
+%% 					  fun(X) -> t_await_reg(X) end,
+%% 					  fun(X) -> t_give_away(X) end]
+%% 			   }
+%% 		end]}
+%%        ]}
+%%      ]}.
+
+%% -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)),
+%%     ?assertMatch(true, t_call(P, {apply, gproc, unreg, [Name]})),
+%%     ?assertMatch(ok, t_lookup_everywhere(Name, Ns, undefined)),
+%%     ?assertMatch(ok, t_call(P, die)).
+
+%% t_await_reg([A,B|_]) ->
+%%     ?debugMsg(t_await_reg),
+%%     Name = ?T_NAME,
+%%     P = t_spawn(A),
+%%     P ! {self(), {apply, gproc, await, [Name]}},
+%%     P1 = t_spawn_reg(B, Name),
+%%     ?assert(P1 == receive
+%% 		      {P, Res} ->
+%% 			  element(1, Res)
+%% 		  end),
+%%     ?assertMatch(ok, t_call(P, die)),
+%%     ?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)),
+%%     %% ?debugHere,
+%%     ?assertMatch(Pb, t_call(Pa, {apply, {gproc, give_away, [Na, Nb]}})),
+%%     ?assertMatch(ok, t_lookup_everywhere(Na, Ns, Pb)),
+%%     %% ?debugHere,
+%%     ?assertMatch(Pa, t_call(Pa, {apply, {gproc, give_away, [Na, Pa]}})),
+%%     ?assertMatch(ok, t_lookup_everywhere(Na, Ns, Pa)),
+%%     %% ?debugHere,
+%%     ?assertMatch(ok, t_call(Pa, die)),
+%%     ?assertMatch(ok, t_call(Pb, die)).
     
-t_sleep() ->
-    timer:sleep(1000).
-
-t_lookup_everywhere(Key, Nodes, Exp) ->
-    t_lookup_everywhere(Key, Nodes, Exp, 3).
-
-t_lookup_everywhere(Key, _, Exp, 0) ->
-    {lookup_failed, Key, Exp};
-t_lookup_everywhere(Key, Nodes, Exp, I) ->
-    Expected = [{N, Exp} || N <- Nodes],
-    Found = [{N,rpc:call(N, gproc, where, [Key])} || N <- Nodes],
-    if Expected =/= Found ->
-	    ?debugFmt("lookup ~p failed (~p), retrying...~n", [Key, Found]),
-	    t_sleep(),
-	    t_lookup_everywhere(Key, Nodes, Exp, I-1);
-       true ->
-	    ok
-    end.
+%% t_sleep() ->
+%%     timer:sleep(1000).
+
+%% t_lookup_everywhere(Key, Nodes, Exp) ->
+%%     t_lookup_everywhere(Key, Nodes, Exp, 3).
+
+%% t_lookup_everywhere(Key, _, Exp, 0) ->
+%%     {lookup_failed, Key, Exp};
+%% t_lookup_everywhere(Key, Nodes, Exp, I) ->
+%%     Expected = [{N, Exp} || N <- Nodes],
+%%     Found = [{N,rpc:call(N, gproc, where, [Key])} || N <- Nodes],
+%%     if Expected =/= Found ->
+%% 	    ?debugFmt("lookup ~p failed (~p), retrying...~n", [Key, Found]),
+%% 	    t_sleep(),
+%% 	    t_lookup_everywhere(Key, Nodes, Exp, I-1);
+%%        true ->
+%% 	    ok
+%%     end.
 				  
 
-t_spawn(Node) ->
-    Me = self(),
-    P = spawn(Node, fun() ->
-			    Me ! {self(), ok},
-			    t_loop()
-		    end),
-    receive
-	{P, ok} -> P
-    end.
-
-t_spawn_reg(Node, Name) ->
-    Me = self(),
-    spawn(Node, fun() ->
-			?assertMatch(true, gproc:reg(Name)),
-			Me ! {self(), ok},
-			t_loop()
-		end),
-    receive
-	{P, ok} -> P
-    end.
-
-t_call(P, Req) ->
-    P ! {self(), Req},
-    receive
-	{P, Res} ->
-	    Res
-    end.
-
-t_loop() ->
-    receive
-	{From, die} ->
-	    From ! {self(), ok};
-	{From, {apply, M, F, A}} ->
-	    From ! {self(), apply(M, F, A)},
-	    t_loop()
-    end.
-
-start_slaves(Ns) ->
-    [H|T] = Nodes = [start_slave(N) || N <- Ns],
-    ?debugVal([pong = rpc:call(H, net, ping, [N]) || N <- T]),
-    ?debugVal(rpc:multicall(Nodes, application, start, [gproc])),
-    Nodes.
+%% t_spawn(Node) ->
+%%     Me = self(),
+%%     P = spawn(Node, fun() ->
+%% 			    Me ! {self(), ok},
+%% 			    t_loop()
+%% 		    end),
+%%     receive
+%% 	{P, ok} -> P
+%%     end.
+
+%% t_spawn_reg(Node, Name) ->
+%%     Me = self(),
+%%     spawn(Node, fun() ->
+%% 			?assertMatch(true, gproc:reg(Name)),
+%% 			Me ! {self(), ok},
+%% 			t_loop()
+%% 		end),
+%%     receive
+%% 	{P, ok} -> P
+%%     end.
+
+%% t_call(P, Req) ->
+%%     P ! {self(), Req},
+%%     receive
+%% 	{P, Res} ->
+%% 	    Res
+%%     end.
+
+%% t_loop() ->
+%%     receive
+%% 	{From, die} ->
+%% 	    From ! {self(), ok};
+%% 	{From, {apply, M, F, A}} ->
+%% 	    From ! {self(), apply(M, F, A)},
+%% 	    t_loop()
+%%     end.
+
+%% start_slaves(Ns) ->
+%%     [H|T] = Nodes = [start_slave(N) || N <- Ns],
+%%     %% ?debugVal([pong = rpc:call(H, net, ping, [N]) || N <- T]),
+%%     %% ?debugVal(rpc:multicall(Nodes, application, start, [gproc])),
+%%     Nodes.
 	       
-start_slave(Name) ->
-    case node() of
-        nonode@nohost ->
-            os:cmd("epmd -daemon"),
-            {ok, _} = net_kernel:start([gproc_master, shortnames]);
-        _ ->
-            ok
-    end,
-    {ok, Node} = slave:start(
-		   host(), Name,
-		   "-pa . -pz ../ebin -pa ../deps/gen_leader/ebin "
-		   "-gproc gproc_dist all"),
-    io:fwrite(user, "Slave node: ~p~n", [Node]),
-    Node.
-
-host() ->
-    [Name, Host] = re:split(atom_to_list(node()), "@", [{return, list}]),
-    list_to_atom(Host).
-
--endif.
+%% start_slave(Name) ->
+%%     case node() of
+%%         nonode@nohost ->
+%%             os:cmd("epmd -daemon"),
+%%             {ok, _} = net_kernel:start([gproc_master, shortnames]);
+%%         _ ->
+%%             ok
+%%     end,
+%%     {ok, Node} = slave:start(
+%% 		   host(), Name,
+%% 		   "-pa . -pz ../ebin -pa ../deps/gen_leader/ebin "
+%% 		   "-gproc gproc_dist all"),
+%%     %% io:fwrite(user, "Slave node: ~p~n", [Node]),
+%%     Node.
+
+%% host() ->
+%%     [Name, Host] = re:split(atom_to_list(node()), "@", [{return, list}]),
+%%     list_to_atom(Host).
+
+%% -endif.

+ 7 - 9
src/gproc_init.erl

@@ -17,17 +17,11 @@
 %% API
 %%====================================================================
 
-%% types
-
+%%--------------------------------------------------------------------
 %% soft_reset
--spec soft_reset() -> ok.
-
-%% hard_reset
--spec hard_reset() -> ok.
 
-
-%%--------------------------------------------------------------------
 %% soft_reset
+-spec soft_reset() -> ok.
 
 soft_reset() ->
     ok = hard_reset(), %% soft reset isn't enough
@@ -36,9 +30,13 @@ soft_reset() ->
 %%--------------------------------------------------------------------
 %% hard_reset
 
+%% hard_reset
+-spec hard_reset() -> ok.
+
 hard_reset() ->
     %% exit normal {n,'_','_'}
-    [ exit(Pid,normal) || Pid <- gproc:lookup_pids({n,'_','_'}), is_process_alive(Pid) ],
+    [ exit(Pid,normal) || Pid <- gproc:lookup_pids({n,'_','_'}),
+			  (node(Pid) =/= node()) orelse is_process_alive(Pid) ],
     %% kill via supervisor
     ok = supervisor:terminate_child(gproc_sup, gproc),
     %% delete ets table

+ 173 - 0
test/gproc_dist_tests.erl

@@ -0,0 +1,173 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%% 
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%% 
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% @author Ulf Wiger <ulf.wiger@erlang-solutions.com>
+%% 
+-module(gproc_dist_tests).
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+-export([t_spawn/1, t_spawn_reg/2]).
+
+dist_test_() ->
+    {timeout, 60,
+     [{foreach,
+       fun() ->
+	       Ns = start_slaves([n1, n2]),
+	       %% dbg:tracer(),
+	       %% [dbg:n(N) || N <- Ns],
+	       %% dbg:tpl(gproc_dist, x),
+	       %% dbg:p(all,[c]),
+	       Ns
+       end,
+       fun(Ns) ->
+	       [rpc:call(N, init, stop, []) || N <- Ns]
+       end,
+       [
+	{with, [fun(_) -> {in_parallel, [fun(X) ->
+						 ?debugVal(t_simple_reg(X)) end,
+					 fun(X) ->
+						 ?debugVal(t_await_reg(X))
+					 end,
+					 fun(X) ->
+						 ?debugVal(t_give_away(X))
+					 end]
+			  }
+		end]}
+       ]}
+     ]}.
+
+-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)),
+    ?assertMatch(true, t_call(P, {apply, gproc, unreg, [Name]})),
+    ?assertMatch(ok, t_lookup_everywhere(Name, Ns, undefined)),
+    ?assertMatch(ok, t_call(P, die)).
+
+t_await_reg([A,B|_]) ->
+    ?debugMsg(t_await_reg),
+    Name = ?T_NAME,
+    P = t_spawn(A),
+    P ! {self(), {apply, gproc, await, [Name]}},
+    P1 = t_spawn_reg(B, Name),
+    ?assert(P1 == receive
+		      {P, Res} ->
+			  element(1, Res)
+		  end),
+    ?assertMatch(ok, t_call(P, die)),
+    ?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)),
+    %% ?debugHere,
+    ?assertMatch(Pb, t_call(Pa, {apply, {gproc, give_away, [Na, Nb]}})),
+    ?assertMatch(ok, t_lookup_everywhere(Na, Ns, Pb)),
+    %% ?debugHere,
+    ?assertMatch(Pa, t_call(Pa, {apply, {gproc, give_away, [Na, Pa]}})),
+    ?assertMatch(ok, t_lookup_everywhere(Na, Ns, Pa)),
+    %% ?debugHere,
+    ?assertMatch(ok, t_call(Pa, die)),
+    ?assertMatch(ok, t_call(Pb, die)).
+    
+t_sleep() ->
+    timer:sleep(1000).
+
+t_lookup_everywhere(Key, Nodes, Exp) ->
+    t_lookup_everywhere(Key, Nodes, Exp, 3).
+
+t_lookup_everywhere(Key, _, Exp, 0) ->
+    {lookup_failed, Key, Exp};
+t_lookup_everywhere(Key, Nodes, Exp, I) ->
+    Expected = [{N, Exp} || N <- Nodes],
+    Found = [{N,rpc:call(N, gproc, where, [Key])} || N <- Nodes],
+    if Expected =/= Found ->
+	    ?debugFmt("lookup ~p failed (~p), retrying...~n", [Key, Found]),
+	    t_sleep(),
+	    t_lookup_everywhere(Key, Nodes, Exp, I-1);
+       true ->
+	    ok
+    end.
+				  
+
+t_spawn(Node) ->
+    Me = self(),
+    P = spawn(Node, fun() ->
+			    Me ! {self(), ok},
+			    t_loop()
+		    end),
+    receive
+	{P, ok} -> P
+    end.
+
+t_spawn_reg(Node, Name) ->
+    Me = self(),
+    spawn(Node, fun() ->
+			?assertMatch(true, gproc:reg(Name)),
+			Me ! {self(), ok},
+			t_loop()
+		end),
+    receive
+	{P, ok} -> P
+    end.
+
+t_call(P, Req) ->
+    P ! {self(), Req},
+    receive
+	{P, Res} ->
+	    Res
+    end.
+
+t_loop() ->
+    receive
+	{From, die} ->
+	    From ! {self(), ok};
+	{From, {apply, M, F, A}} ->
+	    From ! {self(), apply(M, F, A)},
+	    t_loop()
+    end.
+
+start_slaves(Ns) ->
+    [start_slave(N) || N <- Ns].
+	       
+start_slave(Name) ->
+    case node() of
+        nonode@nohost ->
+            os:cmd("epmd -daemon"),
+            {ok, _} = net_kernel:start([gproc_master, shortnames]);
+        _ ->
+            ok
+    end,
+    {ok, Node} = slave:start(
+		   host(), Name,
+		   "-pa . -pz ../ebin -pa ../deps/gen_leader/ebin "
+		   "-gproc gproc_dist all"),
+    %% io:fwrite(user, "Slave node: ~p~n", [Node]),
+    Node.
+
+host() ->
+    [_Name, Host] = re:split(atom_to_list(node()), "@", [{return, list}]),
+    list_to_atom(Host).
+
+-endif.

+ 226 - 0
test/gproc_tests.erl

@@ -0,0 +1,226 @@
+%% ``The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved via the world wide web at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% @author Ulf Wiger <ulf.wiger@erlang-solutions.com>
+%%
+-module(gproc_tests).
+
+-ifdef(TEST).
+
+-include_lib("eunit/include/eunit.hrl").
+
+reg_test_() ->
+    {setup,
+     fun() ->
+             application:start(gproc)
+     end,
+     fun(_) ->
+             application:stop(gproc)
+     end,
+     [
+      {spawn, ?_test(t_simple_reg())}
+      , ?_test(t_is_clean())
+      , {spawn, ?_test(t_simple_prop())}
+      , ?_test(t_is_clean())
+      , {spawn, ?_test(t_await())}
+      , ?_test(t_is_clean())
+      , {spawn, ?_test(t_simple_mreg())}
+      , ?_test(t_is_clean())
+      , {spawn, ?_test(t_gproc_crash())}
+      , ?_test(t_is_clean())
+      , {spawn, ?_test(t_cancel_wait_and_register())}
+      , ?_test(t_is_clean())
+      , {spawn, ?_test(t_give_away_to_pid())}
+      , ?_test(t_is_clean())
+      , {spawn, ?_test(t_give_away_to_self())}
+      , ?_test(t_is_clean())
+      , {spawn, ?_test(t_give_away_badarg())}
+      , ?_test(t_is_clean())
+      , {spawn, ?_test(t_give_away_to_unknown())}
+      , ?_test(t_is_clean())
+      , {spawn, ?_test(t_give_away_and_back())}
+      , ?_test(t_is_clean())
+     ]}.
+
+t_simple_reg() ->
+    ?assert(gproc:reg({n,l,name}) =:= true),
+    ?assert(gproc:where({n,l,name}) =:= self()),
+    ?assert(gproc:unreg({n,l,name}) =:= true),
+    ?assert(gproc:where({n,l,name}) =:= undefined).
+
+
+                       
+t_simple_prop() ->
+    ?assert(gproc:reg({p,l,prop}) =:= true),
+    ?assert(t_other_proc(fun() ->
+                                 ?assert(gproc:reg({p,l,prop}) =:= true)
+                         end) =:= ok),
+    ?assert(gproc:unreg({p,l,prop}) =:= true).
+
+t_other_proc(F) ->
+    {_Pid,Ref} = spawn_monitor(fun() -> exit(F()) end),
+    receive
+        {'DOWN',Ref,_,_,R} ->
+            R
+    after 10000 ->
+            erlang:error(timeout)
+    end.
+
+t_await() ->
+    Me = self(),
+    {_Pid,Ref} = spawn_monitor(
+                   fun() -> exit(?assert(gproc:await({n,l,t_await}) =:= {Me,val})) end),
+    ?assert(gproc:reg({n,l,t_await},val) =:= true),
+    receive
+        {'DOWN', Ref, _, _, R} ->
+            ?assertEqual(R, ok)
+    after 10000 ->
+            erlang:error(timeout)
+    end.
+
+t_is_clean() ->
+    sys:get_status(gproc), % in order to synch
+    T = ets:tab2list(gproc),
+    ?assert(T =:= []).
+                                        
+
+t_simple_mreg() ->
+    ok.
+
+
+t_gproc_crash() ->
+    P = spawn_helper(),
+    ?assert(gproc:where({n,l,P}) =:= P),
+    exit(whereis(gproc), kill),
+    give_gproc_some_time(100),
+    ?assert(whereis(gproc) =/= undefined),
+    %%
+    %% Check that the registration is still there using an ets:lookup(),
+    %% Once we've killed the process, gproc will always return undefined
+    %% if the process is not alive, regardless of whether the registration
+    %% is still there. So, here, the lookup should find something...
+    %%
+    ?assert(ets:lookup(gproc,{{n,l,P},n}) =/= []),
+    ?assert(gproc:where({n,l,P}) =:= P),
+    exit(P, kill),
+    %% ...and here, it shouldn't.
+    %% (sleep for a while first to let gproc handle the EXIT
+    give_gproc_some_time(10),
+    ?assert(ets:lookup(gproc,{{n,l,P},n}) =:= []).
+
+t_cancel_wait_and_register() ->
+    Alias = {n, l, foo},
+    Me = self(),
+    P = spawn(fun() ->
+		      {'EXIT',_} = (catch gproc:await(Alias, 100)),
+		      ?assert(element(1,sys:get_status(gproc)) == status),
+		      Me ! {self(), go_ahead},
+		      timer:sleep(infinity)
+	      end),
+    receive
+	{P, go_ahead} ->
+	    ?assertEqual(gproc:reg(Alias, undefined), true),
+	    exit(P, kill),
+	    timer:sleep(500),
+	    ?assert(element(1,sys:get_status(gproc)) == status)
+    end.
+
+
+t_give_away_to_pid() ->
+    From = {n, l, foo},
+    Me = self(),
+    P = spawn_link(fun t_loop/0),
+    ?assertEqual(true, gproc:reg(From, undefined)),
+    ?assertEqual(Me, gproc:where(From)),
+    ?assertEqual(P, gproc:give_away(From, P)),
+    ?assertEqual(P, gproc:where(From)),
+    ?assertEqual(ok, t_call(P, die)).
+
+t_give_away_to_self() ->
+    From = {n, l, foo},
+    Me = self(),
+    ?assertEqual(true, gproc:reg(From, undefined)),
+    ?assertEqual(Me, gproc:where(From)),
+    ?assertEqual(Me, gproc:give_away(From, Me)),
+    ?assertEqual(Me, gproc:where(From)),
+    ?assertEqual(true, gproc:unreg(From)).
+
+t_give_away_badarg() ->
+    From = {n, l, foo},
+    Me = self(),
+    ?assertEqual(undefined, gproc:where(From)),
+    ?assertError(badarg, gproc:give_away(From, Me)).
+
+t_give_away_to_unknown() ->
+    From = {n, l, foo},
+    Unknown = {n, l, unknown},
+    Me = self(),
+    ?assertEqual(true, gproc:reg(From, undefined)),
+    ?assertEqual(Me, gproc:where(From)),
+    ?assertEqual(undefined, gproc:where(Unknown)),
+    ?assertEqual(undefined, gproc:give_away(From, Unknown)),
+    ?assertEqual(undefined, gproc:where(From)).
+
+t_give_away_and_back() ->
+    From = {n, l, foo},
+    Me = self(),
+    P = spawn_link(fun t_loop/0),
+    ?assertEqual(true, gproc:reg(From, undefined)),
+    ?assertEqual(Me, gproc:where(From)),
+    ?assertEqual(P, gproc:give_away(From, P)),
+    ?assertEqual(P, gproc:where(From)),
+    ?assertEqual(ok, t_call(P, {give_away, From})),
+    ?assertEqual(Me, gproc:where(From)),
+    ?assertEqual(ok, t_call(P, die)).
+
+t_loop() ->
+    receive
+	{From, {give_away, Key}} ->
+	    ?assertEqual(From, gproc:give_away(Key, From)),
+	    From ! {self(), ok},
+	    t_loop();
+	{From, die} ->
+	    From ! {self(), ok}
+    end.
+    
+
+t_call(P, Msg) ->
+    P ! {self(), Msg},
+    receive
+	{P, Reply} ->
+	    Reply
+    end.
+
+spawn_helper() ->
+    Parent = self(),
+    P = spawn(fun() ->
+		      ?assert(gproc:reg({n,l,self()}) =:= true),
+		      Ref = erlang:monitor(process, Parent),
+		      Parent ! {ok,self()},
+		      receive 
+			  {'DOWN', Ref, _, _, _} ->
+			      ok
+		      end
+	      end),
+    receive
+	{ok,P} ->
+	     P
+    end.
+
+give_gproc_some_time(T) ->
+    timer:sleep(T),
+    sys:get_status(gproc).
+
+-endif.