Browse Source

Merge pull request #103 from uwiger/uw-reg_other

added reg_other(), unreg_other() + test cases
Ulf Wiger 9 years ago
parent
commit
ad62d6739d
4 changed files with 165 additions and 26 deletions
  1. 95 22
      src/gproc.erl
  2. 36 4
      src/gproc_dist.erl
  3. 14 0
      test/gproc_dist_tests.erl
  4. 20 0
      test/gproc_tests.erl

+ 95 - 22
src/gproc.erl

@@ -47,6 +47,7 @@
 
 -export([start_link/0,
          reg/1, reg/2, reg/3, unreg/1, set_attributes/2,
+         reg_other/2, reg_other/3, reg_other/4, unreg_other/2,
 	 reg_or_locate/1, reg_or_locate/2, reg_or_locate/3,
 	 reg_shared/1, reg_shared/2, reg_shared/3, unreg_shared/1,
 	 set_attributes_shared/2, set_value_shared/2,
@@ -1014,6 +1015,48 @@ reg1({rc,l,_} = Key, Value, As) ->
 reg1(_, _, _) ->
     ?THROW_GPROC_ERROR(badarg).
 
+%% @equiv reg_other(Key, Pid, default(Key), [])
+reg_other(Key, Pid) ->
+    ?CATCH_GPROC_ERROR(reg_other1(Key, Pid), [Key, Pid]).
+
+%% @equiv reg_other(Key, Pid, Value, [])
+reg_other(Key, Pid, Value) ->
+    ?CATCH_GPROC_ERROR(reg_other1(Key, Pid, Value, []), [Key, Pid, Value]).
+
+%% @spec reg_other(Key, Pid, Value, Attrs) -> true
+%% @doc Register name or property to another process.
+%%
+%% Equivalent to {@link reg/3}, but allows for registration of another process
+%% instead of the current process.
+%%
+%% Note that registering other processes introduces the possibility of
+%% confusing race conditions in user code. Letting each process register
+%% its own resources is highly recommended.
+%%
+%% Only the following resource types can be registered through this function:
+%%
+%% * `n'  - unique names
+%% * `a'  - aggregated counters
+%% * `r'  - resource properties
+%% * `rc' - resource counters
+%% @end
+reg_other(Key, Pid, Value, Attrs) ->
+    ?CATCH_GPROC_ERROR(reg_other1(Key, Pid, Value, Attrs),
+                       [Key, Pid, Value, Attrs]).
+
+reg_other1(Key, Pid) ->
+    reg_other1(Key, Pid, default(Key), []).
+
+reg_other1({_,g,_} = Key, Pid, Value, As) when is_pid(Pid) ->
+    ?CHK_DIST,
+    gproc_dist:reg_other(Key, Pid, Value, As);
+reg_other1({T,l,_} = Key, Pid, Value, As) when is_pid(Pid) ->
+    if T==n; T==a; T==r; T==rc ->
+            call({reg_other, Key, Pid, Value, As});
+       true ->
+            ?THROW_GPROC_ERROR(badarg)
+    end.
+
 %% @spec reg_or_locate(Key::key(), Value) -> {pid(), NewValue}
 %%
 %% @doc Try registering a unique name, or return existing registration.
@@ -1185,6 +1228,26 @@ unreg1(Key) ->
             end
     end.
 
+%% @spec unreg_other(key(), pid()) -> true
+%% @doc Unregister a name registered to another process.
+%%
+%% This function is equivalent to {@link unreg/1}, but specifies another
+%% process as the holder of the registration. An exception is raised if the
+%% name or property is not registered to the given process.
+%% @end
+unreg_other(Key, Pid) ->
+    ?CATCH_GPROC_ERROR(unreg_other1(Key, Pid), [Key, Pid]).
+
+unreg_other1({_,g,_} = Key, Pid) ->
+    ?CHK_DIST,
+    gproc_dist:unreg_other(Key, Pid);
+unreg_other1({T,l,_} = Key, Pid) when is_pid(Pid) ->
+    if T==n; T==a; T==r; T==rc ->
+            call({unreg_other, Key, Pid});
+       true ->
+            ?THROW_GPROC_ERROR(badarg)
+    end.
+
 %% @spec (Key::key(), Props::[{atom(), any()}]) -> true
 %%
 %% @doc Add/modify `{Key, Value}' attributes associated with a registration.
@@ -2123,17 +2186,9 @@ handle_cast({cancel_wait_or_monitor, Pid, {T,_,_} = Key}, S) ->
 
 %% @hidden
 handle_call({reg, {_T,l,_} = Key, Val, Attrs}, {Pid,_}, S) ->
-    case try_insert_reg(Key, Val, Pid) of
-        true ->
-            _ = gproc_lib:ensure_monitor(Pid,l),
-            _ = if Attrs =/= [] ->
-                        gproc_lib:insert_attr(Key, Attrs, Pid, l);
-                   true -> true
-                end,
-            {reply, true, S};
-        false ->
-            {reply, badarg, S}
-    end;
+    handle_reg_call(Key, Pid, Val, Attrs, S);
+handle_call({reg_other, {_T,l,_} = Key, Pid, Val, Attrs}, _, S) ->
+    handle_reg_call(Key, Pid, Val, Attrs, S);
 handle_call({set_attributes, {_,l,_} = Key, Attrs}, {Pid,_}, S) ->
     case gproc_lib:insert_attr(Key, Attrs, Pid, l) of
 	false -> {reply, badarg, S};
@@ -2232,16 +2287,9 @@ handle_call({reg_shared, {_T,l,_} = Key, Val, Attrs}, _From, S) ->
 	    {reply, badarg, S}
     end;
 handle_call({unreg, {_,l,_} = Key}, {Pid,_}, S) ->
-    case ets:lookup(?TAB, {Pid,Key}) of
-        [{_, r}] ->
-            _ = gproc_lib:remove_reg(Key, Pid, unreg, []),
-            {reply, true, S};
-        [{_, Opts}] when is_list(Opts) ->
-            _ = gproc_lib:remove_reg(Key, Pid, unreg, Opts),
-            {reply, true, S};
-        [] ->
-            {reply, badarg, S}
-    end;
+    handle_unreg_call(Key, Pid, S);
+handle_call({unreg_other, {_,l,_} = Key, Pid}, _, S) ->
+    handle_unreg_call(Key, Pid, S);
 handle_call({unreg_shared, {_,l,_} = Key}, _, S) ->
     _ = case ets:lookup(?TAB, {shared, Key}) of
 	    [{_, r}] ->
@@ -2321,11 +2369,36 @@ code_change(_FromVsn, S, _Extra) ->
         end,
     {ok, S}.
 
-
 %% @hidden
 terminate(_Reason, _S) ->
     ok.
 
+%% handle_call body common to reg and reg_other.
+%%
+handle_reg_call(Key, Pid, Val, Attrs, S) ->
+    case try_insert_reg(Key, Val, Pid) of
+        true ->
+            _ = gproc_lib:ensure_monitor(Pid,l),
+            _ = if Attrs =/= [] ->
+                        gproc_lib:insert_attr(Key, Attrs, Pid, l);
+                   true -> true
+                end,
+            {reply, true, S};
+        false ->
+            {reply, badarg, S}
+    end.
+
+handle_unreg_call(Key, Pid, S) ->
+    case ets:lookup(?TAB, {Pid,Key}) of
+        [{_, r}] ->
+            _ = gproc_lib:remove_reg(Key, Pid, unreg, []),
+            {reply, true, S};
+        [{_, Opts}] when is_list(Opts) ->
+            _ = gproc_lib:remove_reg(Key, Pid, unreg, Opts),
+            {reply, true, S};
+        [] ->
+            {reply, badarg, S}
+    end.
 
 call(Req) ->
     call(Req, l).

+ 36 - 4
src/gproc_dist.erl

@@ -24,6 +24,7 @@
 
 -export([start_link/0, start_link/1,
          reg/1, reg/3, unreg/1,
+         reg_other/4, unreg_other/2,
 	 reg_or_locate/3,
 	 reg_shared/3, unreg_shared/1,
          monitor/2,
@@ -109,13 +110,14 @@ reg_or_locate(_, _, _) ->
     ?THROW_GPROC_ERROR(badarg).
 
 
-%%% @spec({Class,Scope, Key}, Value) -> true
+%%% @spec({Class,g, Key}, Value) -> true
 %%% @doc
 %%%    Class = n  - unique name
 %%%          | p  - non-unique property
 %%%          | c  - counter
 %%%          | a  - aggregated counter
-%%%    Scope = l | g (global or local)
+%%%          | r  - resource property
+%%%          | rc - resource counter
 %%% @end
 reg({_,g,_} = Key, Value, Attrs) ->
     %% anything global
@@ -123,6 +125,33 @@ reg({_,g,_} = Key, Value, Attrs) ->
 reg(_, _, _) ->
     ?THROW_GPROC_ERROR(badarg).
 
+%% @spec ({Class,g,Key}, pid(), Value, Attrs) -> true
+%% @doc
+%%    Class = n  - unique name
+%%          | a  - aggregated counter
+%%          | r  - resource property
+%%          | rc - resource counter
+%%    Value = term()
+%%    Attrs = [{Key, Value}]
+%% @end
+reg_other({T,g,_} = Key, Pid, Value, Attrs) when is_pid(Pid) ->
+    if T==n; T==a; T==r; T==rc ->
+            leader_call({reg_other, Key, Value, Pid, Attrs});
+       true ->
+            ?THROW_GPROC_ERROR(badarg)
+    end;
+reg_other(_, _, _, _) ->
+    ?THROW_GPROC_ERROR(badarg).
+
+unreg_other({T,g,_} = Key, Pid) when is_pid(Pid) ->
+    if T==n; T==a; T==r; T==rc ->
+            leader_call({unreg_other, Key, Pid});
+       true ->
+            ?THROW_GPROC_ERROR(badarg)
+    end;
+unreg_other(_, _) ->
+    ?THROW_GPROC_ERROR(badarg).
+
 reg_shared({_,g,_} = Key, Value, Attrs) ->
     leader_call({reg, Key, Value, shared, Attrs});
 reg_shared(_, _, _) ->
@@ -334,7 +363,8 @@ handle_leader_call(sync, From, #state{sync_requests = SReqs} = S, E) ->
             GenLeader:broadcast({from_leader, {sync, From}}, Alive, E),
             {noreply, S#state{sync_requests = [{From, Alive}|SReqs]}}
     end;
-handle_leader_call({reg, {_C,g,_Name} = K, Value, Pid, As}, _From, S, _E) ->
+handle_leader_call({Reg, {_C,g,_Name} = K, Value, Pid, As}, _From, S, _E)
+  when Reg==reg; Reg==reg_other ->
     case gproc_lib:insert_reg(K, Value, Pid, g) of
         false ->
             {reply, badarg, S};
@@ -479,7 +509,9 @@ handle_leader_call({reset_counter, {c,g,_Ctr} = Key, Pid}, _From, S, _E) ->
 	    io:fwrite("reset_counter failed: ~p~n~p~n", [_R, erlang:get_stacktrace()]),
 	    {reply, badarg, S}
     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)
+  when Unreg==unreg;
+       Unreg==unreg_other->
     Key = if T == n; T == a; T == rc -> {K,T};
              true -> {K, Pid}
           end,

+ 14 - 0
test/gproc_dist_tests.erl

@@ -52,6 +52,9 @@ dist_test_() ->
                                        ?debugVal(t_simple_reg(Ns))
                                end,
                                fun() ->
+                                       ?debugVal(t_simple_reg_other(Ns))
+                               end,
+                               fun() ->
                                        ?debugVal(t_simple_reg_or_locate(Ns))
                                end,
                                fun() ->
@@ -150,6 +153,17 @@ t_simple_reg([H|_] = Ns) ->
     ?assertMatch(ok, t_lookup_everywhere(Name, Ns, undefined)),
     ?assertMatch(ok, t_call(P, die)).
 
+t_simple_reg_other([A, B|_] = Ns) ->
+    Name = ?T_NAME,
+    P1 = t_spawn(A),
+    P2 = t_spawn(B),
+    ?assertMatch(true, t_call(P1, {apply, gproc, reg_other, [Name, P2]})),
+    ?assertMatch(ok, t_lookup_everywhere(Name, Ns, P2)),
+    ?assertMatch(true, t_call(P1, {apply, gproc, unreg_other, [Name, P2]})),
+    ?assertMatch(ok, t_lookup_everywhere(Name, Ns, undefined)),
+    ?assertMatch(ok, t_call(P1, die)),
+    ?assertMatch(ok, t_call(P2, die)).
+
 t_simple_reg_or_locate([A,B|_] = _Ns) ->
     Name = ?T_NAME,
     P1 = t_spawn(A),

+ 20 - 0
test/gproc_tests.erl

@@ -74,6 +74,8 @@ reg_test_() ->
      [
       {spawn, ?_test(?debugVal(t_simple_reg()))}
       , ?_test(t_is_clean())
+      , {spawn, ?_test(?debugVal(t_simple_reg_other()))}
+      , ?_test(t_is_clean())
       , {spawn, ?_test(?debugVal(t_simple_reg_or_locate()))}
       , ?_test(t_is_clean())
       , {spawn, ?_test(?debugVal(t_reg_or_locate2()))}
@@ -158,6 +160,24 @@ t_simple_reg() ->
     ?assert(gproc:unreg({n,l,name}) =:= true),
     ?assert(gproc:where({n,l,name}) =:= undefined).
 
+t_simple_reg_other() ->
+    P = self(),
+    P1 = spawn_link(fun() ->
+                            receive
+                                {P, goodbye} -> ok
+                            end
+                    end),
+    Ref = erlang:monitor(process, P1),
+    ?assert(gproc:reg_other({n,l,name}, P1) =:= true),
+    ?assert(gproc:where({n,l,name}) =:= P1),
+    ?assert(gproc:unreg_other({n,l,name}, P1) =:= true),
+    ?assert(gproc:where({n,l,name}) =:= undefined),
+    P1 ! {self(), goodbye},
+    receive
+        {'DOWN', Ref, _, _, _} ->
+            ok
+    end.
+
 t_simple_reg_or_locate() ->
     P = self(),
     ?assertMatch({P, undefined}, gproc:reg_or_locate({n,l,name})),