Browse Source

Added gproc:munreg/3

Ulf Wiger 14 years ago
parent
commit
30bf9073ef
7 changed files with 126 additions and 30 deletions
  1. 23 2
      doc/gproc.md
  2. 11 1
      doc/gproc_dist.md
  3. 49 4
      src/gproc.erl
  4. 15 0
      src/gproc_dist.erl
  5. 17 18
      src/gproc_lib.erl
  6. 8 3
      test/gproc_dist_tests.erl
  7. 3 2
      test/gproc_tests.erl

+ 23 - 2
doc/gproc.md

@@ -163,7 +163,7 @@ Type and scope for select(), qlc() and stepping:
 
 
 
-<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#add_global_aggr_counter-1">add_global_aggr_counter/1</a></td><td>Registers a global (unique) aggregated counter.</td></tr><tr><td valign="top"><a href="#add_global_counter-2">add_global_counter/2</a></td><td>Registers a global (non-unique) counter.</td></tr><tr><td valign="top"><a href="#add_global_name-1">add_global_name/1</a></td><td>Registers a global (unique) name.</td></tr><tr><td valign="top"><a href="#add_global_property-2">add_global_property/2</a></td><td>Registers a global (non-unique) property.</td></tr><tr><td valign="top"><a href="#add_local_aggr_counter-1">add_local_aggr_counter/1</a></td><td>Registers a local (unique) aggregated counter.</td></tr><tr><td valign="top"><a href="#add_local_counter-2">add_local_counter/2</a></td><td>Registers a local (non-unique) counter.</td></tr><tr><td valign="top"><a href="#add_local_name-1">add_local_name/1</a></td><td>Registers a local (unique) name.</td></tr><tr><td valign="top"><a href="#add_local_property-2">add_local_property/2</a></td><td>Registers a local (non-unique) property.</td></tr><tr><td valign="top"><a href="#audit_process-1">audit_process/1</a></td><td></td></tr><tr><td valign="top"><a href="#await-1">await/1</a></td><td>Equivalent to <a href="#await-2"><tt>await(Key, infinity)</tt></a>.</td></tr><tr><td valign="top"><a href="#await-2">await/2</a></td><td>Wait for a local name to be registered.</td></tr><tr><td valign="top"><a href="#cancel_wait-2">cancel_wait/2</a></td><td></td></tr><tr><td valign="top"><a href="#default-1">default/1</a></td><td></td></tr><tr><td valign="top"><a href="#first-1">first/1</a></td><td>Behaves as ets:first(Tab) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#get_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>Fetch 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>Read the value stored with a key registered to the current process.</td></tr><tr><td valign="top"><a href="#give_away-2">give_away/2</a></td><td>Atomically transfers the key <code>From</code> to the process identified by <code>To</code>.</td></tr><tr><td valign="top"><a href="#info-1">info/1</a></td><td>Similar to <code>process_info(Pid)</code> but with additional gproc info.</td></tr><tr><td valign="top"><a href="#info-2">info/2</a></td><td>Similar to process_info(Pid, Item), but with additional gproc info.</td></tr><tr><td valign="top"><a href="#last-1">last/1</a></td><td>Behaves as ets:last(Tab) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#lookup_global_aggr_counter-1">lookup_global_aggr_counter/1</a></td><td>Lookup a global (unique) aggregated counter and returns its value.</td></tr><tr><td valign="top"><a href="#lookup_global_counters-1">lookup_global_counters/1</a></td><td>Look up all global (non-unique) instances of a given Counter.</td></tr><tr><td valign="top"><a href="#lookup_global_name-1">lookup_global_name/1</a></td><td>Lookup a global unique name.</td></tr><tr><td valign="top"><a href="#lookup_global_properties-1">lookup_global_properties/1</a></td><td>Look up all global (non-unique) instances of a given Property.</td></tr><tr><td valign="top"><a href="#lookup_local_aggr_counter-1">lookup_local_aggr_counter/1</a></td><td>Lookup a local (unique) aggregated counter and returns its value.</td></tr><tr><td valign="top"><a href="#lookup_local_counters-1">lookup_local_counters/1</a></td><td>Look up all local (non-unique) instances of a given Counter.</td></tr><tr><td valign="top"><a href="#lookup_local_name-1">lookup_local_name/1</a></td><td>Lookup a local unique name.</td></tr><tr><td valign="top"><a href="#lookup_local_properties-1">lookup_local_properties/1</a></td><td>Look up all local (non-unique) instances of a given Property.</td></tr><tr><td valign="top"><a href="#lookup_pid-1">lookup_pid/1</a></td><td>Lookup the Pid stored with a key.</td></tr><tr><td valign="top"><a href="#lookup_pids-1">lookup_pids/1</a></td><td>Returns a list of pids with the published key Key.</td></tr><tr><td valign="top"><a href="#lookup_value-1">lookup_value/1</a></td><td>Lookup the value stored with a key.</td></tr><tr><td valign="top"><a href="#lookup_values-1">lookup_values/1</a></td><td>Retrieve the <code>{Pid,Value}</code> pairs corresponding to Key.</td></tr><tr><td valign="top"><a href="#mreg-3">mreg/3</a></td><td>Register multiple {Key,Value} pairs of a given type and scope.</td></tr><tr><td valign="top"><a href="#nb_wait-1">nb_wait/1</a></td><td>Wait for a local name to be registered.</td></tr><tr><td valign="top"><a href="#next-2">next/2</a></td><td>Behaves as ets:next(Tab,Key) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#prev-2">prev/2</a></td><td>Behaves as ets:prev(Tab,Key) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#reg-1">reg/1</a></td><td>Equivalent to <a href="#reg-2"><tt>reg(Key, default(Key))</tt></a>.</td></tr><tr><td valign="top"><a href="#reg-2">reg/2</a></td><td>Register a name or property for the current process.</td></tr><tr><td valign="top"><a href="#select-1">select/1</a></td><td>Equivalent to <a href="#select-2"><tt>select(all, Pat)</tt></a>.</td></tr><tr><td valign="top"><a href="#select-2">select/2</a></td><td>Perform a select operation on the process registry.</td></tr><tr><td valign="top"><a href="#select-3">select/3</a></td><td>Like <a href="#select-2"><code>select/2</code></a> but returns Limit objects at a time.</td></tr><tr><td valign="top"><a href="#send-2">send/2</a></td><td>Sends a message to the process, or processes, corresponding to Key.</td></tr><tr><td valign="top"><a href="#set_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 registeration entry given by Key.</td></tr><tr><td valign="top"><a href="#start_link-0">start_link/0</a></td><td>Starts the gproc server.</td></tr><tr><td valign="top"><a href="#table-1">table/1</a></td><td>Equivalent to <a href="#table-2"><tt>table(Context, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#table-2">table/2</a></td><td>QLC table generator for the gproc registry.</td></tr><tr><td valign="top"><a href="#unreg-1">unreg/1</a></td><td>Unregister a name or property.</td></tr><tr><td valign="top"><a href="#unregister_name-1">unregister_name/1</a></td><td>Equivalent to <tt>unreg / 1</tt>.</td></tr><tr><td valign="top"><a href="#update_counter-2">update_counter/2</a></td><td>Updates the counter registered as Key for the current process.</td></tr><tr><td valign="top"><a href="#where-1">where/1</a></td><td>Returns the pid registered as Key.</td></tr><tr><td valign="top"><a href="#whereis_name-1">whereis_name/1</a></td><td>Equivalent to <tt>where / 1</tt>.</td></tr></table>
+<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#add_global_aggr_counter-1">add_global_aggr_counter/1</a></td><td>Registers a global (unique) aggregated counter.</td></tr><tr><td valign="top"><a href="#add_global_counter-2">add_global_counter/2</a></td><td>Registers a global (non-unique) counter.</td></tr><tr><td valign="top"><a href="#add_global_name-1">add_global_name/1</a></td><td>Registers a global (unique) name.</td></tr><tr><td valign="top"><a href="#add_global_property-2">add_global_property/2</a></td><td>Registers a global (non-unique) property.</td></tr><tr><td valign="top"><a href="#add_local_aggr_counter-1">add_local_aggr_counter/1</a></td><td>Registers a local (unique) aggregated counter.</td></tr><tr><td valign="top"><a href="#add_local_counter-2">add_local_counter/2</a></td><td>Registers a local (non-unique) counter.</td></tr><tr><td valign="top"><a href="#add_local_name-1">add_local_name/1</a></td><td>Registers a local (unique) name.</td></tr><tr><td valign="top"><a href="#add_local_property-2">add_local_property/2</a></td><td>Registers a local (non-unique) property.</td></tr><tr><td valign="top"><a href="#audit_process-1">audit_process/1</a></td><td></td></tr><tr><td valign="top"><a href="#await-1">await/1</a></td><td>Equivalent to <a href="#await-2"><tt>await(Key, infinity)</tt></a>.</td></tr><tr><td valign="top"><a href="#await-2">await/2</a></td><td>Wait for a local name to be registered.</td></tr><tr><td valign="top"><a href="#cancel_wait-2">cancel_wait/2</a></td><td></td></tr><tr><td valign="top"><a href="#default-1">default/1</a></td><td></td></tr><tr><td valign="top"><a href="#first-1">first/1</a></td><td>Behaves as ets:first(Tab) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#get_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>Fetch 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>Read the value stored with a key registered to the current process.</td></tr><tr><td valign="top"><a href="#give_away-2">give_away/2</a></td><td>Atomically transfers the key <code>From</code> to the process identified by <code>To</code>.</td></tr><tr><td valign="top"><a href="#info-1">info/1</a></td><td>Similar to <code>process_info(Pid)</code> but with additional gproc info.</td></tr><tr><td valign="top"><a href="#info-2">info/2</a></td><td>Similar to process_info(Pid, Item), but with additional gproc info.</td></tr><tr><td valign="top"><a href="#last-1">last/1</a></td><td>Behaves as ets:last(Tab) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#lookup_global_aggr_counter-1">lookup_global_aggr_counter/1</a></td><td>Lookup a global (unique) aggregated counter and returns its value.</td></tr><tr><td valign="top"><a href="#lookup_global_counters-1">lookup_global_counters/1</a></td><td>Look up all global (non-unique) instances of a given Counter.</td></tr><tr><td valign="top"><a href="#lookup_global_name-1">lookup_global_name/1</a></td><td>Lookup a global unique name.</td></tr><tr><td valign="top"><a href="#lookup_global_properties-1">lookup_global_properties/1</a></td><td>Look up all global (non-unique) instances of a given Property.</td></tr><tr><td valign="top"><a href="#lookup_local_aggr_counter-1">lookup_local_aggr_counter/1</a></td><td>Lookup a local (unique) aggregated counter and returns its value.</td></tr><tr><td valign="top"><a href="#lookup_local_counters-1">lookup_local_counters/1</a></td><td>Look up all local (non-unique) instances of a given Counter.</td></tr><tr><td valign="top"><a href="#lookup_local_name-1">lookup_local_name/1</a></td><td>Lookup a local unique name.</td></tr><tr><td valign="top"><a href="#lookup_local_properties-1">lookup_local_properties/1</a></td><td>Look up all local (non-unique) instances of a given Property.</td></tr><tr><td valign="top"><a href="#lookup_pid-1">lookup_pid/1</a></td><td>Lookup the Pid stored with a key.</td></tr><tr><td valign="top"><a href="#lookup_pids-1">lookup_pids/1</a></td><td>Returns a list of pids with the published key Key.</td></tr><tr><td valign="top"><a href="#lookup_value-1">lookup_value/1</a></td><td>Lookup the value stored with a key.</td></tr><tr><td valign="top"><a href="#lookup_values-1">lookup_values/1</a></td><td>Retrieve the <code>{Pid,Value}</code> pairs corresponding to Key.</td></tr><tr><td valign="top"><a href="#mreg-3">mreg/3</a></td><td>Register multiple {Key,Value} pairs of a given type and scope.</td></tr><tr><td valign="top"><a href="#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="#next-2">next/2</a></td><td>Behaves as ets:next(Tab,Key) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#prev-2">prev/2</a></td><td>Behaves as ets:prev(Tab,Key) for a given type of registration object.</td></tr><tr><td valign="top"><a href="#reg-1">reg/1</a></td><td>Equivalent to <a href="#reg-2"><tt>reg(Key, default(Key))</tt></a>.</td></tr><tr><td valign="top"><a href="#reg-2">reg/2</a></td><td>Register a name or property for the current process.</td></tr><tr><td valign="top"><a href="#select-1">select/1</a></td><td>Equivalent to <a href="#select-2"><tt>select(all, Pat)</tt></a>.</td></tr><tr><td valign="top"><a href="#select-2">select/2</a></td><td>Perform a select operation on the process registry.</td></tr><tr><td valign="top"><a href="#select-3">select/3</a></td><td>Like <a href="#select-2"><code>select/2</code></a> but returns Limit objects at a time.</td></tr><tr><td valign="top"><a href="#send-2">send/2</a></td><td>Sends a message to the process, or processes, corresponding to Key.</td></tr><tr><td valign="top"><a href="#set_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 registeration entry given by Key.</td></tr><tr><td valign="top"><a href="#start_link-0">start_link/0</a></td><td>Starts the gproc server.</td></tr><tr><td valign="top"><a href="#table-1">table/1</a></td><td>Equivalent to <a href="#table-2"><tt>table(Context, [])</tt></a>.</td></tr><tr><td valign="top"><a href="#table-2">table/2</a></td><td>QLC table generator for the gproc registry.</td></tr><tr><td valign="top"><a href="#unreg-1">unreg/1</a></td><td>Unregister a name or property.</td></tr><tr><td valign="top"><a href="#unregister_name-1">unregister_name/1</a></td><td>Equivalent to <tt>unreg / 1</tt>.</td></tr><tr><td valign="top"><a href="#update_counter-2">update_counter/2</a></td><td>Updates the counter registered as Key for the current process.</td></tr><tr><td valign="top"><a href="#where-1">where/1</a></td><td>Returns the pid registered as Key.</td></tr><tr><td valign="top"><a href="#whereis_name-1">whereis_name/1</a></td><td>Equivalent to <tt>where / 1</tt>.</td></tr></table>
 
 
 
@@ -800,7 +800,28 @@ object, the return value can be a list of any length.<a name="mreg-3"></a>
 
 Register multiple {Key,Value} pairs of a given type and scope.
 
-This function is more efficient than calling [`reg/2`](#reg-2) repeatedly.<a name="nb_wait-1"></a>
+This function is more efficient than calling [`reg/2`](#reg-2) repeatedly.
+It is also atomic in regard to unique names; either all names are registered
+or none are.<a name="munreg-3"></a>
+
+<h3>munreg/3</h3>
+
+
+
+
+
+<pre>munreg(T::<a href="#type-type">type()</a>, X2::<a href="#type-scope">scope()</a>, L::[Key::any()]) -> true</pre>
+<br></br>
+
+
+
+
+
+
+Unregister multiple Key items of a given type and scope.
+
+This function is usually more efficient than calling [`unreg/1`](#unreg-1)
+repeatedly.<a name="nb_wait-1"></a>
 
 <h3>nb_wait/1</h3>
 

+ 11 - 1
doc/gproc_dist.md

@@ -30,7 +30,7 @@ For a detailed description, see gproc/doc/erlang07-wiger.pdf.
 
 
 
-<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#code_change-4">code_change/4</a></td><td></td></tr><tr><td valign="top"><a href="#elected-2">elected/2</a></td><td></td></tr><tr><td valign="top"><a href="#elected-3">elected/3</a></td><td></td></tr><tr><td valign="top"><a href="#from_leader-3">from_leader/3</a></td><td></td></tr><tr><td valign="top"><a href="#get_leader-0">get_leader/0</a></td><td>Returns the node of the current gproc leader.</td></tr><tr><td valign="top"><a href="#give_away-2">give_away/2</a></td><td></td></tr><tr><td valign="top"><a href="#handle_DOWN-3">handle_DOWN/3</a></td><td></td></tr><tr><td valign="top"><a href="#handle_call-4">handle_call/4</a></td><td></td></tr><tr><td valign="top"><a href="#handle_cast-3">handle_cast/3</a></td><td></td></tr><tr><td valign="top"><a href="#handle_info-2">handle_info/2</a></td><td></td></tr><tr><td valign="top"><a href="#handle_leader_call-4">handle_leader_call/4</a></td><td></td></tr><tr><td valign="top"><a href="#handle_leader_cast-3">handle_leader_cast/3</a></td><td></td></tr><tr><td valign="top"><a href="#init-1">init/1</a></td><td></td></tr><tr><td valign="top"><a href="#leader_call-1">leader_call/1</a></td><td></td></tr><tr><td valign="top"><a href="#leader_cast-1">leader_cast/1</a></td><td></td></tr><tr><td valign="top"><a href="#mreg-2">mreg/2</a></td><td></td></tr><tr><td valign="top"><a href="#reg-1">reg/1</a></td><td></td></tr><tr><td valign="top"><a href="#reg-2">reg/2</a></td><td>
+<table width="100%" border="1" cellspacing="0" cellpadding="2" summary="function index"><tr><td valign="top"><a href="#code_change-4">code_change/4</a></td><td></td></tr><tr><td valign="top"><a href="#elected-2">elected/2</a></td><td></td></tr><tr><td valign="top"><a href="#elected-3">elected/3</a></td><td></td></tr><tr><td valign="top"><a href="#from_leader-3">from_leader/3</a></td><td></td></tr><tr><td valign="top"><a href="#get_leader-0">get_leader/0</a></td><td>Returns the node of the current gproc leader.</td></tr><tr><td valign="top"><a href="#give_away-2">give_away/2</a></td><td></td></tr><tr><td valign="top"><a href="#handle_DOWN-3">handle_DOWN/3</a></td><td></td></tr><tr><td valign="top"><a href="#handle_call-4">handle_call/4</a></td><td></td></tr><tr><td valign="top"><a href="#handle_cast-3">handle_cast/3</a></td><td></td></tr><tr><td valign="top"><a href="#handle_info-2">handle_info/2</a></td><td></td></tr><tr><td valign="top"><a href="#handle_leader_call-4">handle_leader_call/4</a></td><td></td></tr><tr><td valign="top"><a href="#handle_leader_cast-3">handle_leader_cast/3</a></td><td></td></tr><tr><td valign="top"><a href="#init-1">init/1</a></td><td></td></tr><tr><td valign="top"><a href="#leader_call-1">leader_call/1</a></td><td></td></tr><tr><td valign="top"><a href="#leader_cast-1">leader_cast/1</a></td><td></td></tr><tr><td valign="top"><a href="#mreg-2">mreg/2</a></td><td></td></tr><tr><td valign="top"><a href="#munreg-2">munreg/2</a></td><td></td></tr><tr><td valign="top"><a href="#reg-1">reg/1</a></td><td></td></tr><tr><td valign="top"><a href="#reg-2">reg/2</a></td><td>
 Class = n  - unique name
 | p  - non-unique property
 | c  - counter
@@ -207,6 +207,16 @@ Returns the node of the current gproc leader.<a name="give_away-2"></a>
 
 `mreg(T, KVL) -> any()`
 
+<a name="munreg-2"></a>
+
+<h3>munreg/2</h3>
+
+
+
+
+
+`munreg(T, Keys) -> any()`
+
 <a name="reg-1"></a>
 
 <h3>reg/1</h3>

+ 49 - 4
src/gproc.erl

@@ -50,6 +50,7 @@
 -export([start_link/0,
          reg/1, reg/2, unreg/1,
          mreg/3,
+	 munreg/3,
          set_value/2,
          get_value/1,
          where/1,
@@ -653,6 +654,8 @@ reg(_, _) ->
 %% @doc Register multiple {Key,Value} pairs of a given type and scope.
 %% 
 %% This function is more efficient than calling {@link reg/2} repeatedly.
+%% It is also atomic in regard to unique names; either all names are registered
+%% or none are.
 %% @end
 mreg(T, g, KVL) ->
     ?CHK_DIST,
@@ -668,6 +671,40 @@ mreg(p, l, KVL) ->
 mreg(_, _, _) ->
     erlang:error(badarg).
 
+%% @spec munreg(type(), scope(), [Key::any()]) -> true
+%%
+%% @doc Unregister multiple Key items of a given type and scope.
+%% 
+%% This function is usually more efficient than calling {@link unreg/1}
+%% repeatedly.
+%% @end
+munreg(T, g, L) ->
+    ?CHK_DIST,
+    gproc_dist:munreg(T, existing(T,g,L));
+munreg(T, l, L) when T==a; T==n ->
+    if is_list(L) ->
+            call({munreg, T, l, existing(T,l,L)});
+       true ->
+            erlang:error(badarg)
+    end;
+munreg(p, l, L) ->
+    local_munreg(p, existing(p,l,L));
+munreg(_, _, _) ->
+    erlang:error(badarg).
+
+existing(T,Scope,L) ->
+    Keys = if T==p; T==c ->
+		   [{{T,Scope,K}, self()} || K <- L];
+	      T==a; T==n ->
+		   [{{T,Scope,K}, T} || K <- L]
+	   end,
+    _ = [case ets:member(?TAB, K) of
+	     false -> erlang:error(badarg);
+	     true  -> true
+	 end || K <- Keys],
+    L.
+
+
 %% @spec (Key:: key()) -> true
 %%
 %% @doc Unregister a name or property.
@@ -682,7 +719,8 @@ unreg(Key) ->
         {_, l, _} ->
             case ets:member(?TAB, {Key,self()}) of
                 true ->
-                    gproc_lib:remove_reg(Key, self());
+                    _ = gproc_lib:remove_reg(Key, self()),
+		    true;
                 false ->
                     erlang:error(badarg)
             end
@@ -735,6 +773,10 @@ local_mreg(T, [_|_] = KVL) ->
         {true,_}  -> monitor_me()
     end.
 
+local_munreg(T, L) when T==p; T==c ->
+    _ = [gproc_lib:remove_reg({T,l,K}) || K <- L],
+    true.
+
 %% @spec (Key :: key(), Value) -> true
 %% @doc Sets the value of the registeration entry given by Key
 %% 
@@ -1115,7 +1157,7 @@ handle_call({reg, {_T,l,_} = Key, Val}, {Pid,_}, S) ->
 handle_call({unreg, {_,l,_} = Key}, {Pid,_}, S) ->
     case ets:member(?TAB, {Pid,Key}) of
         true ->
-            gproc_lib:remove_reg(Key, Pid),
+            _ = gproc_lib:remove_reg(Key, Pid),
             {reply, true, S};
         false ->
             {reply, badarg, S}
@@ -1137,6 +1179,9 @@ handle_call({mreg, T, l, L}, {Pid,_}, S) ->
     catch
         error:_  -> {reply, badarg, S}
     end;
+handle_call({munreg, T, l, L}, {Pid,_}, S) ->
+    _ = gproc_lib:remove_many(T, l, L, Pid),
+    {reply, true, S};
 handle_call({set, {_,l,_} = Key, Value}, {Pid,_}, S) ->
     case gproc_lib:do_set_value(Key, Value, Pid) of
         true ->
@@ -1293,7 +1338,7 @@ do_give_away({T,l,_} = K, To, Pid) when T==n; T==a ->
                     gproc_lib:ensure_monitor(ToPid, l),
                     ToPid;
                 undefined ->
-                    gproc_lib:remove_reg(K, Pid),
+                    _ = gproc_lib:remove_reg(K, Pid),
                     undefined
             end;
         _ ->
@@ -1318,7 +1363,7 @@ do_give_away({T,l,_} = K, To, Pid) when T==c; T==p ->
                             ToPid
                     end;
                 undefined ->
-                    gproc_lib:remove_reg(K, Pid),
+                    _ = gproc_lib:remove_reg(K, Pid),
                     undefined
             end;
         _ ->

+ 15 - 0
src/gproc_dist.erl

@@ -25,6 +25,7 @@
 -export([start_link/0, start_link/1,
 	 reg/1, reg/2, unreg/1,
 	 mreg/2,
+	 munreg/2,
 	 set_value/2,
 	 give_away/2,
 	 update_counter/2]).
@@ -98,6 +99,11 @@ mreg(T, KVL) ->
        true -> erlang:error(badarg)
     end.
 
+munreg(T, Keys) ->
+    if is_list(Keys) -> leader_call({munreg, T, g, Keys, self()});
+       true -> erlang:error(badarg)
+    end.
+	     
 
 unreg({_,g,_} = Key) ->
     leader_call({unreg, Key, self()});
@@ -313,6 +319,15 @@ handle_leader_call({mreg, T, g, L, Pid}, _From, S, _E) ->
 	    end;
        true -> {reply, badarg, S}
     end;
+handle_leader_call({munreg, T, g, L, Pid}, _From, S, _E) ->
+    try gproc_lib:remove_many(T, g, L, Pid) of
+	[] ->
+	    {reply, true, S};
+	Objs ->
+	    {reply, true, [{delete, Objs}], S}
+    catch
+	error:_ -> {reply, badarg, S}
+    end;
 handle_leader_call({set,{T,g,N} =K,V,Pid}, _From, S, _E) ->
     if T == a ->
 	    if is_integer(V) ->

+ 17 - 18
src/gproc_lib.erl

@@ -192,17 +192,28 @@ ensure_monitor(Pid, Scope) when Scope==g; Scope==l ->
     end.
 
 remove_reg(Key, Pid) ->
-    remove_reg_1(Key, Pid),
-    ets:delete(?TAB, {Pid,Key}).
+    Reg = remove_reg_1(Key, Pid),
+    ets:delete(?TAB, Rev = {Pid,Key}),
+    [Reg, Rev].
+
+remove_many(T, Scope, L, Pid) ->
+    lists:flatmap(fun(K) ->
+			  Key = {T, Scope, K},
+			  remove_reg(Key, Pid)
+		  end, L).
 
 remove_reg_1({c,_,_} = Key, Pid) ->
-    remove_counter_1(Key, ets:lookup_element(?TAB, {Key,Pid}, 3), Pid);
+    remove_counter_1(Key, ets:lookup_element(?TAB, Reg = {Key,Pid}, 3), Pid),
+    Reg;
 remove_reg_1({a,_,_} = Key, _Pid) ->
-    ets:delete(?TAB, {Key,a});
+    ets:delete(?TAB, Reg = {Key,a}),
+    Reg;
 remove_reg_1({n,_,_} = Key, _Pid) ->
-    ets:delete(?TAB, {Key,n});
+    ets:delete(?TAB, Reg = {Key,n}),
+    Reg;
 remove_reg_1({_,_,_} = Key, Pid) ->
-    ets:delete(?TAB, {Key, Pid}).
+    ets:delete(?TAB, Reg = {Key, Pid}),
+    Reg.
 
 remove_counter_1({c,C,N} = Key, Val, Pid) ->
     Res = ets:delete(?TAB, {Key, Pid}),
@@ -236,18 +247,6 @@ update_counter({c,l,Ctr} = Key, Incr, Pid) ->
 update_aggr_counter(C, N, Val) ->
     catch ets:update_counter(?TAB, {{a,C,N},a}, {3, Val}).
 
-%% cleanup_counter({c,g,N}=K, Pid, Acc) ->
-%%     remove_reg(K,Pid),
-%%     case ets:lookup(?TAB, {{a,g,N},a}) of
-%%         [Aggr] ->
-%%             [Aggr|Acc];
-%%         [] ->
-%%             Acc
-%%     end;
-%% cleanup_counter(K, Pid, Acc) ->
-%%     remove_reg(K,Pid),
-%%     Acc.
-
 scan_existing_counters(Ctxt, Name) ->
     Head = {{{c,Ctxt,Name},'_'},'_','$1'},
     Cs = ets:select(?TAB, [{Head, [], ['$1']}]),

+ 8 - 3
test/gproc_dist_tests.erl

@@ -81,9 +81,14 @@ t_simple_reg([H|_] = Ns) ->
     ?assertMatch(ok, t_lookup_everywhere(Name, Ns, undefined)),
     ?assertMatch(ok, t_call(P, die)).
 
-t_mreg([H|_]) ->
+t_mreg([H|_] = Ns) ->
     Kvl = ?T_KVL,
+    Keys = [K || {K,_} <- Kvl],
     P = t_spawn_mreg(H, Kvl),
+    [?assertMatch(ok, t_lookup_everywhere({n,g,K}, Ns, P)) || K <- Keys],
+    ?assertMatch(true, t_call(P, {apply, gproc, munreg, [n, g, Keys]})),
+    timer:sleep(1000),
+    [?assertMatch(ok, t_lookup_everywhere({n,g,K},Ns,undefined)) || K <- Keys],
     ?assertMatch(ok, t_call(P, die)).
 
 t_await_reg([A,B|_]) ->
@@ -143,7 +148,7 @@ t_sync(Ns) ->
 %% Verify that the gproc_dist:sync() call returns true even if a candidate dies
 %% while the sync is underway. This test makes use of sys:suspend() to ensure that
 %% the other candidate doesn't respond too quickly.
-t_sync_cand_dies([A,B|_] = Ns) ->
+t_sync_cand_dies([A,B|_]) ->
     Leader = rpc:call(A, gproc_dist, get_leader, []),
     Other = case Leader of 
 		A -> B;
@@ -220,7 +225,7 @@ t_spawn_reg(Node, Name) ->
 t_spawn_mreg(Node, KVL) ->
     Me = self(),
     spawn(Node, fun() ->
-			?assertMatch(true, gproc:mreg(p, g, KVL)),
+			?assertMatch(true, gproc:mreg(n, g, KVL)),
 			Me ! {self(), ok},
 			t_loop()
 		end),

+ 3 - 2
test/gproc_tests.erl

@@ -116,8 +116,9 @@ t_simple_mreg() ->
     ?assertEqual(true, gproc:mreg(n, l, [{foo, foo_val},
 					 {bar, bar_val}])),
     ?assertEqual(P, gproc:where({n,l,foo})),
-    ?assertEqual(P, gproc:where({n,l,bar})).
-    
+    ?assertEqual(P, gproc:where({n,l,bar})),
+    ?assertEqual(true, gproc:munreg(n, l, [foo, bar])).
+
 
 t_gproc_crash() ->
     P = spawn_helper(),