Browse Source

Allow to remove pids from PG.

Roberto Ostinelli 9 years ago
parent
commit
88ff8f8e22
2 changed files with 46 additions and 1 deletions
  1. 5 0
      src/syn.erl
  2. 41 1
      src/syn_pg.erl

+ 5 - 0
src/syn.erl

@@ -38,6 +38,7 @@
 
 %% PG
 -export([add_to_pg/2]).
+-export([remove_from_pg/2]).
 -export([pg_member/2]).
 -export([pids_of_pg/1]).
 
@@ -98,6 +99,10 @@ registry_count(Node) ->
 add_to_pg(Name, Pid) ->
     syn_pg:add_to_pg(Name, Pid).
 
+-spec remove_from_pg(Name :: any(), Pid :: pid()) -> ok | {error, undefined | pid_not_in_group}.
+remove_from_pg(Name, Pid) ->
+    syn_pg:remove_from_pg(Name, Pid).
+
 -spec pg_member(Pid :: pid(), Name :: any()) -> boolean().
 pg_member(Pid, Name) ->
     syn_pg:pg_member(Pid, Name).

+ 41 - 1
src/syn_pg.erl

@@ -28,6 +28,7 @@
 %% API
 -export([start_link/0]).
 -export([add_to_pg/2]).
+-export([remove_from_pg/2]).
 -export([pg_member/2]).
 -export([pids_of_pg/1]).
 
@@ -54,6 +55,18 @@ add_to_pg(Name, Pid) ->
     Node = node(Pid),
     gen_server:call({?MODULE, Node}, {add_to_pg, Name, Pid}).
 
+-spec remove_from_pg(Name :: any(), Pid :: pid()) -> ok | {error, undefined | pid_not_in_group}.
+remove_from_pg(Name, Pid) ->
+    case i_find_by_pid(Pid) of
+        undefined ->
+            {error, undefined};
+        Process when Process#syn_pg_table.name =/= Name ->
+            {error, pid_not_in_group};
+        Process ->
+            Node = Process#syn_pg_table.node,
+            gen_server:call({?MODULE, Node}, {remove_from_pg, Name, Pid})
+    end.
+
 -spec pg_member(Pid :: pid(), Name :: any()) -> boolean().
 pg_member(Pid, Name) ->
     i_pg_member(Pid, Name).
@@ -109,6 +122,22 @@ handle_call({add_to_pg, Name, Pid}, _From, State) ->
             {reply, pid_already_in_group, State}
     end;
 
+handle_call({remove_from_pg, Name, Pid}, _From, State) ->
+    %% we check again to return the correct response regardless of race conditions
+    case i_find_by_pid(Pid) of
+        undefined ->
+            {reply, {error, undefined}, State};
+        Process when Process#syn_pg_table.name =/= Name ->
+            {error, pid_not_in_group};
+        Process ->
+            %% remove from table
+            remove_process(Process),
+            %% unlink
+            erlang:unlink(Pid),
+            %% reply
+            {reply, ok, State}
+    end;
+
 handle_call(Request, From, State) ->
     error_logger:warning_msg("Received from ~p an unknown call message: ~p", [Request, From]),
     {reply, undefined, State}.
@@ -167,9 +196,20 @@ i_pg_member(Pid, Name) ->
         _ -> true
     end.
 
--spec i_pids_of_pg(Name :: any()) -> [Process :: #syn_global_table{}].
+-spec i_pids_of_pg(Name :: any()) -> [Process :: #syn_pg_table{}].
 i_pids_of_pg(Name) ->
     Processes = mnesia:dirty_read(syn_pg_table, Name),
     lists:map(fun(Process) ->
         Process#syn_pg_table.pid
     end, Processes).
+
+-spec i_find_by_pid(Pid :: pid()) -> Process :: #syn_pg_table{} | undefined.
+i_find_by_pid(Pid) ->
+    case mnesia:dirty_index_read(syn_pg_table, Pid, #syn_pg_table.pid) of
+        [Process] -> Process;
+        _ -> undefined
+    end.
+
+-spec remove_process(Process :: #syn_pg_table{}) -> ok.
+remove_process(Process) ->
+    mnesia:dirty_delete_object(syn_pg_table, Process).