Browse Source

Merge pull request #95 from uwiger/uw-resource-counts

Uw resource counts
Ulf Wiger 9 years ago
parent
commit
981fab2e99
11 changed files with 619 additions and 230 deletions
  1. 15 7
      doc/gproc.md
  2. 29 3
      doc/gproc_pool.md
  3. 0 23
      include/gproc.hrl
  4. 212 129
      src/gproc.erl
  5. 3 1
      src/gproc_bcast.erl
  6. 48 28
      src/gproc_dist.erl
  7. 4 0
      src/gproc_int.hrl
  8. 125 35
      src/gproc_lib.erl
  9. 102 3
      test/gproc_dist_tests.erl
  10. 14 1
      test/gproc_test_lib.erl
  11. 67 0
      test/gproc_tests.erl

+ 15 - 7
doc/gproc.md

@@ -296,12 +296,12 @@ unique_id() = {n | a, <a href="#type-scope">scope()</a>, any()}
 ## Function Index ##
 ## Function Index ##
 
 
 
 
-<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="#add_shared_local_counter-2">add_shared_local_counter/2</a></td><td>Registers a local shared (unique) counter.</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="#await-3">await/3</a></td><td>Wait for a local name to be registered on <code>Node</code>.</td></tr><tr><td valign="top"><a href="#bcast-2">bcast/2</a></td><td>Equivalent to <a href="#bcast-3"><tt>bcast(nodes(), Key, Msg)</tt></a>.</td></tr><tr><td valign="top"><a href="#bcast-3">bcast/3</a></td><td>Sends a message to processes corresponding to Key on Nodes.</td></tr><tr><td valign="top"><a href="#cancel_wait-2">cancel_wait/2</a></td><td>Cancels a previous call to nb_wait/1.</td></tr><tr><td valign="top"><a href="#cancel_wait-3">cancel_wait/3</a></td><td>Cancels a previous call to nb_wait/2.</td></tr><tr><td valign="top"><a href="#cancel_wait_or_monitor-1">cancel_wait_or_monitor/1</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="#demonitor-2">demonitor/2</a></td><td>Remove a monitor on a registered name
+<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="#add_shared_local_counter-2">add_shared_local_counter/2</a></td><td>Registers a local shared (unique) counter.</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 name or aggregated counter to be registered.</td></tr><tr><td valign="top"><a href="#await-3">await/3</a></td><td>Wait for a name or aggregated counter to be registered on <code>Node</code>.</td></tr><tr><td valign="top"><a href="#bcast-2">bcast/2</a></td><td>Equivalent to <a href="#bcast-3"><tt>bcast(nodes(), Key, Msg)</tt></a>.</td></tr><tr><td valign="top"><a href="#bcast-3">bcast/3</a></td><td>Sends a message to processes corresponding to Key on Nodes.</td></tr><tr><td valign="top"><a href="#cancel_wait-2">cancel_wait/2</a></td><td>Cancels a previous call to nb_wait/1.</td></tr><tr><td valign="top"><a href="#cancel_wait-3">cancel_wait/3</a></td><td>Cancels a previous call to nb_wait/2.</td></tr><tr><td valign="top"><a href="#cancel_wait_or_monitor-1">cancel_wait_or_monitor/1</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="#demonitor-2">demonitor/2</a></td><td>Remove a monitor on a registered name
 This function is the reverse of monitor/1.</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.</td></tr><tr><td valign="top"><a href="#get_attribute-2">get_attribute/2</a></td><td>Get attribute value of <code>Attr</code> associated with <code>Key</code> for most likely Pid.</td></tr><tr><td valign="top"><a href="#get_attribute-3">get_attribute/3</a></td><td>Get the attribute value of <code>Attr</code> associated with <code>Key</code> for process Pid.</td></tr><tr><td valign="top"><a href="#get_attribute_shared-2">get_attribute_shared/2</a></td><td>Get the attribute value of <code>Attr</code> associated with the shared <code>Key</code>.</td></tr><tr><td valign="top"><a href="#get_attributes-1">get_attributes/1</a></td><td>Get attributes associated with registration.</td></tr><tr><td valign="top"><a href="#get_attributes-2">get_attributes/2</a></td><td>Returns the list of attributes associated with the registration.</td></tr><tr><td valign="top"><a href="#get_env-3">get_env/3</a></td><td>Equivalent to <a href="#get_env-4"><tt>get_env(Scope, App, Key, [app_env])</tt></a>.</td></tr><tr><td valign="top"><a href="#get_env-4">get_env/4</a></td><td>Read an environment value, potentially cached as a <code>gproc_env</code> property.</td></tr><tr><td valign="top"><a href="#get_set_env-3">get_set_env/3</a></td><td>Equivalent to <a href="#get_set_env-4"><tt>get_set_env(Scope, App, Key, [app_env])</tt></a>.</td></tr><tr><td valign="top"><a href="#get_set_env-4">get_set_env/4</a></td><td>Fetch and cache an environment value, if not already cached.</td></tr><tr><td valign="top"><a href="#get_value-1">get_value/1</a></td><td>Reads the value stored with a key registered to the current process.</td></tr><tr><td valign="top"><a href="#get_value-2">get_value/2</a></td><td>Reads the value stored with a key registered to the process Pid.</td></tr><tr><td valign="top"><a href="#get_value_shared-1">get_value_shared/1</a></td><td>Reads the value stored with a shared key.</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="#goodbye-0">goodbye/0</a></td><td>Unregister all items of the calling process and inform gproc
 This function is the reverse of monitor/1.</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.</td></tr><tr><td valign="top"><a href="#get_attribute-2">get_attribute/2</a></td><td>Get attribute value of <code>Attr</code> associated with <code>Key</code> for most likely Pid.</td></tr><tr><td valign="top"><a href="#get_attribute-3">get_attribute/3</a></td><td>Get the attribute value of <code>Attr</code> associated with <code>Key</code> for process Pid.</td></tr><tr><td valign="top"><a href="#get_attribute_shared-2">get_attribute_shared/2</a></td><td>Get the attribute value of <code>Attr</code> associated with the shared <code>Key</code>.</td></tr><tr><td valign="top"><a href="#get_attributes-1">get_attributes/1</a></td><td>Get attributes associated with registration.</td></tr><tr><td valign="top"><a href="#get_attributes-2">get_attributes/2</a></td><td>Returns the list of attributes associated with the registration.</td></tr><tr><td valign="top"><a href="#get_env-3">get_env/3</a></td><td>Equivalent to <a href="#get_env-4"><tt>get_env(Scope, App, Key, [app_env])</tt></a>.</td></tr><tr><td valign="top"><a href="#get_env-4">get_env/4</a></td><td>Read an environment value, potentially cached as a <code>gproc_env</code> property.</td></tr><tr><td valign="top"><a href="#get_set_env-3">get_set_env/3</a></td><td>Equivalent to <a href="#get_set_env-4"><tt>get_set_env(Scope, App, Key, [app_env])</tt></a>.</td></tr><tr><td valign="top"><a href="#get_set_env-4">get_set_env/4</a></td><td>Fetch and cache an environment value, if not already cached.</td></tr><tr><td valign="top"><a href="#get_value-1">get_value/1</a></td><td>Reads the value stored with a key registered to the current process.</td></tr><tr><td valign="top"><a href="#get_value-2">get_value/2</a></td><td>Reads the value stored with a key registered to the process Pid.</td></tr><tr><td valign="top"><a href="#get_value_shared-1">get_value_shared/1</a></td><td>Reads the value stored with a shared key.</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="#goodbye-0">goodbye/0</a></td><td>Unregister all items of the calling process and inform gproc
 to forget about the calling process.</td></tr><tr><td valign="top"><a href="#i-0">i/0</a></td><td>Similar to the built-in shell command <code>i()</code> but inserts information
 to forget about the calling process.</td></tr><tr><td valign="top"><a href="#i-0">i/0</a></td><td>Similar to the built-in shell command <code>i()</code> but inserts information
 about names and properties registered in Gproc, where applicable.</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.</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="#monitor-1">monitor/1</a></td><td>Equivalent to <a href="#monitor-2"><tt>monitor(Key, info)</tt></a>.</td></tr><tr><td valign="top"><a href="#monitor-2">monitor/2</a></td><td>monitor a registered name
 about names and properties registered in Gproc, where applicable.</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.</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="#monitor-1">monitor/1</a></td><td>Equivalent to <a href="#monitor-2"><tt>monitor(Key, info)</tt></a>.</td></tr><tr><td valign="top"><a href="#monitor-2">monitor/2</a></td><td>monitor a registered name
 <code>monitor(Key, info)</code> works much like erlang:monitor(process, Pid), but monitors
 <code>monitor(Key, info)</code> works much like erlang:monitor(process, Pid), but monitors
-a unique name registered via gproc.</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="#munreg-3">munreg/3</a></td><td>Unregister multiple Key items 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="#nb_wait-2">nb_wait/2</a></td><td>Wait for a local name to be registered on <code>Node</code>.</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.</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.</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="#reg_or_locate-1">reg_or_locate/1</a></td><td>Equivalent to <a href="#reg_or_locate-2"><tt>reg_or_locate(Key, default(Key))</tt></a>.</td></tr><tr><td valign="top"><a href="#reg_or_locate-2">reg_or_locate/2</a></td><td>Try registering a unique name, or return existing registration.</td></tr><tr><td valign="top"><a href="#reg_or_locate-3">reg_or_locate/3</a></td><td>Spawn a process with a registered name, or return existing registration.</td></tr><tr><td valign="top"><a href="#reg_shared-1">reg_shared/1</a></td><td>Register a resource, but don't tie it to a particular process.</td></tr><tr><td valign="top"><a href="#reg_shared-2">reg_shared/2</a></td><td>Register a resource, but don't tie it to a particular process.</td></tr><tr><td valign="top"><a href="#register_name-2">register_name/2</a></td><td>Behaviour support callback.</td></tr><tr><td valign="top"><a href="#reset_counter-1">reset_counter/1</a></td><td>Reads and resets a counter in a "thread-safe" way.</td></tr><tr><td valign="top"><a href="#select-1">select/1</a></td><td>Perform a select operation on the process registry.</td></tr><tr><td valign="top"><a href="#select-2">select/2</a></td><td>Perform a select operation with limited context 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="#select_count-1">select_count/1</a></td><td>Equivalent to <a href="#select_count-2"><tt>select_count(all, Pat)</tt></a>.</td></tr><tr><td valign="top"><a href="#select_count-2">select_count/2</a></td><td>Perform a select_count operation on the process registry.</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_attributes-2">set_attributes/2</a></td><td>Add/modify <code>{Key, Value}</code> attributes associated with a registration.</td></tr><tr><td valign="top"><a href="#set_attributes_shared-2">set_attributes_shared/2</a></td><td>Add/modify <code>{Key, Value}</code> attributes associated with a shared registration.</td></tr><tr><td valign="top"><a href="#set_env-5">set_env/5</a></td><td>Updates the cached value as well as underlying environment.</td></tr><tr><td valign="top"><a href="#set_value-2">set_value/2</a></td><td>Sets the value of the registration given by Key.</td></tr><tr><td valign="top"><a href="#set_value_shared-2">set_value_shared/2</a></td><td>Sets the value of the shared registration 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-0">table/0</a></td><td>Equivalent to <a href="#table-1"><tt>table({all, all})</tt></a>.</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="#unreg_shared-1">unreg_shared/1</a></td><td>Unregister a shared resource.</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="#update_counter-3">update_counter/3</a></td><td></td></tr><tr><td valign="top"><a href="#update_counters-2">update_counters/2</a></td><td>Update a list of counters.</td></tr><tr><td valign="top"><a href="#update_shared_counter-2">update_shared_counter/2</a></td><td>Updates the shared counter registered as Key.</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><tr><td valign="top"><a href="#wide_await-3">wide_await/3</a></td><td>Wait for a local name to be registered on any of <code>Nodes</code>.</td></tr></table>
+a unique name registered via gproc.</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="#munreg-3">munreg/3</a></td><td>Unregister multiple Key items 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 name or aggregated counter to be registered.</td></tr><tr><td valign="top"><a href="#nb_wait-2">nb_wait/2</a></td><td>Wait for a name or aggregated counter to be registered on <code>Node</code>.</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.</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.</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="#reg_or_locate-1">reg_or_locate/1</a></td><td>Equivalent to <a href="#reg_or_locate-2"><tt>reg_or_locate(Key, default(Key))</tt></a>.</td></tr><tr><td valign="top"><a href="#reg_or_locate-2">reg_or_locate/2</a></td><td>Try registering a unique name, or return existing registration.</td></tr><tr><td valign="top"><a href="#reg_or_locate-3">reg_or_locate/3</a></td><td>Spawn a process with a registered name, or return existing registration.</td></tr><tr><td valign="top"><a href="#reg_shared-1">reg_shared/1</a></td><td>Register a resource, but don't tie it to a particular process.</td></tr><tr><td valign="top"><a href="#reg_shared-2">reg_shared/2</a></td><td>Register a resource, but don't tie it to a particular process.</td></tr><tr><td valign="top"><a href="#register_name-2">register_name/2</a></td><td>Behaviour support callback.</td></tr><tr><td valign="top"><a href="#reset_counter-1">reset_counter/1</a></td><td>Reads and resets a counter in a "thread-safe" way.</td></tr><tr><td valign="top"><a href="#select-1">select/1</a></td><td>Perform a select operation on the process registry.</td></tr><tr><td valign="top"><a href="#select-2">select/2</a></td><td>Perform a select operation with limited context 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="#select_count-1">select_count/1</a></td><td>Equivalent to <a href="#select_count-2"><tt>select_count(all, Pat)</tt></a>.</td></tr><tr><td valign="top"><a href="#select_count-2">select_count/2</a></td><td>Perform a select_count operation on the process registry.</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_attributes-2">set_attributes/2</a></td><td>Add/modify <code>{Key, Value}</code> attributes associated with a registration.</td></tr><tr><td valign="top"><a href="#set_attributes_shared-2">set_attributes_shared/2</a></td><td>Add/modify <code>{Key, Value}</code> attributes associated with a shared registration.</td></tr><tr><td valign="top"><a href="#set_env-5">set_env/5</a></td><td>Updates the cached value as well as underlying environment.</td></tr><tr><td valign="top"><a href="#set_value-2">set_value/2</a></td><td>Sets the value of the registration given by Key.</td></tr><tr><td valign="top"><a href="#set_value_shared-2">set_value_shared/2</a></td><td>Sets the value of the shared registration 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-0">table/0</a></td><td>Equivalent to <a href="#table-1"><tt>table({all, all})</tt></a>.</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="#unreg_shared-1">unreg_shared/1</a></td><td>Unregister a shared resource.</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="#update_counter-3">update_counter/3</a></td><td></td></tr><tr><td valign="top"><a href="#update_counters-2">update_counters/2</a></td><td>Update a list of counters.</td></tr><tr><td valign="top"><a href="#update_shared_counter-2">update_shared_counter/2</a></td><td>Updates the shared counter registered as Key.</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><tr><td valign="top"><a href="#wide_await-3">wide_await/3</a></td><td>Wait for a local name to be registered on any of <code>Nodes</code>.</td></tr></table>
 
 
 
 
 <a name="functions"></a>
 <a name="functions"></a>
@@ -410,7 +410,7 @@ await(Key::<a href="#type-key">key()</a>, Timeout) -&gt; {pid(), Value}
 
 
 <ul class="definitions"><li><code>Timeout = integer() | infinity</code></li></ul>
 <ul class="definitions"><li><code>Timeout = integer() | infinity</code></li></ul>
 
 
-Wait for a local name to be registered.
+Wait for a name or aggregated counter to be registered.
 The function raises an exception if the timeout expires. Timeout must be
 The function raises an exception if the timeout expires. Timeout must be
 either an interger > 0 or 'infinity'.
 either an interger > 0 or 'infinity'.
 A small optimization: we first perform a lookup, to see if the name
 A small optimization: we first perform a lookup, to see if the name
@@ -428,7 +428,7 @@ await(Node::node(), Key::<a href="#type-key">key()</a>, Timeout) -&gt; {pid(), V
 
 
 <ul class="definitions"><li><code>Timeout = integer() | infinity</code></li></ul>
 <ul class="definitions"><li><code>Timeout = integer() | infinity</code></li></ul>
 
 
-Wait for a local name to be registered on `Node`.
+Wait for a name or aggregated counter to be registered on `Node`.
 This function works exactly like [`await/2`](#await-2), but queries a remote
 This function works exactly like [`await/2`](#await-2), but queries a remote
 node instead. An exception is thrown if `Node` cannot be reached. If gproc
 node instead. An exception is thrown if `Node` cannot be reached. If gproc
 is not running on a given node, this is treated the same as the node being
 is not running on a given node, this is treated the same as the node being
@@ -1149,7 +1149,7 @@ nb_wait(Key::<a href="#type-key">key()</a>) -&gt; Ref
 </code></pre>
 </code></pre>
 <br />
 <br />
 
 
-Wait for a local name to be registered.
+Wait for a name or aggregated counter to be registered.
 The caller can expect to receive a message,
 The caller can expect to receive a message,
 {gproc, Ref, registered, {Key, Pid, Value}}, once the name is registered.
 {gproc, Ref, registered, {Key, Pid, Value}}, once the name is registered.
 <a name="nb_wait-2"></a>
 <a name="nb_wait-2"></a>
@@ -1162,7 +1162,7 @@ nb_wait(Node::node(), Key::<a href="#type-key">key()</a>) -&gt; Ref
 </code></pre>
 </code></pre>
 <br />
 <br />
 
 
-Wait for a local name to be registered on `Node`.
+Wait for a name or aggregated counter to be registered on `Node`.
 The caller can expect to receive a message,
 The caller can expect to receive a message,
 {gproc, Ref, registered, {Key, Pid, Value}}, once the name is registered.
 {gproc, Ref, registered, {Key, Pid, Value}}, once the name is registered.
 <a name="next-2"></a>
 <a name="next-2"></a>
@@ -1335,16 +1335,24 @@ Behaviour support callback
 reset_counter(Key) -&gt; {ValueBefore, ValueAfter}
 reset_counter(Key) -&gt; {ValueBefore, ValueAfter}
 </code></pre>
 </code></pre>
 
 
-<ul class="definitions"><li><code>Key = {c, Scope, Name}</code></li><li><code>Scope = l | g</code></li><li><code>ValueBefore = integer()</code></li><li><code>ValueAfter = integer()</code></li></ul>
+<ul class="definitions"><li><code>Key = {c, Scope, Name} | {n, Scope, Name}</code></li><li><code>Scope = l | g</code></li><li><code>ValueBefore = integer()</code></li><li><code>ValueAfter = integer()</code></li></ul>
 
 
 
 
 Reads and resets a counter in a "thread-safe" way
 Reads and resets a counter in a "thread-safe" way
 
 
 
 
+
 This function reads the current value of a counter and then resets it to its
 This function reads the current value of a counter and then resets it to its
 initial value. The reset operation is done using [`update_counter/2`](#update_counter-2),
 initial value. The reset operation is done using [`update_counter/2`](#update_counter-2),
 which allows for concurrent calls to [`update_counter/2`](#update_counter-2) without losing
 which allows for concurrent calls to [`update_counter/2`](#update_counter-2) without losing
 updates. Aggregated counters are updated accordingly.
 updates. Aggregated counters are updated accordingly.
+
+
+If `Key` refers to a unique name, the operation will depend on the value
+part of the registration being an integer(). While non-integer values are
+not permitted at all for counter objects, it is the user's responsibility to
+ensure that a name, on which `reset_counter/1` is to be performed, has the
+appropriate value type.
 <a name="select-1"></a>
 <a name="select-1"></a>
 
 
 ### select/1 ###
 ### select/1 ###

+ 29 - 3
doc/gproc_pool.md

@@ -63,7 +63,7 @@ jobs will not exceed the size of the pool.<a name="index"></a>
 ## Function Index ##
 ## Function Index ##
 
 
 
 
-<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#active_workers-1">active_workers/1</a></td><td>Return a list of currently connected workers in the pool.</td></tr><tr><td valign="top"><a href="#add_worker-2">add_worker/2</a></td><td>Assign a worker name to the pool, returning the worker's position.</td></tr><tr><td valign="top"><a href="#add_worker-3">add_worker/3</a></td><td>Assign a worker name to a given slot in the pool, returning the slot.</td></tr><tr><td valign="top"><a href="#claim-2">claim/2</a></td><td>Picks the first available worker in the pool and applies <code>Fun</code>.</td></tr><tr><td valign="top"><a href="#connect_worker-2">connect_worker/2</a></td><td>Connect the current process to <code>Name</code> in <code>Pool</code>.</td></tr><tr><td valign="top"><a href="#defined_workers-1">defined_workers/1</a></td><td>Return a list of added workers in the pool.</td></tr><tr><td valign="top"><a href="#delete-1">delete/1</a></td><td>Delete an existing pool.</td></tr><tr><td valign="top"><a href="#disconnect_worker-2">disconnect_worker/2</a></td><td>Disconnect the current process from <code>Name</code> in <code>Pool</code>.</td></tr><tr><td valign="top"><a href="#force_delete-1">force_delete/1</a></td><td>Forcibly remove a pool, terminating all active workers.</td></tr><tr><td valign="top"><a href="#log-1">log/1</a></td><td>Update a counter associated with a worker name.</td></tr><tr><td valign="top"><a href="#new-1">new/1</a></td><td>Equivalent to <a href="#new-3"><tt>new(Pool, round_robin, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#new-3">new/3</a></td><td>Create a new pool.</td></tr><tr><td valign="top"><a href="#pick-1">pick/1</a></td><td>Pick a worker from the pool given the pool's load-balancing algorithm.</td></tr><tr><td valign="top"><a href="#pick-2">pick/2</a></td><td>Pick a worker from the pool based on <code>Value</code>.</td></tr><tr><td valign="top"><a href="#pick_worker-1">pick_worker/1</a></td><td>Pick a worker pid from the pool given the pool's load-balancing algorithm.</td></tr><tr><td valign="top"><a href="#pick_worker-2">pick_worker/2</a></td><td>Pick a worker pid from the pool given the pool's load-balancing algorithm.</td></tr><tr><td valign="top"><a href="#ptest-4">ptest/4</a></td><td></td></tr><tr><td valign="top"><a href="#randomize-1">randomize/1</a></td><td>Randomizes the "next" pointer for the pool.</td></tr><tr><td valign="top"><a href="#remove_worker-2">remove_worker/2</a></td><td>Remove a previously added worker.</td></tr><tr><td valign="top"><a href="#test_run0-2">test_run0/2</a></td><td></td></tr><tr><td valign="top"><a href="#whereis_worker-2">whereis_worker/2</a></td><td>Look up the pid of a connected worker.</td></tr><tr><td valign="top"><a href="#worker_id-2">worker_id/2</a></td><td>Return the unique gproc name corresponding to a name in the pool.</td></tr><tr><td valign="top"><a href="#worker_pool-1">worker_pool/1</a></td><td>Return a list of slots and/or named workers in the pool.</td></tr></table>
+<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#active_workers-1">active_workers/1</a></td><td>Return a list of currently connected workers in the pool.</td></tr><tr><td valign="top"><a href="#add_worker-2">add_worker/2</a></td><td>Assign a worker name to the pool, returning the worker's position.</td></tr><tr><td valign="top"><a href="#add_worker-3">add_worker/3</a></td><td>Assign a worker name to a given slot in the pool, returning the slot.</td></tr><tr><td valign="top"><a href="#claim-2">claim/2</a></td><td>Equivalent to <a href="#claim-3"><tt>claim(Pool, F, nowait)</tt></a>.</td></tr><tr><td valign="top"><a href="#claim-3">claim/3</a></td><td>Picks the first available worker in the pool and applies <code>Fun</code>.</td></tr><tr><td valign="top"><a href="#connect_worker-2">connect_worker/2</a></td><td>Connect the current process to <code>Name</code> in <code>Pool</code>.</td></tr><tr><td valign="top"><a href="#defined_workers-1">defined_workers/1</a></td><td>Return a list of added workers in the pool.</td></tr><tr><td valign="top"><a href="#delete-1">delete/1</a></td><td>Delete an existing pool.</td></tr><tr><td valign="top"><a href="#disconnect_worker-2">disconnect_worker/2</a></td><td>Disconnect the current process from <code>Name</code> in <code>Pool</code>.</td></tr><tr><td valign="top"><a href="#force_delete-1">force_delete/1</a></td><td>Forcibly remove a pool, terminating all active workers.</td></tr><tr><td valign="top"><a href="#log-1">log/1</a></td><td>Update a counter associated with a worker name.</td></tr><tr><td valign="top"><a href="#new-1">new/1</a></td><td>Equivalent to <a href="#new-3"><tt>new(Pool, round_robin, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#new-3">new/3</a></td><td>Create a new pool.</td></tr><tr><td valign="top"><a href="#pick-1">pick/1</a></td><td>Pick a worker from the pool given the pool's load-balancing algorithm.</td></tr><tr><td valign="top"><a href="#pick-2">pick/2</a></td><td>Pick a worker from the pool based on <code>Value</code>.</td></tr><tr><td valign="top"><a href="#pick_worker-1">pick_worker/1</a></td><td>Pick a worker pid from the pool given the pool's load-balancing algorithm.</td></tr><tr><td valign="top"><a href="#pick_worker-2">pick_worker/2</a></td><td>Pick a worker pid from the pool given the pool's load-balancing algorithm.</td></tr><tr><td valign="top"><a href="#ptest-4">ptest/4</a></td><td></td></tr><tr><td valign="top"><a href="#randomize-1">randomize/1</a></td><td>Randomizes the "next" pointer for the pool.</td></tr><tr><td valign="top"><a href="#remove_worker-2">remove_worker/2</a></td><td>Remove a previously added worker.</td></tr><tr><td valign="top"><a href="#setup_test_pool-4">setup_test_pool/4</a></td><td></td></tr><tr><td valign="top"><a href="#test_run0-2">test_run0/2</a></td><td></td></tr><tr><td valign="top"><a href="#whereis_worker-2">whereis_worker/2</a></td><td>Look up the pid of a connected worker.</td></tr><tr><td valign="top"><a href="#worker_id-2">worker_id/2</a></td><td>Return the unique gproc name corresponding to a name in the pool.</td></tr><tr><td valign="top"><a href="#worker_pool-1">worker_pool/1</a></td><td>Return a list of slots and/or named workers in the pool.</td></tr></table>
 
 
 
 
 <a name="functions"></a>
 <a name="functions"></a>
@@ -135,16 +135,25 @@ otherwise the pool is expanded to accomodate the new position.
 
 
 ### claim/2 ###
 ### claim/2 ###
 
 
+`claim(Pool, F) -> any()`
+
+Equivalent to [`claim(Pool, F, nowait)`](#claim-3).
+<a name="claim-3"></a>
+
+### claim/3 ###
+
 
 
 <pre><code>
 <pre><code>
-claim(Pool::any(), Fun::function()) -&gt; {true, Res} | false
+claim(Pool, F::Fun, Wait) -&gt; {true, Res} | false
 </code></pre>
 </code></pre>
-<br />
+
+<ul class="definitions"><li><code>Pool = any()</code></li><li><code>Fun = function()</code></li><li><code>Wait = nowait | {busy_wait, integer()}</code></li></ul>
 
 
 
 
 Picks the first available worker in the pool and applies `Fun`.
 Picks the first available worker in the pool and applies `Fun`.
 
 
 
 
+
 A `claim` pool allows the caller to "claim" a worker during a short span
 A `claim` pool allows the caller to "claim" a worker during a short span
 (essentially, a lock is set and released as soon as `Fun` returns).
 (essentially, a lock is set and released as soon as `Fun` returns).
 Once a worker is selected, `Fun(Name, Pid)` is called, where `Name` is a
 Once a worker is selected, `Fun(Name, Pid)` is called, where `Name` is a
@@ -152,6 +161,16 @@ unique gproc name of the worker, and `Pid` is its process identifier.
 The gproc name of the worker serves as a mutex, where its value is 0 (zero)
 The gproc name of the worker serves as a mutex, where its value is 0 (zero)
 if the worker is free, and 1 (one) if it is busy. The mutex operation is
 if the worker is free, and 1 (one) if it is busy. The mutex operation is
 implemented using `gproc:update_counter/2`.
 implemented using `gproc:update_counter/2`.
+
+
+
+`Wait == nowait` means that the call will return `false` immediately if
+there is no available worker.
+
+
+`Wait == {busy_wait, Timeout}` will keep repeating the claim attempt
+for `Timeout` milliseconds. If still no worker is available, it will
+return `false`.
 <a name="connect_worker-2"></a>
 <a name="connect_worker-2"></a>
 
 
 ### connect_worker/2 ###
 ### connect_worker/2 ###
@@ -424,6 +443,13 @@ Remove a previously added worker.
 This function will assume that any connected worker is disconnected first.
 This function will assume that any connected worker is disconnected first.
 It will fail if there is no such pool, but will return `true` in the case
 It will fail if there is no such pool, but will return `true` in the case
 when `Name` did not exist in the pool in the first place.
 when `Name` did not exist in the pool in the first place.
+<a name="setup_test_pool-4"></a>
+
+### setup_test_pool/4 ###
+
+`setup_test_pool(P, Type0, Opts, Workers) -> any()`
+
+
 <a name="test_run0-2"></a>
 <a name="test_run0-2"></a>
 
 
 ### test_run0/2 ###
 ### test_run0/2 ###

+ 0 - 23
include/gproc.hrl

@@ -18,26 +18,3 @@
 %% gproc.hrl: Common definitions
 %% gproc.hrl: Common definitions
 
 
 -define(TAB, gproc).
 -define(TAB, gproc).
-
-
--type type()     :: n | p | c | a.
--type scope()    :: l | g.
--type context()  :: {scope(),type()} | type().
--type sel_type() :: n | p | c | a |
-                    names | props | counters | aggr_counters.
-
--type sel_var() :: '_' | atom().
--type keypat()  :: {sel_type() | sel_var(), l | g | sel_var(), any()}.
--type pidpat()  :: pid() | sel_var().
--type headpat() :: {keypat(),pidpat(),any()}.
--type key()     :: {type(), scope(), any()}.
-
--type sel_pattern() :: [{headpat(), list(), list()}].
-
-%% update_counter increment
--type ctr_incr()   :: integer().
--type ctr_thr()    :: integer().
--type ctr_setval() :: integer().
--type ctr_update()  :: ctr_incr()
-		     | {ctr_incr(), ctr_thr(), ctr_setval()}.
--type increment() :: ctr_incr() | ctr_update() | [ctr_update()].

+ 212 - 129
src/gproc.erl

@@ -42,43 +42,13 @@
 %%
 %%
 %% @end
 %% @end
 
 
-%% @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 reg_id() = {type(), scope(), any()}.
-%% @type unique_id() = {n | a, scope(), any()}.
-%%
-%% @type monitor_type() = info | standby | follow.
-%%
-%% @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(),
-%%                   any()}.
-%% @type pidpat() = pid() | sel_var().
-%% @type sel_var() = DollarVar | '_'.
-%% @type sel_pattern() = [{headpat(), Guards, Prod}].
-%% @type key()   = {type(), scope(), any()}.
-%%
-%% update_counter increment
-%% @type ctr_incr()   = integer().
-%% @type ctr_thr()    = integer().
-%% @type ctr_setval() = integer().
-%% @type ctr_update()  = ctr_incr()
-%% 		     | {ctr_incr(), ctr_thr(), ctr_setval()}.
-%% @type increment() = ctr_incr() | ctr_update() | [ctr_update()].
-
 -module(gproc).
 -module(gproc).
 -behaviour(gen_server).
 -behaviour(gen_server).
 
 
 -export([start_link/0,
 -export([start_link/0,
-         reg/1, reg/2, unreg/1, set_attributes/2,
+         reg/1, reg/2, reg/3, unreg/1, set_attributes/2,
 	 reg_or_locate/1, reg_or_locate/2, reg_or_locate/3,
 	 reg_or_locate/1, reg_or_locate/2, reg_or_locate/3,
-	 reg_shared/1, reg_shared/2, unreg_shared/1,
+	 reg_shared/1, reg_shared/2, reg_shared/3, unreg_shared/1,
 	 set_attributes_shared/2, set_value_shared/2,
 	 set_attributes_shared/2, set_value_shared/2,
          mreg/3,
          mreg/3,
          munreg/3,
          munreg/3,
@@ -161,6 +131,40 @@
 -include("gproc_int.hrl").
 -include("gproc_int.hrl").
 -include("gproc.hrl").
 -include("gproc.hrl").
 
 
+-export_type([scope/0, type/0, key/0,
+              context/0, sel_pattern/0, sel_scope/0, sel_context/0,
+              reg_id/0, unique_id/0, monitor_type/0]).
+
+-type type()     :: n | p | c | a | r | rc.
+-type scope()    :: l | g.
+-type context()  :: {scope(),type()} | type().
+-type sel_type() :: type()
+                    | names | props | counters | aggr_counters
+                    | resources | resource_counters.
+
+-type sel_var() :: '_' | atom().
+-type keypat()  :: {sel_type() | sel_var(), l | g | sel_var(), any()}.
+-type pidpat()  :: pid() | sel_var().
+-type headpat() :: {keypat(), pidpat(), any()}.
+-type key()     :: {type(), scope(), any()}.
+
+-type sel_pattern() :: [{headpat(), list(), list()}].
+
+-type reg_id() :: {type(), scope(), any()}.
+-type unique_id() :: {n | a, scope(), any()}.
+-type monitor_type() :: info | standby | follow.
+-type sel_scope() :: scope | all | global | local.
+-type sel_context() :: {scope(), type()} | type().
+
+%% update_counter increment
+-type ctr_incr()   :: integer().
+-type ctr_thr()    :: integer().
+-type ctr_setval() :: integer().
+-type ctr_update()  :: ctr_incr()
+		     | {ctr_incr(), ctr_thr(), ctr_setval()}.
+-type increment() :: ctr_incr() | ctr_update() | [ctr_update()].
+
+
 -define(SERVER, ?MODULE).
 -define(SERVER, ?MODULE).
 %%-define(l, l(?LINE)). % when activated, calls a traceable empty function
 %%-define(l, l(?LINE)). % when activated, calls a traceable empty function
 -define(l, ignore).
 -define(l, ignore).
@@ -196,7 +200,8 @@ start_link() ->
 %% @doc Registers a local (unique) name. @equiv reg({n,l,Name})
 %% @doc Registers a local (unique) name. @equiv reg({n,l,Name})
 %% @end
 %% @end
 %%
 %%
-add_local_name(Name)  -> ?CATCH_GPROC_ERROR(reg1({n,l,Name}, undefined), [Name]).
+add_local_name(Name)  ->
+    ?CATCH_GPROC_ERROR(reg1({n,l,Name}, undefined, []), [Name]).
 
 
 
 
 %% spec(Name::any()) -> true
 %% spec(Name::any()) -> true
@@ -204,7 +209,8 @@ add_local_name(Name)  -> ?CATCH_GPROC_ERROR(reg1({n,l,Name}, undefined), [Name])
 %% @doc Registers a global (unique) name. @equiv reg({n,g,Name})
 %% @doc Registers a global (unique) name. @equiv reg({n,g,Name})
 %% @end
 %% @end
 %%
 %%
-add_global_name(Name) -> ?CATCH_GPROC_ERROR(reg1({n,g,Name}, undefined), [Name]).
+add_global_name(Name) ->
+    ?CATCH_GPROC_ERROR(reg1({n,g,Name}, undefined, []), [Name]).
 
 
 
 
 %% spec(Name::any(), Value::any()) -> true
 %% spec(Name::any(), Value::any()) -> true
@@ -213,7 +219,7 @@ add_global_name(Name) -> ?CATCH_GPROC_ERROR(reg1({n,g,Name}, undefined), [Name])
 %% @end
 %% @end
 %%
 %%
 add_local_property(Name , Value) ->
 add_local_property(Name , Value) ->
-    ?CATCH_GPROC_ERROR(reg1({p,l,Name}, Value), [Name, Value]).
+    ?CATCH_GPROC_ERROR(reg1({p,l,Name}, Value, []), [Name, Value]).
 
 
 %% spec(Name::any(), Value::any()) -> true
 %% spec(Name::any(), Value::any()) -> true
 %%
 %%
@@ -221,7 +227,7 @@ add_local_property(Name , Value) ->
 %% @end
 %% @end
 %%
 %%
 add_global_property(Name, Value) ->
 add_global_property(Name, Value) ->
-    ?CATCH_GPROC_ERROR(reg1({p,g,Name}, Value), [Name, Value]).
+    ?CATCH_GPROC_ERROR(reg1({p,g,Name}, Value, []), [Name, Value]).
 
 
 %% spec(Name::any(), Initial::integer()) -> true
 %% spec(Name::any(), Initial::integer()) -> true
 %%
 %%
@@ -229,7 +235,7 @@ add_global_property(Name, Value) ->
 %% @end
 %% @end
 %%
 %%
 add_local_counter(Name, Initial) when is_integer(Initial) ->
 add_local_counter(Name, Initial) when is_integer(Initial) ->
-    ?CATCH_GPROC_ERROR(reg1({c,l,Name}, Initial), [Name, Initial]).
+    ?CATCH_GPROC_ERROR(reg1({c,l,Name}, Initial, []), [Name, Initial]).
 
 
 
 
 %% spec(Name::any(), Initial::integer()) -> true
 %% spec(Name::any(), Initial::integer()) -> true
@@ -248,7 +254,7 @@ add_shared_local_counter(Name, Initial) when is_integer(Initial) ->
 %% @end
 %% @end
 %%
 %%
 add_global_counter(Name, Initial) when is_integer(Initial) ->
 add_global_counter(Name, Initial) when is_integer(Initial) ->
-    ?CATCH_GPROC_ERROR(reg1({c,g,Name}, Initial), [Name, Initial]).
+    ?CATCH_GPROC_ERROR(reg1({c,g,Name}, Initial, []), [Name, Initial]).
 
 
 %% spec(Name::any()) -> true
 %% spec(Name::any()) -> true
 %%
 %%
@@ -538,7 +544,7 @@ lookup_env(Scope, App, Key, P) ->
 
 
 cache_env(Scope, App, Key, Value) ->
 cache_env(Scope, App, Key, Value) ->
     ?CATCH_GPROC_ERROR(
     ?CATCH_GPROC_ERROR(
-       reg1({p, Scope, {gproc_env, App, Key}}, Value),
+       reg1({p, Scope, {gproc_env, App, Key}}, Value, []),
        [Scope,App,Key,Value]).
        [Scope,App,Key,Value]).
 
 
 update_cached_env(Scope, App, Key, Value) ->
 update_cached_env(Scope, App, Key, Value) ->
@@ -604,13 +610,13 @@ is_string(S) ->
 %% @spec reg(Key::key()) -> true
 %% @spec reg(Key::key()) -> true
 %%
 %%
 %% @doc
 %% @doc
-%% @equiv reg(Key, default(Key))
+%% @equiv reg(Key, default(Key), [])
 %% @end
 %% @end
 reg(Key) ->
 reg(Key) ->
     ?CATCH_GPROC_ERROR(reg1(Key), [Key]).
     ?CATCH_GPROC_ERROR(reg1(Key), [Key]).
 
 
 reg1(Key) ->
 reg1(Key) ->
-    reg1(Key, default(Key)).
+    reg1(Key, default(Key), []).
 
 
 %% @spec reg_or_locate(Key::key()) -> {pid(), NewValue}
 %% @spec reg_or_locate(Key::key()) -> {pid(), NewValue}
 %%
 %%
@@ -635,7 +641,7 @@ await(Key) ->
 %% @spec await(Key::key(), Timeout) -> {pid(),Value}
 %% @spec await(Key::key(), Timeout) -> {pid(),Value}
 %%   Timeout = integer() | infinity
 %%   Timeout = integer() | infinity
 %%
 %%
-%% @doc Wait for a local name to be registered.
+%% @doc Wait for a name or aggregated counter to be registered.
 %% The function raises an exception if the timeout expires. Timeout must be
 %% The function raises an exception if the timeout expires. Timeout must be
 %% either an interger &gt; 0 or 'infinity'.
 %% either an interger &gt; 0 or 'infinity'.
 %% A small optimization: we first perform a lookup, to see if the name
 %% A small optimization: we first perform a lookup, to see if the name
@@ -650,7 +656,7 @@ await(Key, Timeout) ->
 %% @spec await(Node::node(), Key::key(), Timeout) -> {pid(),Value}
 %% @spec await(Node::node(), Key::key(), Timeout) -> {pid(),Value}
 %%   Timeout = integer() | infinity
 %%   Timeout = integer() | infinity
 %%
 %%
-%% @doc Wait for a local name to be registered on `Node'.
+%% @doc Wait for a name or aggregated counter to be registered on `Node'.
 %% This function works exactly like {@link await/2}, but queries a remote
 %% This function works exactly like {@link await/2}, but queries a remote
 %% node instead. An exception is thrown if `Node' cannot be reached. If gproc
 %% node instead. An exception is thrown if `Node' cannot be reached. If gproc
 %% is not running on a given node, this is treated the same as the node being
 %% is not running on a given node, this is treated the same as the node being
@@ -662,11 +668,11 @@ await(Node, Key, Timeout) when Node == node() ->
 await(Node, Key, Timeout) when is_atom(Node) ->
 await(Node, Key, Timeout) when is_atom(Node) ->
     ?CATCH_GPROC_ERROR(await1(Node, Key, Timeout), [Node, Key, Timeout]).
     ?CATCH_GPROC_ERROR(await1(Node, Key, Timeout), [Node, Key, Timeout]).
 
 
-await1({n,g,_} = Key, Timeout) ->
+await1({T,g,_} = Key, Timeout) when T=:=n; T=:=a; T=:=rc ->
     ?CHK_DIST,
     ?CHK_DIST,
     request_wait(Key, Timeout);
     request_wait(Key, Timeout);
-await1({n,l,_} = Key, Timeout) ->
-    case ets:lookup(?TAB, {Key, n}) of
+await1({T,l,_} = Key, Timeout) when T=:=n; T=:=a; T=:=rc ->
+    case ets:lookup(?TAB, {Key, T}) of
         [{_, Pid, Value}] ->
         [{_, Pid, Value}] ->
 	    case is_process_alive(Pid) of
 	    case is_process_alive(Pid) of
 		true ->
 		true ->
@@ -685,12 +691,12 @@ await1({n,l,_} = Key, Timeout) ->
             request_wait(Key, Timeout)
             request_wait(Key, Timeout)
     end;
     end;
 await1(_, _) ->
 await1(_, _) ->
-    throw(badarg).
+    ?THROW_GPROC_ERROR(badarg).
 
 
 await1(N, {n,l,_} = Key, Timeout) when is_atom(N) ->
 await1(N, {n,l,_} = Key, Timeout) when is_atom(N) ->
     request_wait(N, Key, Timeout);
     request_wait(N, Key, Timeout);
 await1(_, _, _) ->
 await1(_, _, _) ->
-    throw(badarg).
+    ?THROW_GPROC_ERROR(badarg).
 
 
 
 
 request_wait({_,g,_} = Key, Timeout) ->
 request_wait({_,g,_} = Key, Timeout) ->
@@ -698,7 +704,7 @@ request_wait({_,g,_} = Key, Timeout) ->
 request_wait(Key, Timeout) ->
 request_wait(Key, Timeout) ->
     request_wait(node(), Key, Timeout).
     request_wait(node(), Key, Timeout).
 
 
-request_wait(N, {n,C,_} = Key, Timeout) when C==l; C==g ->
+request_wait(N, {_,C,_} = Key, Timeout) when C==l; C==g ->
     TRef = case Timeout of
     TRef = case Timeout of
                infinity -> no_timer;
                infinity -> no_timer;
                T when is_integer(T), T > 0 ->
                T when is_integer(T), T > 0 ->
@@ -741,7 +747,7 @@ request_wait(N, {n,C,_} = Key, Timeout) when C==l; C==g ->
 wide_await(Nodes, Key, Timeout) ->
 wide_await(Nodes, Key, Timeout) ->
     ?CATCH_GPROC_ERROR(wide_await1(Nodes, Key, Timeout), [Nodes, Key, Timeout]).
     ?CATCH_GPROC_ERROR(wide_await1(Nodes, Key, Timeout), [Nodes, Key, Timeout]).
 
 
-wide_await1(Nodes, {n,l,_} = Key, Timeout) ->
+wide_await1(Nodes, {T,l,_} = Key, Timeout) when T=:=n; T=:=a ->
     {_, Ref} = spawn_monitor(fun() ->
     {_, Ref} = spawn_monitor(fun() ->
 				     wide_request_wait(Nodes, Key, Timeout)
 				     wide_request_wait(Nodes, Key, Timeout)
 			     end),
 			     end),
@@ -758,7 +764,7 @@ wide_await1(_, _, _) ->
     ?THROW_GPROC_ERROR(badarg).
     ?THROW_GPROC_ERROR(badarg).
 
 
 
 
-wide_request_wait(Nodes, {n,l,_} = Key, Timeout) ->
+wide_request_wait(Nodes, {Tk,l,_} = Key, Timeout) when Tk=:=n; Tk=:=a ->
         TRef = case Timeout of
         TRef = case Timeout of
                infinity -> no_timer;
                infinity -> no_timer;
                T when is_integer(T), T > 0 ->
                T when is_integer(T), T > 0 ->
@@ -771,8 +777,8 @@ wide_request_wait(Nodes, {n,l,_} = Key, Timeout) ->
 	     fun(Node) ->
 	     fun(Node) ->
 		     S = {?MODULE, Node},
 		     S = {?MODULE, Node},
 		     Ref = erlang:monitor(process, S),
 		     Ref = erlang:monitor(process, S),
-		     catch erlang:send(S, {'$gen_call', {self(), Ref}, Req},
-				       [noconnect]),
+		     ?MAY_FAIL(erlang:send(S, {'$gen_call', {self(), Ref}, Req},
+                                           [noconnect])),
 		     {Node, Ref}
 		     {Node, Ref}
 	     end, Nodes),
 	     end, Nodes),
     collect_replies(Refs, Key, TRef).
     collect_replies(Refs, Key, TRef).
@@ -798,7 +804,7 @@ collect_replies(Refs, Key, TRef) ->
 
 
 %% @spec nb_wait(Key::key()) -> Ref
 %% @spec nb_wait(Key::key()) -> Ref
 %%
 %%
-%% @doc Wait for a local name to be registered.
+%% @doc Wait for a name or aggregated counter to be registered.
 %% The caller can expect to receive a message,
 %% The caller can expect to receive a message,
 %% {gproc, Ref, registered, {Key, Pid, Value}}, once the name is registered.
 %% {gproc, Ref, registered, {Key, Pid, Value}}, once the name is registered.
 %% @end
 %% @end
@@ -808,23 +814,25 @@ nb_wait(Key) ->
 
 
 %% @spec nb_wait(Node::node(), Key::key()) -> Ref
 %% @spec nb_wait(Node::node(), Key::key()) -> Ref
 %%
 %%
-%% @doc Wait for a local name to be registered on `Node'.
+%% @doc Wait for a name or aggregated counter to be registered on `Node'.
 %% The caller can expect to receive a message,
 %% The caller can expect to receive a message,
 %% {gproc, Ref, registered, {Key, Pid, Value}}, once the name is registered.
 %% {gproc, Ref, registered, {Key, Pid, Value}}, once the name is registered.
 %% @end
 %% @end
 %%
 %%
-nb_wait(Node, {n,l,_} = Key) when is_atom(Node) ->
+nb_wait(Node, Key) ->
     ?CATCH_GPROC_ERROR(nb_wait1(Node, Key), [Node, Key]).
     ?CATCH_GPROC_ERROR(nb_wait1(Node, Key), [Node, Key]).
 
 
-nb_wait1({n,g,_} = Key) ->
+nb_wait1({T,g,_} = Key) when T=:=n; T=:=a; T=:=rc ->
     ?CHK_DIST,
     ?CHK_DIST,
     call({await, Key, self()}, g);
     call({await, Key, self()}, g);
-nb_wait1({n,l,_} = Key) ->
+nb_wait1({T,l,_} = Key) when T=:=n; T=:=a; T=:=rc ->
     call({await, Key, self()}, l);
     call({await, Key, self()}, l);
 nb_wait1(_) ->
 nb_wait1(_) ->
     ?THROW_GPROC_ERROR(badarg).
     ?THROW_GPROC_ERROR(badarg).
 
 
-nb_wait1(Node, {n,l,_} = Key) when is_atom(Node) ->
+nb_wait1(Node, {T,l,_} = Key) when is_atom(Node), T=:=n;
+                                   is_atom(Node), T=:=a;
+                                   is_atom(Node), T=:=rc ->
     call(Node, {await, Key, self()}, l).
     call(Node, {await, Key, self()}, l).
 
 
 
 
@@ -945,21 +953,65 @@ demonitor1(_, _) ->
 %%
 %%
 %%
 %%
 reg(Key, Value) ->
 reg(Key, Value) ->
-    ?CATCH_GPROC_ERROR(reg1(Key, Value), [Key, Value]).
+    ?CATCH_GPROC_ERROR(reg1(Key, Value, []), [Key, Value]).
+
+%% @spec reg(Key::key(), Value, Attrs::[{atom(),any()}]) -> true
+%%
+%% @doc Register a name or property for the current process
+%% `Attrs' (default: `[]') can be inspected using {@link get_attribute/2}.
+%%
+%% The structure of `Key' is `{Type, Context, Name}', where:
+%%
+%% * `Context :: l | g' - `l' means 'local' context; `g' means 'global'
+%% * `Type :: p | n | c | a | r | rc' specifies the type of entry
+%%
+%% The semantics of the different types:
+%%
+%% * `p' - 'property', is non-unique, i.e. different processes can each
+%%    register a property with the same name.
+%% * `n' - 'name, is unique within the given context (local or global).
+%% * `c' - 'counter', is similar to a property, but has a numeric value
+%%    and behaves roughly as an ets counter (see {@link update_counter/2}.)
+%% * `a' - 'aggregated counter', is automatically updated by gproc, and
+%%    reflects the sum of all counter objects with the same name in the given
+%%    scope. The initial value for an aggregated counter must be `undefined'.
+%% * `r' - 'resource property', behaves like a property, but can be tracked
+%%    with a 'resource counter'.
+%% * `rc' - 'resource counter', tracks the number of resource properties
+%%    with the same name. When the resource count reaches `0', any triggers
+%%    specified using an `on_zero' attribute may be executed (see below).
+%%
+%% On-zero triggers:
+%%
+%% `Msg = {gproc, resource_on_zero, Context, Name, Pid}'
+%%
+%% * `{send, Key}' - run `gproc:send(Key, Msg)'
+%% * `{bcast, Key}' - run `gproc:bcast(Key, Msg)'
+%% * `publish' - run
+%%  `gproc_ps:publish(Context, gproc_resource_on_zero, {Context, Name, Pid})'
+%% * `{unreg_shared, Type, Name}' - unregister the shared key
+%%  `{Type, Context, Name}'
+%% @end
+reg(Key, Value, Attrs) ->
+    ?CATCH_GPROC_ERROR(reg1(Key, Value, Attrs), [Key, Value, Attrs]).
 
 
-reg1({_,g,_} = Key, Value) ->
+reg1({T,g,_} = Key, Value, As) when T==p; T==a; T==c; T==n; T==r; T==rc ->
     %% anything global
     %% anything global
     ?CHK_DIST,
     ?CHK_DIST,
-    gproc_dist:reg(Key, Value);
-reg1({p,l,_} = Key, Value) ->
-    local_reg(Key, Value);
-reg1({a,l,_} = Key, undefined) ->
-    call({reg, Key, undefined});
-reg1({c,l,_} = Key, Value) when is_integer(Value) ->
-    call({reg, Key, Value});
-reg1({n,l,_} = Key, Value) ->
-    call({reg, Key, Value});
-reg1(_, _) ->
+    gproc_dist:reg(Key, Value, As);
+reg1({p,l,_} = Key, Value, As) ->
+    local_reg(Key, Value, As);
+reg1({a,l,_} = Key, undefined, As) ->
+    call({reg, Key, undefined, As});
+reg1({c,l,_} = Key, Value, As) when is_integer(Value) ->
+    call({reg, Key, Value, As});
+reg1({n,l,_} = Key, Value, As) ->
+    call({reg, Key, Value, As});
+reg1({r,l,_} = Key, Value, As) ->
+    call({reg, Key, Value, As});
+reg1({rc,l,_} = Key, Value, As) ->
+    call({reg, Key, Value, As});
+reg1(_, _, _) ->
     ?THROW_GPROC_ERROR(badarg).
     ?THROW_GPROC_ERROR(badarg).
 
 
 %% @spec reg_or_locate(Key::key(), Value) -> {pid(), NewValue}
 %% @spec reg_or_locate(Key::key(), Value) -> {pid(), NewValue}
@@ -991,7 +1043,7 @@ reg_or_locate({n,_,_} = Key, Value, F) when is_function(F, 0) ->
 reg_or_locate1({_,g,_} = Key, Value, P) ->
 reg_or_locate1({_,g,_} = Key, Value, P) ->
     ?CHK_DIST,
     ?CHK_DIST,
     gproc_dist:reg_or_locate(Key, Value, P);
     gproc_dist:reg_or_locate(Key, Value, P);
-reg_or_locate1({n,l,_} = Key, Value, P) ->
+reg_or_locate1({T,l,_} = Key, Value, P) when T==n; T==a; T==rc ->
     call({reg_or_locate, Key, Value, P});
     call({reg_or_locate, Key, Value, P});
 reg_or_locate1(_, _, _) ->
 reg_or_locate1(_, _, _) ->
     ?THROW_GPROC_ERROR(badarg).
     ?THROW_GPROC_ERROR(badarg).
@@ -1026,20 +1078,25 @@ reg_shared1({T,_,_} = Key) when T==a; T==p; T==c ->
 %% @end
 %% @end
 %%
 %%
 reg_shared(Key, Value) ->
 reg_shared(Key, Value) ->
-    ?CATCH_GPROC_ERROR(reg_shared1(Key, Value), [Key, Value]).
+    ?CATCH_GPROC_ERROR(reg_shared1(Key, Value, []), [Key, Value]).
+
+reg_shared(Key, Value, Attrs) when is_list(Attrs) ->
+    ?CATCH_GPROC_ERROR(reg_shared1(Key, Value, Attrs), [Key, Value, Attrs]).
 
 
 %% @private
 %% @private
-reg_shared1({_,g,_} = Key, Value) ->
+reg_shared1({_,g,_} = Key, Value, As) ->
     %% anything global
     %% anything global
     ?CHK_DIST,
     ?CHK_DIST,
-    gproc_dist:reg_shared(Key, Value);
-reg_shared1({a,l,_} = Key, undefined) ->
-    call({reg_shared, Key, undefined});
-reg_shared1({c,l,_} = Key, Value) when is_integer(Value) ->
-    call({reg_shared, Key, Value});
-reg_shared1({p,l,_} = Key, Value) ->
-    call({reg_shared, Key, Value});
-reg_shared1(_, _) ->
+    gproc_dist:reg_shared(Key, Value, As);
+reg_shared1({a,l,_} = Key, undefined, As) ->
+    call({reg_shared, Key, undefined, As});
+reg_shared1({c,l,_} = Key, Value, As) when is_integer(Value) ->
+    call({reg_shared, Key, Value, As});
+reg_shared1({p,l,_} = Key, Value, As) ->
+    call({reg_shared, Key, Value, As});
+reg_shared1({rc,l,_} = Key, undefined, As) ->
+    call({reg_shared, Key, undefined, As});
+reg_shared1(_, _, _) ->
     ?THROW_GPROC_ERROR(badarg).
     ?THROW_GPROC_ERROR(badarg).
 
 
 %% @spec mreg(type(), scope(), [{Key::any(), Value::any()}]) -> true
 %% @spec mreg(type(), scope(), [{Key::any(), Value::any()}]) -> true
@@ -1116,8 +1173,8 @@ unreg1(Key) ->
         {_, g, _} ->
         {_, g, _} ->
             ?CHK_DIST,
             ?CHK_DIST,
             gproc_dist:unreg(Key);
             gproc_dist:unreg(Key);
-        {T, l, _} when T == n;
-                       T == a -> call({unreg, Key});
+        {T, l, _} when T == n; T == a; T == r; T == rc ->
+            call({unreg, Key});
         {_, l, _} ->
         {_, l, _} ->
             case ets:member(?TAB, {Key,self()}) of
             case ets:member(?TAB, {Key,self()}) of
                 true ->
                 true ->
@@ -1165,7 +1222,8 @@ unreg_shared1(Key) ->
             gproc_dist:unreg_shared(Key);
             gproc_dist:unreg_shared(Key);
         {T, l, _} when T == c;
         {T, l, _} when T == c;
                        T == a;
                        T == a;
-		       T == p -> call({unreg_shared, Key});
+		       T == p;
+                       T == rc -> call({unreg_shared, Key});
         _ ->
         _ ->
 	    ?THROW_GPROC_ERROR(badarg)
 	    ?THROW_GPROC_ERROR(badarg)
     end.
     end.
@@ -1235,7 +1293,7 @@ select({?TAB, _, _, _, _, _, _, _} = Continuation) ->
 select(Pat) ->
 select(Pat) ->
     select(all, Pat).
     select(all, Pat).
 
 
-%% @spec (Context::context(), Pat::sel_pattern()) -> [{Key, Pid, Value}]
+%% @spec (Context::sel_context(), Pat::sel_pattern()) -> [{Key, Pid, Value}]
 %%
 %%
 %% @doc Perform a select operation with limited context on the process registry
 %% @doc Perform a select operation with limited context on the process registry
 %%
 %%
@@ -1285,10 +1343,15 @@ select_count(Context, Pat) ->
 %%% Local properties can be registered in the local process, since
 %%% Local properties can be registered in the local process, since
 %%% no other process can interfere.
 %%% no other process can interfere.
 %%%
 %%%
-local_reg(Key, Value) ->
+local_reg({_,Scope,_} = Key, Value, As) ->
     case gproc_lib:insert_reg(Key, Value, self(), l) of
     case gproc_lib:insert_reg(Key, Value, self(), l) of
         false -> ?THROW_GPROC_ERROR(badarg);
         false -> ?THROW_GPROC_ERROR(badarg);
-        true  -> monitor_me()
+        true  ->
+            monitor_me(),
+            if As =/= [] ->
+                    gproc_lib:insert_attr(Key, As, self(), Scope);
+               true -> true
+            end
     end.
     end.
 
 
 local_mreg(_, []) -> true;
 local_mreg(_, []) -> true;
@@ -1327,7 +1390,8 @@ set_value(Key, Value) ->
 %%
 %%
 set_value_shared({T,_,_} = Key, Value) when T == c;
 set_value_shared({T,_,_} = Key, Value) when T == c;
 					    T == a;
 					    T == a;
-					    T == p->
+					    T == p;
+                                            T == r ->
     ?CATCH_GPROC_ERROR(set_value_shared1(Key, Value), [Key, Value]).
     ?CATCH_GPROC_ERROR(set_value_shared1(Key, Value), [Key, Value]).
 
 
 set_value1({_,g,_} = Key, Value) ->
 set_value1({_,g,_} = Key, Value) ->
@@ -1386,7 +1450,7 @@ get_value(Key, Pid) ->
     ?CATCH_GPROC_ERROR(get_value1(Key, Pid), [Key, Pid]).
     ?CATCH_GPROC_ERROR(get_value1(Key, Pid), [Key, Pid]).
 
 
 get_value1({T,_,_} = Key, Pid) when is_pid(Pid) ->
 get_value1({T,_,_} = Key, Pid) when is_pid(Pid) ->
-    if T==n orelse T==a ->
+    if T==n; T==a; T==rc ->
             case ets:lookup(?TAB, {Key, T}) of
             case ets:lookup(?TAB, {Key, T}) of
                 [{_, P, Value}] when P == Pid -> Value;
                 [{_, P, Value}] when P == Pid -> Value;
                 _ -> ?THROW_GPROC_ERROR(badarg)
                 _ -> ?THROW_GPROC_ERROR(badarg)
@@ -1394,11 +1458,13 @@ get_value1({T,_,_} = Key, Pid) when is_pid(Pid) ->
        true ->
        true ->
             ets:lookup_element(?TAB, {Key, Pid}, 3)
             ets:lookup_element(?TAB, {Key, Pid}, 3)
     end;
     end;
-get_value1({T,_,_} = K, shared) when T==c; T==a; T==p ->
+get_value1({T,_,_} = K, shared) when T==c; T==a; T==p; T==r ->
     Key = case T of
     Key = case T of
-	      c -> {K, shared};
-	      p -> {K, shared};
-	      a -> {K, a}
+	      c  -> {K, shared};
+	      p  -> {K, shared};
+              r  -> {K, shared};
+	      a  -> {K, a};
+              rc -> {K, rc}
 	  end,
 	  end,
     case ets:lookup(?TAB, Key) of
     case ets:lookup(?TAB, Key) of
 	[{_, shared, Value}] -> Value;
 	[{_, shared, Value}] -> Value;
@@ -1417,9 +1483,9 @@ get_value1(_, _) ->
 %% @end
 %% @end
 get_attribute(Key, A) ->
 get_attribute(Key, A) ->
     Pid = case Key of
     Pid = case Key of
-	      {T,_,_} when T==n; T==a ->
+	      {T,_,_} when T==n; T==a; T==rc ->
 		  where(Key);
 		  where(Key);
-	      {T,_,_} when T==p; T==c ->
+	      {T,_,_} when T==p; T==c; T==r ->
 		  self()
 		  self()
 	  end,
 	  end,
     ?CATCH_GPROC_ERROR(get_attribute1(Key, Pid, A), [Key, A]).
     ?CATCH_GPROC_ERROR(get_attribute1(Key, Pid, A), [Key, A]).
@@ -1507,7 +1573,7 @@ lookup_pid({_T,_,_} = Key) ->
 %% This function raises a `badarg' exception if `Key' is not registered.
 %% This function raises a `badarg' exception if `Key' is not registered.
 %% @end
 %% @end
 lookup_value({T,_,_} = Key) ->
 lookup_value({T,_,_} = Key) ->
-    if T==n orelse T==a ->
+    if T==n orelse T==a orelse T==rc ->
             ets:lookup_element(?TAB, {Key,T}, 3);
             ets:lookup_element(?TAB, {Key,T}, 3);
        true ->
        true ->
             erlang:error(badarg)
             erlang:error(badarg)
@@ -1526,7 +1592,7 @@ where(Key) ->
     ?CATCH_GPROC_ERROR(where1(Key), [Key]).
     ?CATCH_GPROC_ERROR(where1(Key), [Key]).
 
 
 where1({T,_,_}=Key) ->
 where1({T,_,_}=Key) ->
-    if T==n orelse T==a ->
+    if T==n orelse T==a orelse T==rc ->
             case ets:lookup(?TAB, {Key,T}) of
             case ets:lookup(?TAB, {Key,T}) of
                 [{_, P, _Value}] ->
                 [{_, P, _Value}] ->
                     case my_is_process_alive(P) of
                     case my_is_process_alive(P) of
@@ -1558,7 +1624,7 @@ whereis_name(Key) ->
 %% @end
 %% @end
 %%
 %%
 lookup_pids({T,_,_} = Key) ->
 lookup_pids({T,_,_} = Key) ->
-    L = if T==n orelse T==a ->
+    L = if T==n orelse T==a orelse T==rc ->
                 ets:select(?TAB, [{{{Key,T}, '$1', '_'},
                 ets:select(?TAB, [{{{Key,T}, '$1', '_'},
 				   [{is_pid, '$1'}], ['$1']}]);
 				   [{is_pid, '$1'}], ['$1']}]);
            true ->
            true ->
@@ -1586,7 +1652,7 @@ my_is_process_alive(_) ->
 %% @end
 %% @end
 %%
 %%
 lookup_values({T,_,_} = Key) ->
 lookup_values({T,_,_} = Key) ->
-    L = if T==n orelse T==a ->
+    L = if T==n orelse T==a orelse T==rc ->
                 ets:select(?TAB, [{{{Key,T}, '$1', '$2'},[],[{{'$1','$2'}}]}]);
                 ets:select(?TAB, [{{{Key,T}, '$1', '$2'},[],[{{'$1','$2'}}]}]);
            true ->
            true ->
                 ets:select(?TAB, [{{{Key,'_'}, '$1', '$2'},[],[{{'$1','$2'}}]}])
                 ets:select(?TAB, [{{{Key,'_'}, '$1', '$2'},[],[{{'$1','$2'}}]}])
@@ -1793,14 +1859,14 @@ send(Key, Msg) ->
     ?CATCH_GPROC_ERROR(send1(Key, Msg), [Key, Msg]).
     ?CATCH_GPROC_ERROR(send1(Key, Msg), [Key, Msg]).
 
 
 send1({T,C,_} = Key, Msg) when C==l; C==g ->
 send1({T,C,_} = Key, Msg) when C==l; C==g ->
-    if T == n orelse T == a ->
+    if T == n orelse T == a orelse T == rc ->
             case ets:lookup(?TAB, {Key, T}) of
             case ets:lookup(?TAB, {Key, T}) of
                 [{_, Pid, _}] ->
                 [{_, Pid, _}] ->
                     Pid ! Msg;
                     Pid ! Msg;
                 _ ->
                 _ ->
                     ?THROW_GPROC_ERROR(badarg)
                     ?THROW_GPROC_ERROR(badarg)
             end;
             end;
-       T==p orelse T==c ->
+       T==p orelse T==c orelse T==r ->
             %% BUG - if the key part contains select wildcards, we may end up
             %% BUG - if the key part contains select wildcards, we may end up
             %% sending multiple messages to the same pid
             %% sending multiple messages to the same pid
             lists:foreach(fun(Pid) ->
             lists:foreach(fun(Pid) ->
@@ -1836,7 +1902,7 @@ bcast(Key, Msg) ->
 bcast(Ns, Key, Msg) ->
 bcast(Ns, Key, Msg) ->
     ?CATCH_GPROC_ERROR(bcast1(Ns, Key, Msg), [Key, Msg]).
     ?CATCH_GPROC_ERROR(bcast1(Ns, Key, Msg), [Key, Msg]).
 
 
-bcast1(Ns, {T,l,_} = Key, Msg) when T==p; T==a; T==c; T==n ->
+bcast1(Ns, {T,l,_} = Key, Msg) when T==p; T==a; T==c; T==n; T==r; T==rc ->
     send1(Key, Msg),
     send1(Key, Msg),
     gen_server:abcast(Ns -- [node()], gproc_bcast, {send, Key, Msg}),
     gen_server:abcast(Ns -- [node()], gproc_bcast, {send, Key, Msg}),
     Msg.
     Msg.
@@ -2056,10 +2122,14 @@ handle_cast({cancel_wait_or_monitor, Pid, {T,_,_} = Key}, S) ->
     {noreply, S}.
     {noreply, S}.
 
 
 %% @hidden
 %% @hidden
-handle_call({reg, {_T,l,_} = Key, Val}, {Pid,_}, S) ->
+handle_call({reg, {_T,l,_} = Key, Val, Attrs}, {Pid,_}, S) ->
     case try_insert_reg(Key, Val, Pid) of
     case try_insert_reg(Key, Val, Pid) of
         true ->
         true ->
             _ = gproc_lib:ensure_monitor(Pid,l),
             _ = gproc_lib:ensure_monitor(Pid,l),
+            _ = if Attrs =/= [] ->
+                        gproc_lib:insert_attr(Key, Attrs, Pid, l);
+                   true -> true
+                end,
             {reply, true, S};
             {reply, true, S};
         false ->
         false ->
             {reply, badarg, S}
             {reply, badarg, S}
@@ -2136,7 +2206,7 @@ handle_call({monitor, {T,l,_} = Key, Pid, Type}, _From, S)
 	end,
 	end,
     {reply, Ref, S};
     {reply, Ref, S};
 handle_call({demonitor, {T,l,_} = Key, Ref, Pid}, _From, S)
 handle_call({demonitor, {T,l,_} = Key, Ref, Pid}, _From, S)
-  when T==n; T==a ->
+  when T==n; T==a; T==rc ->
     _ = case where(Key) of
     _ = case where(Key) of
 	    undefined ->
 	    undefined ->
 		ok;  % be nice
 		ok;  % be nice
@@ -2150,10 +2220,13 @@ handle_call({demonitor, {T,l,_} = Key, Ref, Pid}, _From, S)
 		end
 		end
 	end,
 	end,
     {reply, ok, S};
     {reply, ok, S};
-handle_call({reg_shared, {_T,l,_} = Key, Val}, _From, S) ->
+handle_call({reg_shared, {_T,l,_} = Key, Val, Attrs}, _From, S) ->
     case try_insert_reg(Key, Val, shared) of
     case try_insert_reg(Key, Val, shared) of
-    %% case try_insert_shared(Key, Val) of
 	true ->
 	true ->
+            _ = if Attrs =/= [] ->
+                        gproc_lib:insert_attr(Key, Attrs, shared, l);
+                   true -> true
+                end,
 	    {reply, true, S};
 	    {reply, true, S};
 	false ->
 	false ->
 	    {reply, badarg, S}
 	    {reply, badarg, S}
@@ -2309,7 +2382,6 @@ try_insert_reg({T,l,_} = Key, Val, Pid) ->
             true
             true
     end.
     end.
 
 
-
 %% try_insert_shared({c,l,_} = Key, Val) ->
 %% try_insert_shared({c,l,_} = Key, Val) ->
 %%     ets:insert_new(?TAB, [{{Key,shared}, shared, Val}, {{shared, Key}, []}]);
 %%     ets:insert_new(?TAB, [{{Key,shared}, shared, Val}, {{shared, Key}, []}]);
 %% try_insert_shared({a,l,_} = Key, Val) ->
 %% try_insert_shared({a,l,_} = Key, Val) ->
@@ -2368,23 +2440,14 @@ process_is_down(Pid) when is_pid(Pid) ->
                       [{_, _, Value}] = ets:lookup(?TAB, Key),
                       [{_, _, Value}] = ets:lookup(?TAB, Key),
                       ets:delete(?TAB, Key),
                       ets:delete(?TAB, Key),
                       gproc_lib:update_aggr_counter(l, C, -Value);
                       gproc_lib:update_aggr_counter(l, C, -Value);
+                 ({{r,l,Rsrc} = K, _}) ->
+                      Key = {K, Pid},
+                      ets:delete(?TAB, Key),
+                      gproc_lib:decrement_resource_count(l, Rsrc);
+                 ({{rc,l,_} = K, R}) ->
+                      remove_aggregate(rc, K, R, Pid);
                  ({{a,l,_} = K, R}) ->
                  ({{a,l,_} = K, R}) ->
-                      case ets:lookup(?TAB, {K,a}) of
-                          [{_, Pid, V}] ->
-                              ets:delete(?TAB, {K,a}),
-                              opt_notify(R, K, Pid, V);
-                          [{_, OtherPid, _}] when Pid =/= OtherPid ->
-                              case ets:lookup(?TAB, {OtherPid, K}) of
-                                  [{RK, Opts}] when is_list(Opts) ->
-                                      Opts1 = gproc_lib:remove_monitor_pid(
-                                                Opts, Pid),
-                                      ets:insert(?TAB, {RK, Opts1});
-                                  _ ->
-                                      true
-                              end;
-                          [] ->
-                              opt_notify(R, K, Pid, undefined)
-                      end;
+                      remove_aggregate(a, K, R, Pid);
                  ({{p,_,_} = K, _}) ->
                  ({{p,_,_} = K, _}) ->
                       ets:delete(?TAB, {K, Pid})
                       ets:delete(?TAB, {K, Pid})
               end, Revs),
               end, Revs),
@@ -2393,6 +2456,24 @@ process_is_down(Pid) when is_pid(Pid) ->
             ok
             ok
     end.
     end.
 
 
+remove_aggregate(T, K, R, Pid) ->
+    case ets:lookup(?TAB, {K,T}) of
+        [{_, Pid, V}] ->
+            ets:delete(?TAB, {K,T}),
+            opt_notify(R, K, Pid, V);
+        [{_, OtherPid, _}] when Pid =/= OtherPid ->
+            case ets:lookup(?TAB, {OtherPid, K}) of
+                [{RK, Opts}] when is_list(Opts) ->
+                    Opts1 = gproc_lib:remove_monitor_pid(
+                              Opts, Pid),
+                    ets:insert(?TAB, {RK, Opts1});
+                _ ->
+                    true
+            end;
+        [] ->
+            opt_notify(R, K, Pid, undefined)
+    end.
+
 opt_notify(r, _, _, _) ->
 opt_notify(r, _, _, _) ->
     ok;
     ok;
 opt_notify(Opts, {T,_,_} = Key, Pid, Value) ->
 opt_notify(Opts, {T,_,_} = Key, Pid, Value) ->
@@ -2441,7 +2522,7 @@ pick_standby([]) ->
 
 
 
 
 
 
-do_give_away({T,l,_} = K, To, Pid) when T==n; T==a ->
+do_give_away({T,l,_} = K, To, Pid) when T==n; T==a; T==rc ->
     Key = {K, T},
     Key = {K, T},
     case ets:lookup(?TAB, Key) of
     case ets:lookup(?TAB, Key) of
         [{_, Pid, Value}] ->
         [{_, Pid, Value}] ->
@@ -2464,7 +2545,7 @@ do_give_away({T,l,_} = K, To, Pid) when T==n; T==a ->
         _ ->
         _ ->
             badarg
             badarg
     end;
     end;
-do_give_away({T,l,_} = K, To, Pid) when T==c; T==p ->
+do_give_away({T,l,_} = K, To, Pid) when T==c; T==p; T==r ->
     Key = {K, Pid},
     Key = {K, Pid},
     case ets:lookup(?TAB, Key) of
     case ets:lookup(?TAB, Key) of
         [{_, Pid, Value}] ->
         [{_, Pid, Value}] ->
@@ -2493,7 +2574,7 @@ do_give_away({T,l,_} = K, To, Pid) when T==c; T==p ->
 
 
 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;
     P;
-pid_to_give_away_to({T,l,_} = Key) when T==n; T==a ->
+pid_to_give_away_to({T,l,_} = Key) when T==n; T==a; T==rc ->
     case ets:lookup(?TAB, {Key, T}) of
     case ets:lookup(?TAB, {Key, T}) of
         [{_, Pid, _}] ->
         [{_, Pid, _}] ->
             Pid;
             Pid;
@@ -2583,7 +2664,7 @@ headpat(T, C, V1,V2,V3) ->
     Rf = fun(Pos) ->
     Rf = fun(Pos) ->
                  {element,Pos,{element,1,{element,1,'$_'}}}
                  {element,Pos,{element,1,{element,1,'$_'}}}
          end,
          end,
-    K2 = if T==n orelse T==a -> T;
+    K2 = if T==n orelse T==a orelse T==rc -> T;
             true -> '_'
             true -> '_'
          end,
          end,
     {Kp,Vars} = case V1 of
     {Kp,Vars} = case V1 of
@@ -2632,8 +2713,10 @@ type(all)   -> '_';
 type(T) when T==n; T==p; T==c; T==a -> T;
 type(T) when T==n; T==p; T==c; T==a -> T;
 type(names) -> n;
 type(names) -> n;
 type(props) -> p;
 type(props) -> p;
+type(resources) -> r;
 type(counters) -> c;
 type(counters) -> c;
-type(aggr_counters) -> a.
+type(aggr_counters) -> a;
+type(resource_counters) -> rc.
 
 
 rev_keypat(Context) ->
 rev_keypat(Context) ->
     {S,T} = get_s_t(Context),
     {S,T} = get_s_t(Context),

+ 3 - 1
src/gproc_bcast.erl

@@ -35,6 +35,8 @@
 	 terminate/2,
 	 terminate/2,
 	 code_change/3]).
 	 code_change/3]).
 
 
+-include("gproc_int.hrl").
+
 start_link() ->
 start_link() ->
     gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
     gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
 
 
@@ -45,7 +47,7 @@ handle_call(_, _, S) ->
     {reply, {error, unknown_call}, S}.
     {reply, {error, unknown_call}, S}.
 
 
 handle_cast({send, Key, Msg}, S) ->
 handle_cast({send, Key, Msg}, S) ->
-    catch gproc:send(Key, Msg),
+    ?MAY_FAIL(gproc:send(Key, Msg)),
     {noreply, S};
     {noreply, S};
 handle_cast(_, S) ->
 handle_cast(_, S) ->
     {noreply, S}.
     {noreply, S}.

+ 48 - 28
src/gproc_dist.erl

@@ -23,9 +23,9 @@
 -behaviour(gen_leader).
 -behaviour(gen_leader).
 
 
 -export([start_link/0, start_link/1,
 -export([start_link/0, start_link/1,
-         reg/1, reg/2, unreg/1,
+         reg/1, reg/3, unreg/1,
 	 reg_or_locate/3,
 	 reg_or_locate/3,
-	 reg_shared/2, unreg_shared/1,
+	 reg_shared/3, unreg_shared/1,
          monitor/2,
          monitor/2,
          demonitor/2,
          demonitor/2,
 	 set_attributes/2,
 	 set_attributes/2,
@@ -91,7 +91,7 @@ start_link({Nodes, Opts}) ->
 %% {@see gproc:reg/1}
 %% {@see gproc:reg/1}
 %%
 %%
 reg(Key) ->
 reg(Key) ->
-    reg(Key, gproc:default(Key)).
+    reg(Key, gproc:default(Key), []).
 
 
 %% {@see gproc:reg_or_locate/2}
 %% {@see gproc:reg_or_locate/2}
 %%
 %%
@@ -117,15 +117,15 @@ reg_or_locate(_, _, _) ->
 %%%          | a  - aggregated counter
 %%%          | a  - aggregated counter
 %%%    Scope = l | g (global or local)
 %%%    Scope = l | g (global or local)
 %%% @end
 %%% @end
-reg({_,g,_} = Key, Value) ->
+reg({_,g,_} = Key, Value, Attrs) ->
     %% anything global
     %% anything global
-    leader_call({reg, Key, Value, self()});
-reg(_, _) ->
+    leader_call({reg, Key, Value, self(), Attrs});
+reg(_, _, _) ->
     ?THROW_GPROC_ERROR(badarg).
     ?THROW_GPROC_ERROR(badarg).
 
 
-reg_shared({_,g,_} = Key, Value) ->
-    leader_call({reg, Key, Value, shared});
-reg_shared(_, _) ->
+reg_shared({_,g,_} = Key, Value, Attrs) ->
+    leader_call({reg, Key, Value, shared, Attrs});
+reg_shared(_, _, _) ->
     ?THROW_GPROC_ERROR(badarg).
     ?THROW_GPROC_ERROR(badarg).
 
 
 monitor({_,g,_} = Key, Type) when Type==info;
 monitor({_,g,_} = Key, Type) when Type==info;
@@ -255,6 +255,8 @@ handle_info({'DOWN', _MRef, process, Pid, _}, S) ->
     ets:delete(?TAB, {Pid, g}),
     ets:delete(?TAB, {Pid, g}),
     leader_cast({pid_is_DOWN, Pid}),
     leader_cast({pid_is_DOWN, Pid}),
     {ok, S};
     {ok, S};
+handle_info({gproc_unreg, Objs}, S) ->
+    {ok, [{delete, Objs}], S};
 handle_info(_, S) ->
 handle_info(_, S) ->
     {ok, S}.
     {ok, S}.
 
 
@@ -332,23 +334,17 @@ handle_leader_call(sync, From, #state{sync_requests = SReqs} = S, E) ->
             GenLeader:broadcast({from_leader, {sync, From}}, Alive, E),
             GenLeader:broadcast({from_leader, {sync, From}}, Alive, E),
             {noreply, S#state{sync_requests = [{From, Alive}|SReqs]}}
             {noreply, S#state{sync_requests = [{From, Alive}|SReqs]}}
     end;
     end;
-handle_leader_call({reg, {_C,g,_Name} = K, Value, Pid}, _From, S, _E) ->
+handle_leader_call({reg, {_C,g,_Name} = K, Value, Pid, As}, _From, S, _E) ->
     case gproc_lib:insert_reg(K, Value, Pid, g) of
     case gproc_lib:insert_reg(K, Value, Pid, g) of
         false ->
         false ->
             {reply, badarg, S};
             {reply, badarg, S};
         true ->
         true ->
             _ = gproc_lib:ensure_monitor(Pid,g),
             _ = gproc_lib:ensure_monitor(Pid,g),
+            _ = if As =/= [] ->
+                        gproc_lib:insert_attr(K, As, Pid, g);
+                   true -> []
+                end,
 	    Vals = mk_broadcast_insert_vals([{K, Pid, Value}]),
 	    Vals = mk_broadcast_insert_vals([{K, Pid, Value}]),
-            %% Vals =
-            %%     if C == a ->
-            %%             ets:lookup(?TAB, {K,a});
-            %%        C == c ->
-            %%             [{{K,Pid},Pid,Value} | ets:lookup(?TAB,{{a,g,Name},a})];
-            %%        C == n ->
-            %%             [{{K,n},Pid,Value}];
-            %%        true ->
-            %%             [{{K,Pid},Pid,Value}]
-            %%     end,
             {reply, true, [{insert, Vals}], S}
             {reply, true, [{insert, Vals}], S}
     end;
     end;
 handle_leader_call({monitor, {T,g,_} = K, MPid, Type}, _From, S, _E) when T==n;
 handle_leader_call({monitor, {T,g,_} = K, MPid, Type}, _From, S, _E) when T==n;
@@ -484,7 +480,7 @@ handle_leader_call({reset_counter, {c,g,_Ctr} = Key, Pid}, _From, S, _E) ->
 	    {reply, badarg, S}
 	    {reply, badarg, S}
     end;
     end;
 handle_leader_call({unreg, {T,g,Name} = K, Pid}, _From, S, _E) ->
 handle_leader_call({unreg, {T,g,Name} = K, Pid}, _From, S, _E) ->
-    Key = if T == n; T == a -> {K,T};
+    Key = if T == n; T == a; T == rc -> {K,T};
              true -> {K, Pid}
              true -> {K, Pid}
           end,
           end,
     case ets:member(?TAB, Key) of
     case ets:member(?TAB, Key) of
@@ -499,6 +495,14 @@ handle_leader_call({unreg, {T,g,Name} = K, Pid}, _From, S, _E) ->
                         [] ->
                         [] ->
                             {reply, true, [{delete, [Key, {Pid,K}]}], S}
                             {reply, true, [{delete, [Key, {Pid,K}]}], S}
                     end;
                     end;
+               T == r ->
+                    case ets:lookup(?TAB, {{rc,g,Name},rc}) of
+                        [RC] ->
+                            {reply, true, [{delete,[Key, {Pid,K}]},
+                                           {insert, [RC]}], S};
+                        [] ->
+                            {reply, true, [{delete, [Key, {Pid, K}]}], S}
+                    end;
                true ->
                true ->
                     {reply, true, [{notify, [{K, Pid, unreg}]},
                     {reply, true, [{notify, [{K, Pid, unreg}]},
                                    {delete, [Key, {Pid,K}]}], S}
                                    {delete, [Key, {Pid,K}]}], S}
@@ -507,7 +511,7 @@ handle_leader_call({unreg, {T,g,Name} = K, Pid}, _From, S, _E) ->
             {reply, badarg, S}
             {reply, badarg, S}
     end;
     end;
 handle_leader_call({give_away, {T,g,_} = K, To, Pid}, _From, S, _E)
 handle_leader_call({give_away, {T,g,_} = K, To, Pid}, _From, S, _E)
-  when T == a; T == n ->
+  when T == a; T == n; T == rc ->
     Key = {K, T},
     Key = {K, T},
     case ets:lookup(?TAB, Key) of
     case ets:lookup(?TAB, Key) of
         [{_, Pid, Value}] ->
         [{_, Pid, Value}] ->
@@ -537,7 +541,7 @@ handle_leader_call({give_away, {T,g,_} = K, To, Pid}, _From, S, _E)
             {reply, badarg, S}
             {reply, badarg, S}
     end;
     end;
 handle_leader_call({mreg, T, g, L, Pid}, _From, S, _E) ->
 handle_leader_call({mreg, T, g, L, Pid}, _From, S, _E) ->
-    if T==p; T==n ->
+    if T==p; T==n; T==r ->
             try gproc_lib:insert_many(T, g, L, Pid) of
             try gproc_lib:insert_many(T, g, L, Pid) of
                 {true,Objs} -> {reply, true, [{insert,Objs}], S};
                 {true,Objs} -> {reply, true, [{insert,Objs}], S};
                 false       -> {reply, badarg, S}
                 false       -> {reply, badarg, S}
@@ -655,11 +659,14 @@ handle_leader_cast({pid_is_DOWN, Pid}, S, _E) ->
 mk_broadcast_insert_vals(Objs) ->
 mk_broadcast_insert_vals(Objs) ->
     lists:flatmap(
     lists:flatmap(
       fun({{C, g, Name} = K, Pid, Value}) ->
       fun({{C, g, Name} = K, Pid, Value}) ->
-	      if C == a ->
-		      ets:lookup(?TAB, {K,a}) ++ ets:lookup(?TAB, {Pid,K});
+	      if C == a; C == rc ->
+		      ets:lookup(?TAB, {K,C}) ++ ets:lookup(?TAB, {Pid,K});
 		 C == c ->
 		 C == c ->
 		      [{{K,Pid},Pid,Value} | ets:lookup(?TAB,{{a,g,Name},a})]
 		      [{{K,Pid},Pid,Value} | ets:lookup(?TAB,{{a,g,Name},a})]
 			  ++ ets:lookup(?TAB, {Pid,K});
 			  ++ ets:lookup(?TAB, {Pid,K});
+                 C == r ->
+                      [{{K,Pid},Pid,Value} | ets:lookup(?TAB,{{rc,g,Name},rc})]
+                          ++ ets:lookup(?TAB, {Pid, K});
 		 C == n ->
 		 C == n ->
 		      [{{K,n},Pid,Value}| ets:lookup(?TAB, {Pid,K})];
 		      [{{K,n},Pid,Value}| ets:lookup(?TAB, {Pid,K})];
 		 true ->
 		 true ->
@@ -671,7 +678,7 @@ mk_broadcast_insert_vals(Objs) ->
 process_globals(Globals) ->
 process_globals(Globals) ->
     {Modified, Notifications} =
     {Modified, Notifications} =
         lists:foldl(
         lists:foldl(
-          fun({{T,_,_} = Key, Pid}, A) when T==n; T==a ->
+          fun({{T,_,_} = Key, Pid}, A) when T==n; T==a; T==rc ->
                   case ets:lookup(?TAB, {Pid,Key}) of
                   case ets:lookup(?TAB, {Pid,Key}) of
                       [{_, Opts}] when is_list(Opts) ->
                       [{_, Opts}] when is_list(Opts) ->
                           maybe_failover(Key, Pid, Opts, A);
                           maybe_failover(Key, Pid, Opts, A);
@@ -683,6 +690,8 @@ process_globals(Globals) ->
                             c ->
                             c ->
                                 Incr = ets:lookup_element(?TAB, {Key,Pid}, 3),
                                 Incr = ets:lookup_element(?TAB, {Key,Pid}, 3),
                                 update_aggr_counter(Key, -Incr) ++ MA;
                                 update_aggr_counter(Key, -Incr) ++ MA;
+                            r ->
+                                decrement_resource_count(Key, []) ++ MA;
                             _ ->
                             _ ->
                                MA
                                MA
                         end,
                         end,
@@ -813,7 +822,7 @@ do_notify([]) ->
     ok.
     ok.
 
 
 
 
-ets_key({T,_,_} = K, _) when T==n; T==a ->
+ets_key({T,_,_} = K, _) when T==n; T==a; T==rc ->
     {K, T};
     {K, T};
 ets_key(K, Pid) ->
 ets_key(K, Pid) ->
     {K, Pid}.
     {K, Pid}.
@@ -951,6 +960,17 @@ update_aggr_counter({c,g,Ctr}, Incr, Acc) ->
             [New|Acc]
             [New|Acc]
     end.
     end.
 
 
+decrement_resource_count({r,g,Rsrc}, Acc) ->
+    Key = {{rc,g,Rsrc},rc},
+    case ets:member(?TAB, Key) of
+        false ->
+            Acc;
+        true ->
+            %% Call the lib function, which might trigger events
+            gproc_lib:decrement_resource_count(g, Rsrc),
+            ets:lookup(?TAB, Key) ++ Acc
+    end.
+
 pid_to_give_away_to(P) when is_pid(P) ->
 pid_to_give_away_to(P) when is_pid(P) ->
     P;
     P;
 pid_to_give_away_to({T,g,_} = Key) when T==n; T==a ->
 pid_to_give_away_to({T,g,_} = Key) when T==n; T==a ->
@@ -962,7 +982,7 @@ pid_to_give_away_to({T,g,_} = Key) when T==n; T==a ->
     end.
     end.
 
 
 insert_reg([{_, Waiters}], K, Val, Pid, Event) ->
 insert_reg([{_, Waiters}], K, Val, Pid, Event) ->
-    gproc_lib:insert_reg(K, Val, Pid, g, []),
+    gproc_lib:insert_reg(K, Val, Pid, g),
     tell_waiters(Waiters, K, Pid, Val, Event).
     tell_waiters(Waiters, K, Pid, Val, Event).
 
 
 tell_waiters([{P,R}|T], K, Pid, V, Event) ->
 tell_waiters([{P,R}|T], K, Pid, V, Event) ->

+ 4 - 0
src/gproc_int.hrl

@@ -25,3 +25,7 @@
 	end).
 	end).
 
 
 -define(THROW_GPROC_ERROR(E), throw({gproc_error, E})).
 -define(THROW_GPROC_ERROR(E), throw({gproc_error, E})).
+
+%% Used to wrap operations that may fail, but we ignore the exception.
+%% Use instead of catch, to avoid building a stacktrace unnecessarily.
+-define(MAY_FAIL(Expr), try (Expr) catch _:_ -> '$caught_exception' end).

+ 125 - 35
src/gproc_lib.erl

@@ -42,6 +42,7 @@
 	 remove_wait/4,
 	 remove_wait/4,
          update_aggr_counter/3,
          update_aggr_counter/3,
          update_counter/3,
          update_counter/3,
+         decrement_resource_count/2,
 	 valid_opts/2]).
 	 valid_opts/2]).
 
 
 -export([dbg/1]).
 -export([dbg/1]).
@@ -62,30 +63,21 @@ dbg(Mods) ->
 %% Pid around as payload as well. This is a bit redundant, but
 %% Pid around as payload as well. This is a bit redundant, but
 %% symmetric.
 %% symmetric.
 %%
 %%
--spec insert_reg(key(), any(), pid() | shared, scope()) -> boolean().
+-spec insert_reg(gproc:key(), any(), pid() | shared, gproc:scope()) -> boolean().
 insert_reg(K, Value, Pid, Scope) ->
 insert_reg(K, Value, Pid, Scope) ->
     insert_reg(K, Value, Pid, Scope, registered).
     insert_reg(K, Value, Pid, Scope, registered).
 
 
-insert_reg({T,_,Name} = K, Value, Pid, Scope, Event) when T==a; T==n ->
-    MaybeScan = fun() ->
-                        if T==a ->
-                                Initial = scan_existing_counters(Scope, Name),
-                                ets:insert(?TAB, {{K,a}, Pid, Initial});
-                           true ->
-                                true
-                        end
-                end,
-    case ets:insert_new(?TAB, {{K,T}, Pid, Value}) of
-        true ->
-            _ = ets:insert_new(?TAB, {{Pid,K}, []}),
-            MaybeScan();
-        false ->
-            if T==n; T==a ->
-                    maybe_waiters(K, Pid, Value, T, Event);
-               true ->
-                    false
-            end
-    end;
+insert_reg({T,_,Name} = K, Value, Pid, Scope, Event) when T==a; T==n; T==rc ->
+    Res = case ets:insert_new(?TAB, {{K,T}, Pid, Value}) of
+              true ->
+                  %% Use insert_new to avoid overwriting existing entry
+                  _ = ets:insert_new(?TAB, {{Pid,K}, []}),
+                  true;
+              false ->
+                  maybe_waiters(K, Pid, Value, T, Event)
+          end,
+    maybe_scan(T, Pid, Scope, Name, K),
+    Res;
 insert_reg({p,Scope,_} = K, Value, shared, Scope, _E)
 insert_reg({p,Scope,_} = K, Value, shared, Scope, _E)
   when Scope == g; Scope == l ->
   when Scope == g; Scope == l ->
     %% shared properties are unique
     %% shared properties are unique
@@ -103,12 +95,31 @@ insert_reg({c,Scope,Ctr} = Key, Value, Pid, Scope, _E) when Scope==l; Scope==g -
             ignore
             ignore
     end,
     end,
     Res;
     Res;
+insert_reg({r,Scope,R} = Key, Value, Pid, Scope, _E) when Scope==l; Scope==g ->
+    K = {Key, Pid},
+    Kr = {Pid, Key},
+    Res = ets:insert_new(?TAB, [{K, Pid, Value}, {Kr, [{initial, Value}]}]),
+    case Res of
+        true ->
+            update_resource_count(Scope, R, 1);
+        false ->
+            ignore
+    end,
+    Res;
 insert_reg({_,_,_} = Key, Value, Pid, _Scope, _E) when is_pid(Pid) ->
 insert_reg({_,_,_} = Key, Value, Pid, _Scope, _E) when is_pid(Pid) ->
     %% Non-unique keys; store Pid in the key part
     %% Non-unique keys; store Pid in the key part
     K = {Key, Pid},
     K = {Key, Pid},
     Kr = {Pid, Key},
     Kr = {Pid, Key},
     ets:insert_new(?TAB, [{K, Pid, Value}, {Kr, []}]).
     ets:insert_new(?TAB, [{K, Pid, Value}, {Kr, []}]).
 
 
+maybe_scan(a, Pid, Scope, Name, K) ->
+    Initial = scan_existing_counters(Scope, Name),
+    ets:insert(?TAB, {{K,a}, Pid, Initial});
+maybe_scan(rc, Pid, Scope, Name, K) ->
+    Initial = scan_existing_resources(Scope, Name),
+    ets:insert(?TAB, {{K,rc}, Pid, Initial});
+maybe_scan(_, _, _, _, _) ->
+    true.
 
 
 insert_attr({_,Scope,_} = Key, Attrs, Pid, Scope) when Scope==l;
 insert_attr({_,Scope,_} = Key, Attrs, Pid, Scope) when Scope==l;
 						       Scope==g ->
 						       Scope==g ->
@@ -125,7 +136,25 @@ insert_attr({_,Scope,_} = Key, Attrs, Pid, Scope) when Scope==l;
 	    false
 	    false
     end.
     end.
 
 
--spec insert_many(type(), scope(), [{key(),any()}], pid()) ->
+get_attr(Attr, Pid, {_,_,_} = Key, Default) ->
+    case ets:lookup(?TAB, {Pid, Key}) of
+        [{_, Opts}] when is_list(Opts) ->
+            case lists:keyfind(attrs, 1, Opts) of
+                {_, Attrs} ->
+                    case lists:keyfind(Attr, 1, Attrs) of
+                        {_, Val} ->
+                            Val;
+                        _ ->
+                            Default
+                    end;
+                _ ->
+                    Default
+            end;
+        _ ->
+            Default
+    end.
+
+-spec insert_many(gproc:type(), gproc:scope(), [{gproc:key(),any()}], pid()) ->
           {true,list()} | false.
           {true,list()} | false.
 
 
 insert_many(T, Scope, KVL, Pid) ->
 insert_many(T, Scope, KVL, Pid) ->
@@ -155,7 +184,7 @@ insert_many(T, Scope, KVL, Pid) ->
             end
             end
     end.
     end.
 
 
--spec insert_objects([{key(), pid(), any()}]) -> ok.
+-spec insert_objects([{gproc:key(), pid(), any()}]) -> ok.
 
 
 insert_objects(Objs) ->
 insert_objects(Objs) ->
     lists:foreach(
     lists:foreach(
@@ -211,7 +240,7 @@ maybe_waiters(K, Pid, Value, T, Event) ->
             false
             false
     end.
     end.
 
 
--spec notify_waiters([{pid(), reference()}], key(), pid(), any(), any()) -> ok.
+-spec notify_waiters([{pid(), reference()}], gproc:key(), pid(), any(), any()) -> ok.
 notify_waiters([{P, Ref}|T], K, Pid, V, E) ->
 notify_waiters([{P, Ref}|T], K, Pid, V, E) ->
     P ! {gproc, Ref, registered, {K, Pid, V}},
     P ! {gproc, Ref, registered, {K, Pid, V}},
     notify_waiters(T, K, Pid, V, E);
     notify_waiters(T, K, Pid, V, E);
@@ -274,7 +303,7 @@ remove_monitors(Key, Pid, MPid) ->
     end.
     end.
 
 
 
 
-mk_reg_objs(T, Scope, Pid, L) when T==n; T==a ->
+mk_reg_objs(T, Scope, Pid, L) when T==n; T==a; T==rc ->
     lists:map(fun({K,V}) ->
     lists:map(fun({K,V}) ->
                       {{{T,Scope,K},T}, Pid, V};
                       {{{T,Scope,K},T}, Pid, V};
                  (_) ->
                  (_) ->
@@ -422,11 +451,11 @@ unreg_opts(Key, Pid) ->
 remove_reg_1({c,_,_} = Key, Pid) ->
 remove_reg_1({c,_,_} = Key, Pid) ->
     remove_counter_1(Key, ets:lookup_element(?TAB, Reg = {Key,Pid}, 3), Pid),
     remove_counter_1(Key, ets:lookup_element(?TAB, Reg = {Key,Pid}, 3), Pid),
     Reg;
     Reg;
-remove_reg_1({a,_,_} = Key, _Pid) ->
-    ets:delete(?TAB, Reg = {Key,a}),
+remove_reg_1({r,_,_} = Key, Pid) ->
+    remove_resource_1(Key, ets:lookup_element(?TAB, Reg = {Key,Pid}, 3), Pid),
     Reg;
     Reg;
-remove_reg_1({n,_,_} = Key, _Pid) ->
-    ets:delete(?TAB, Reg = {Key,n}),
+remove_reg_1({T,_,_} = Key, _Pid) when T==a; T==n; T==rc ->
+    ets:delete(?TAB, Reg = {Key,T}),
     Reg;
     Reg;
 remove_reg_1({_,_,_} = Key, Pid) ->
 remove_reg_1({_,_,_} = Key, Pid) ->
     ets:delete(?TAB, Reg = {Key, Pid}),
     ets:delete(?TAB, Reg = {Key, Pid}),
@@ -437,18 +466,23 @@ remove_counter_1({c,C,N} = Key, Val, Pid) ->
     update_aggr_counter(C, N, -Val),
     update_aggr_counter(C, N, -Val),
     Res.
     Res.
 
 
+remove_resource_1({r,C,N} = Key, _, Pid) ->
+    Res = ets:delete(?TAB, {Key, Pid}),
+    update_resource_count(C, N, -1),
+    Res.
+
 do_set_value({T,_,_} = Key, Value, Pid) ->
 do_set_value({T,_,_} = Key, Value, Pid) ->
     K2 = if Pid == shared -> shared;
     K2 = if Pid == shared -> shared;
-	    T==n orelse T==a -> T;
+	    T==n orelse T==a orelse T==rc -> T;
 	    true -> Pid
 	    true -> Pid
          end,
          end,
-    case (catch ets:lookup_element(?TAB, {Key,K2}, 2)) of
-        {'EXIT', {badarg, _}} ->
-            false;
+    try ets:lookup_element(?TAB, {Key,K2}, 2) of
         Pid ->
         Pid ->
             ets:insert(?TAB, {{Key, K2}, Pid, Value});
             ets:insert(?TAB, {{Key, K2}, Pid, Value});
         _ ->
         _ ->
             false
             false
+    catch
+        error:_ -> false
     end.
     end.
 
 
 do_set_counter_value({_,C,N} = Key, Value, Pid) ->
 do_set_counter_value({_,C,N} = Key, Value, Pid) ->
@@ -478,6 +512,7 @@ update_counter({T,l,Ctr} = Key, {Incr, Threshold, SetValue}, Pid)
     end,
     end,
     New;
     New;
 update_counter({T,l,Ctr} = Key, Ops, Pid) when is_list(Ops), T==c;
 update_counter({T,l,Ctr} = Key, Ops, Pid) when is_list(Ops), T==c;
+                                               is_list(Ops), T==r;
 					       is_list(Ops), T==n ->
 					       is_list(Ops), T==n ->
     case ets:update_counter(?TAB, {Key, Pid},
     case ets:update_counter(?TAB, {Key, Pid},
 			    [{3, 0} | expand_ops(Ops)]) of
 			    [{3, 0} | expand_ops(Ops)]) of
@@ -505,18 +540,73 @@ expand_ops([]) ->
 expand_ops(_) ->
 expand_ops(_) ->
     ?THROW_GPROC_ERROR(badarg).
     ?THROW_GPROC_ERROR(badarg).
 
 
+update_aggr_counter(C, N, Val) ->
+    ?MAY_FAIL(ets:update_counter(?TAB, {{a,C,N},a}, {3, Val})).
 
 
+decrement_resource_count(C, N) ->
+    update_resource_count(C, N, -1).
 
 
+update_resource_count(C, N, Val) ->
+    try ets:update_counter(?TAB, {{rc,C,N},rc}, {3, Val}) of
+        0 ->
+            resource_count_zero(C, N);
+        _ ->
+            ok
+    catch
+        _:_ -> ok
+    end.
 
 
+resource_count_zero(C, N) ->
+    case ets:lookup(?TAB, {K = {rc,C,N},rc}) of
+        [{_, Pid, _}] ->
+            case get_attr(on_zero, Pid, K, undefined) of
+                undefined -> ok;
+                Actions ->
+                    perform_on_zero(Actions, C, N, Pid)
+            end;
+        _ -> ok
+    end.
 
 
-update_aggr_counter(C, N, Val) ->
-    catch ets:update_counter(?TAB, {{a,C,N},a}, {3, Val}).
+perform_on_zero(Actions, C, N, Pid) ->
+    lists:foreach(
+      fun(A) ->
+              try perform_on_zero_(A, C, N, Pid)
+              catch error:_ -> ignore
+              end
+      end, Actions).
+
+perform_on_zero_({send, ToProc}, C, N, Pid) ->
+    gproc:send(ToProc, {gproc, resource_on_zero, C, N, Pid}),
+    ok;
+perform_on_zero_({bcast, ToProc}, C, N, Pid) ->
+    gproc:bcast(ToProc, {gproc, resource_on_zero, C, N, Pid}),
+    ok;
+perform_on_zero_(publish, C, N, Pid) ->
+    gproc_ps:publish(C, gproc_resource_on_zero, {C, N, Pid}),
+    ok;
+perform_on_zero_({unreg_shared, T,N}, C, _, _) ->
+    K = {T, C, N},
+    case ets:member(?TAB, {K, shared}) of
+        true ->
+            Objs = remove_reg(K, shared, unreg),
+            _ = if C == g -> self() ! {gproc_unreg, Objs};
+                   true   -> ok
+                end,
+            ok;
+        false ->
+            ok
+    end;
+perform_on_zero_(_, _, _, _) ->
+    ok.
 
 
 scan_existing_counters(Ctxt, Name) ->
 scan_existing_counters(Ctxt, Name) ->
     Head = {{{c,Ctxt,Name},'_'},'_','$1'},
     Head = {{{c,Ctxt,Name},'_'},'_','$1'},
     Cs = ets:select(?TAB, [{Head, [], ['$1']}]),
     Cs = ets:select(?TAB, [{Head, [], ['$1']}]),
     lists:sum(Cs).
     lists:sum(Cs).
 
 
+scan_existing_resources(Ctxt, Name) ->
+    Head = {{{r,Ctxt,Name},'_'},'_','_'},
+    ets:select_count(?TAB, [{Head, [], [true]}]).
 
 
 valid_opts(Type, Default) ->
 valid_opts(Type, Default) ->
     Opts = get_app_env(Type, Default),
     Opts = get_app_env(Type, Default),

+ 102 - 3
test/gproc_dist_tests.erl

@@ -61,6 +61,18 @@ dist_test_() ->
                                        ?debugVal(t_aggr_counter(Ns))
                                        ?debugVal(t_aggr_counter(Ns))
                                end,
                                end,
                                fun() ->
                                fun() ->
+                                       ?debugVal(t_awaited_aggr_counter(Ns))
+                               end,
+                               fun() ->
+                                       ?debugVal(t_simple_resource_count(Ns))
+                               end,
+                               fun() ->
+                                       ?debugVal(t_awaited_resource_count(Ns))
+                               end,
+                               fun() ->
+                                       ?debugVal(t_resource_count_on_zero(Ns))
+                               end,
+                               fun() ->
                                        ?debugVal(t_update_counters(Ns))
                                        ?debugVal(t_update_counters(Ns))
                                end,
                                end,
                                fun() ->
                                fun() ->
@@ -127,6 +139,7 @@ run_dist_tests() ->
 -define(T_NAME, {n, g, {?MODULE, ?LINE, erlang:now()}}).
 -define(T_NAME, {n, g, {?MODULE, ?LINE, erlang:now()}}).
 -define(T_KVL, [{foo, "foo"}, {bar, "bar"}]).
 -define(T_KVL, [{foo, "foo"}, {bar, "bar"}]).
 -define(T_COUNTER, {c, g, {?MODULE, ?LINE}}).
 -define(T_COUNTER, {c, g, {?MODULE, ?LINE}}).
+-define(T_RESOURCE, {r, g, {?MODULE, ?LINE}}).
 -define(T_PROP, {p, g, ?MODULE}).
 -define(T_PROP, {p, g, ?MODULE}).
 
 
 t_simple_reg([H|_] = Ns) ->
 t_simple_reg([H|_] = Ns) ->
@@ -201,6 +214,92 @@ t_aggr_counter([H1,H2|_] = Ns) ->
     ?assertMatch(ok, t_call(Pc2, die)),
     ?assertMatch(ok, t_call(Pc2, die)),
     ?assertMatch(ok, t_call(Pa, die)).
     ?assertMatch(ok, t_call(Pa, die)).
 
 
+t_awaited_aggr_counter([H1,H2|_] = Ns) ->
+    {c,g,Nm} = Ctr = ?T_COUNTER,
+    Aggr = {a,g,Nm},
+    Pc1 = t_spawn_reg(H1, Ctr, 3),
+    P = t_spawn(H2),
+    Ref = erlang:monitor(process, P),
+    P ! {self(), Ref, {apply, gproc, await, [Aggr]}},
+    t_sleep(),
+    P1 = t_spawn_reg(H2, Aggr),
+    ?assert(P1 == receive
+                      {P, Ref, Res} ->
+                          element(1, Res);
+                      {'DOWN', Ref, _, _, Reason} ->
+                          erlang:error(Reason);
+                      Other ->
+                          erlang:error({received, Other})
+                  end),
+    ?assertMatch(ok, t_read_everywhere(Aggr, P1, Ns, 3)),
+    ?assertMatch(ok, t_call(Pc1, die)),
+    ?assertMatch(ok, t_call(P, die)),
+    flush_down(Ref),
+    ?assertMatch(ok, t_call(P1, die)).
+
+t_simple_resource_count([H1,H2|_] = Ns) ->
+    {r,g,Nm} = R = ?T_RESOURCE,
+    RC = {rc,g,Nm},
+    Pr1 = t_spawn_reg(H1, R, 3),
+    Prc = t_spawn_reg(H2, RC),
+    ?assertMatch(ok, t_read_everywhere(R, Pr1, Ns, 3)),
+    ?assertMatch(ok, t_read_everywhere(RC, Prc, Ns, 1)),
+    Pr2 = t_spawn_reg(H2, R, 4),
+    ?assertMatch(ok, t_read_everywhere(R, Pr2, Ns, 4)),
+    ?assertMatch(ok, t_read_everywhere(RC, Prc, Ns, 2)),
+    ?assertMatch(ok, t_call(Pr1, die)),
+    ?assertMatch(ok, t_read_everywhere(RC, Prc, Ns, 1)),
+    ?assertMatch(ok, t_call(Pr2, die)),
+    ?assertMatch(ok, t_call(Prc, die)).
+
+t_awaited_resource_count([H1,H2|_] = Ns) ->
+    {r,g,Nm} = R = ?T_RESOURCE,
+    RC = {rc,g,Nm},
+    Pr1 = t_spawn_reg(H1, R, 3),
+    P = t_spawn(H2),
+    Ref = erlang:monitor(process, P),
+    P ! {self(), Ref, {apply, gproc, await, [RC]}},
+    t_sleep(),
+    P1 = t_spawn_reg(H2, RC),
+    ?assert(P1 == receive
+                      {P, Ref, Res} ->
+                          element(1, Res);
+                      {'DOWN', Ref, _, _, Reason} ->
+                          erlang:error(Reason);
+                      Other ->
+                          erlang:error({received, Other})
+                  end),
+    ?assertMatch(ok, t_read_everywhere(RC, P1, Ns, 1)),
+    ?assertMatch(ok, t_call(Pr1, die)),
+    ?assertMatch(ok, t_call(P, die)),
+    flush_down(Ref),
+    ?assertMatch(ok, t_call(P1, die)).
+
+t_resource_count_on_zero([H1,H2|_] = Ns) ->
+    {r,g,Nm} = R = ?T_RESOURCE,
+    Prop = ?T_PROP,
+    RC = {rc,g,Nm},
+    Pr1 = t_spawn_reg(H1, R, 3),
+    Pp = t_spawn_reg(H2, Prop),
+    ?assertMatch(ok, t_call(Pp, {selective, true})),
+    Prc = t_spawn_reg(H2, RC, undefined, [{on_zero, [{send, Prop}]}]),
+    ?assertMatch(ok, t_read_everywhere(R, Pr1, Ns, 3)),
+    ?assertMatch(ok, t_read_everywhere(RC, Prc, Ns, 1)),
+    ?assertMatch(ok, t_call(Pr1, die)),
+    ?assertMatch(ok, t_read_everywhere(RC, Prc, Ns, 0)),
+    ?assertMatch({gproc, resource_on_zero, g, Nm, Prc},
+                 t_call(Pp, {apply_fun, fun() ->
+                                                receive
+                                                    {gproc, _, _, _, _} = M ->
+                                                        M
+                                                after 10000 ->
+                                                        timeout
+                                                end
+                                        end})),
+    ?assertMatch(ok, t_call(Pp, {selective, false})),
+    ?assertMatch(ok, t_call(Pp, die)),
+    ?assertMatch(ok, t_call(Prc, die)).
+
 t_update_counters([H1,H2|_] = Ns) ->
 t_update_counters([H1,H2|_] = Ns) ->
     {c,g,N1} = C1 = ?T_COUNTER,
     {c,g,N1} = C1 = ?T_COUNTER,
     A1 = {a,g,N1},
     A1 = {a,g,N1},
@@ -213,7 +312,6 @@ t_update_counters([H1,H2|_] = Ns) ->
     ?assertMatch(ok, t_read_everywhere(C1, P12, Ns, 2)),
     ?assertMatch(ok, t_read_everywhere(C1, P12, Ns, 2)),
     ?assertMatch(ok, t_read_everywhere(C2, P2, Ns, 1)),
     ?assertMatch(ok, t_read_everywhere(C2, P2, Ns, 1)),
     ?assertMatch(ok, t_read_everywhere(A1, Pa1, Ns, 4)),
     ?assertMatch(ok, t_read_everywhere(A1, Pa1, Ns, 4)),
-    ?debugFmt("code:which(gproc_dist) = ~p~n", [code:which(gproc_dist)]),
     ?assertMatch([{C1,P1, 3},
     ?assertMatch([{C1,P1, 3},
 		  {C1,P12,4},
 		  {C1,P12,4},
 		  {C2,P2, 0}], t_call(P1, {apply, gproc, update_counters,
 		  {C2,P2, 0}], t_call(P1, {apply, gproc, update_counters,
@@ -347,12 +445,12 @@ t_standby_monitor([A,B|_] = Ns) ->
     ?assertMatch({gproc,unreg,Ref1,Na}, got_msg(Pc, gproc)),
     ?assertMatch({gproc,unreg,Ref1,Na}, got_msg(Pc, gproc)),
     ?assertMatch(ok, t_lookup_everywhere(Na, Ns, undefined)).
     ?assertMatch(ok, t_lookup_everywhere(Na, Ns, undefined)).
 
 
-t_follow_monitor([A,B|_] = Ns) ->
+t_follow_monitor([A,B|_]) ->
     Na = ?T_NAME,
     Na = ?T_NAME,
     Pa = t_spawn(A, _Selective = true),
     Pa = t_spawn(A, _Selective = true),
     Ref = t_call(Pa, {apply, gproc, monitor, [Na, follow]}),
     Ref = t_call(Pa, {apply, gproc, monitor, [Na, follow]}),
     {gproc,unreg,Ref,Na} = got_msg(Pa),
     {gproc,unreg,Ref,Na} = got_msg(Pa),
-    Pb = t_spawn_reg(A, Na),
+    Pb = t_spawn_reg(B, Na),
     {gproc,registered,Ref,Na} = got_msg(Pa),
     {gproc,registered,Ref,Na} = got_msg(Pa),
     ok = t_call(Pb, die),
     ok = t_call(Pb, die),
     ok = t_call(Pa, die).
     ok = t_call(Pa, die).
@@ -468,6 +566,7 @@ t_spawn(Node, Selective) -> gproc_test_lib:t_spawn(Node, Selective).
 t_spawn_mreg(Node, KVL) -> gproc_test_lib:t_spawn_mreg(Node, KVL).
 t_spawn_mreg(Node, KVL) -> gproc_test_lib:t_spawn_mreg(Node, KVL).
 t_spawn_reg(Node, N) -> gproc_test_lib:t_spawn_reg(Node, N).
 t_spawn_reg(Node, N) -> gproc_test_lib:t_spawn_reg(Node, N).
 t_spawn_reg(Node, N, V) -> gproc_test_lib:t_spawn_reg(Node, N, V).
 t_spawn_reg(Node, N, V) -> gproc_test_lib:t_spawn_reg(Node, N, V).
+t_spawn_reg(Node, N, V, As) -> gproc_test_lib:t_spawn_reg(Node, N, V, As).
 t_spawn_reg_shared(Node, N, V) -> gproc_test_lib:t_spawn_reg_shared(Node, N, V).
 t_spawn_reg_shared(Node, N, V) -> gproc_test_lib:t_spawn_reg_shared(Node, N, V).
 got_msg(P) -> gproc_test_lib:got_msg(P).
 got_msg(P) -> gproc_test_lib:got_msg(P).
 got_msg(P, Tag) -> gproc_test_lib:got_msg(P, Tag).
 got_msg(P, Tag) -> gproc_test_lib:got_msg(P, Tag).

+ 14 - 1
test/gproc_test_lib.erl

@@ -1,7 +1,7 @@
 -module(gproc_test_lib).
 -module(gproc_test_lib).
 
 
 -export([t_spawn/1, t_spawn/2,
 -export([t_spawn/1, t_spawn/2,
-         t_spawn_reg/2, t_spawn_reg/3,
+         t_spawn_reg/2, t_spawn_reg/3, t_spawn_reg/4,
          t_spawn_reg_shared/3,
          t_spawn_reg_shared/3,
          t_spawn_mreg/2,
          t_spawn_mreg/2,
          t_call/2,
          t_call/2,
@@ -42,6 +42,19 @@ t_spawn_reg(Node, Name, Value) ->
             erlang:error({timeout, t_spawn_reg, [Node, Name, Value]})
             erlang:error({timeout, t_spawn_reg, [Node, Name, Value]})
     end.
     end.
 
 
+t_spawn_reg(Node, Name, Value, Attrs) ->
+    Me = self(),
+    P = spawn(Node, fun() ->
+                            ?assertMatch(true, gproc:reg(Name, Value, Attrs)),
+                            Me ! {self(), ok},
+                            t_loop()
+                    end),
+    receive
+	{P, ok} -> P
+    after 1000 ->
+            erlang:error({timeout, t_spawn_reg, [Node, Name, Value]})
+    end.
+
 t_spawn_mreg(Node, KVL) ->
 t_spawn_mreg(Node, KVL) ->
     Me = self(),
     Me = self(),
     P = spawn(Node, fun() ->
     P = spawn(Node, fun() ->

+ 67 - 0
test/gproc_tests.erl

@@ -84,6 +84,14 @@ reg_test_() ->
       , ?_test(t_is_clean())
       , ?_test(t_is_clean())
       , {spawn, ?_test(?debugVal(t_simple_aggr_counter()))}
       , {spawn, ?_test(?debugVal(t_simple_aggr_counter()))}
       , ?_test(t_is_clean())
       , ?_test(t_is_clean())
+      , {spawn, ?_test(?debugVal(t_awaited_aggr_counter()))}
+      , ?_test(t_is_clean())
+      , {spawn, ?_test(?debugVal(t_simple_resource_count()))}
+      , ?_test(t_is_clean())
+      , {spawn, ?_test(?debugVal(t_awaited_resource_count()))}
+      , ?_test(t_is_clean())
+      , {spawn, ?_test(?debugVal(t_resource_count_on_zero_send()))}
+      , ?_test(t_is_clean())
       , {spawn, ?_test(?debugVal(t_update_counters()))}
       , {spawn, ?_test(?debugVal(t_update_counters()))}
       , ?_test(t_is_clean())
       , ?_test(t_is_clean())
       , {spawn, ?_test(?debugVal(t_simple_prop()))}
       , {spawn, ?_test(?debugVal(t_simple_prop()))}
@@ -229,6 +237,65 @@ t_simple_aggr_counter() ->
     end,
     end,
     ?assert(gproc:get_value({a,l,c1}) =:= 7).
     ?assert(gproc:get_value({a,l,c1}) =:= 7).
 
 
+t_awaited_aggr_counter() ->
+    ?assert(gproc:reg({c,l,c1}, 3) =:= true),
+    gproc:nb_wait({a,l,c1}),
+    ?assert(gproc:reg({a,l,c1}) =:= true),
+    receive {gproc,_,registered,{{a,l,c1},_,_}} -> ok
+    after 1000 ->
+            error(timeout)
+    end,
+    ?assertMatch(3, gproc:get_value({a,l,c1})).
+
+t_simple_resource_count() ->
+    ?assert(gproc:reg({r,l,r1}, 1) =:= true),
+    ?assert(gproc:reg({rc,l,r1}) =:= true),
+    ?assert(gproc:get_value({rc,l,r1}) =:= 1),
+    P = self(),
+    P1 = spawn_link(fun() ->
+                            gproc:reg({r,l,r1}, 1),
+                            P ! {self(), ok},
+                            receive
+                                {P, goodbye} -> ok
+                            end
+                    end),
+    receive {P1, ok} -> ok end,
+    ?assert(gproc:get_value({rc,l,r1}) =:= 2),
+    P1 ! {self(), goodbye},
+    R = erlang:monitor(process, P1),
+    receive {'DOWN', R, _, _, _} ->
+            gproc:audit_process(P1)
+    end,
+    ?assert(gproc:get_value({rc,l,r1}) =:= 1).
+
+t_awaited_resource_count() ->
+    ?assert(gproc:reg({r,l,r1}, 3) =:= true),
+    ?assert(gproc:reg({r,l,r2}, 3) =:= true),
+    ?assert(gproc:reg({r,l,r3}, 3) =:= true),
+    gproc:nb_wait({rc,l,r1}),
+    ?assert(gproc:reg({rc,l,r1}) =:= true),
+    receive {gproc,_,registered,{{rc,l,r1},_,_}} -> ok
+    after 1000 ->
+            error(timeout)
+    end,
+    ?assertMatch(1, gproc:get_value({rc,l,r1})).
+
+t_resource_count_on_zero_send() ->
+    Me = self(),
+    ?assertMatch(true, gproc:reg({p,l,myp})),
+    ?assertMatch(true, gproc:reg({r,l,r1})),
+    ?assertMatch(true, gproc:reg({rc,l,r1}, 1, [{on_zero,
+                                                 [{send, {p,l,myp}}]}])),
+    ?assertMatch(1, gproc:get_value({rc,l,r1})),
+    ?assertMatch(true, gproc:unreg({r,l,r1})),
+    ?assertMatch(0, gproc:get_value({rc,l,r1})),
+    receive
+        {gproc, resource_on_zero, l, r1, Me} ->
+            ok
+    after 1000 ->
+            error(timeout)
+    end.
+
 t_update_counters() ->
 t_update_counters() ->
     ?assert(gproc:reg({c,l,c1}, 3) =:= true),
     ?assert(gproc:reg({c,l,c1}, 3) =:= true),
     ?assert(gproc:reg({a,l,c1}) =:= true),
     ?assert(gproc:reg({a,l,c1}) =:= true),