Browse Source

Single node process callback exit.

Roberto Ostinelli 10 years ago
parent
commit
d9a9ec64e9
3 changed files with 79 additions and 13 deletions
  1. 28 9
      src/syn.erl
  2. 23 4
      src/syn_backbone.erl
  3. 28 0
      test/syn_register_processes_SUITE.erl

+ 28 - 9
src/syn.erl

@@ -67,15 +67,7 @@ find_by_pid(Pid) ->
 
 -spec options(list()) -> ok.
 options(Options) ->
-    %% send message
-    case proplists:get_value(netsplit_conflicting_mode, Options) of
-        undefined ->
-            ok;
-        kill ->
-            syn_netsplits:conflicting_mode(kill);
-        {send_message, Message} ->
-            syn_netsplits:conflicting_mode({send_message, Message})
-    end.
+    set_options(Options).
 
 -spec count() -> non_neg_integer().
 count() ->
@@ -84,3 +76,30 @@ count() ->
 -spec count(Node :: atom()) -> non_neg_integer().
 count(Node) ->
     syn_backbone:count(Node).
+
+%% ===================================================================
+%% Internal
+%% ===================================================================
+set_options([]) -> ok;
+set_options([{netsplit_conflicting_mode, Mode} | Options]) ->
+    case Mode of
+        kill ->
+            syn_netsplits:conflicting_mode(kill);
+        {send_message, Message} ->
+            syn_netsplits:conflicting_mode({send_message, Message});
+        _ ->
+            error(invalid_syn_option, {netsplit_conflicting_mode, Mode})
+    end,
+    set_options(Options);
+set_options([{process_exit_callback, ProcessExitCallback} | Options]) ->
+    case ProcessExitCallback of
+        undefined ->
+            syn_backbone:process_exit_callback(undefined);
+        _ when is_function(ProcessExitCallback) ->
+            syn_backbone:process_exit_callback(ProcessExitCallback);
+        _ ->
+            error(invalid_syn_option, {process_exit_callback, ProcessExitCallback})
+    end,
+    set_options(Options);
+set_options([InvalidSynOption | _Options]) ->
+    error(invalid_syn_option, InvalidSynOption).

+ 23 - 4
src/syn_backbone.erl

@@ -31,6 +31,7 @@
 
 %% API
 -export([start_link/0]).
+-export([process_exit_callback/1]).
 -export([register/2, unregister/1]).
 -export([find_by_key/1, find_by_pid/1]).
 -export([count/0, count/1]).
@@ -39,7 +40,9 @@
 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
 
 %% records
--record(state, {}).
+-record(state, {
+    process_exit_callback = undefined :: undefined | function()
+}).
 
 %% include
 -include("syn.hrl").
@@ -53,6 +56,10 @@ start_link() ->
     Options = [],
     gen_server:start_link({local, ?MODULE}, ?MODULE, [], Options).
 
+-spec process_exit_callback(function() | undefined) -> ok.
+process_exit_callback(ProcessExitCallback) ->
+    gen_server:call(?MODULE, {process_exit_callback, ProcessExitCallback}).
+
 -spec find_by_key(Key :: any()) -> pid() | undefined.
 find_by_key(Key) ->
     case mnesia:dirty_read(syn_processes_table, Key) of
@@ -152,6 +159,10 @@ handle_call({unlink_process, Pid}, _From, State) ->
     erlang:unlink(Pid),
     {reply, ok, State};
 
+handle_call({process_exit_callback, ProcessExitCallback}, _From, State) ->
+    error_logger:info_msg("process_exit_callback set to: ~p~n", [ProcessExitCallback]),
+    {reply, ok, State#state{process_exit_callback = ProcessExitCallback}};
+
 handle_call(Request, From, State) ->
     error_logger:warning_msg("Received from ~p an unknown call message: ~p~n", [Request, From]),
     {reply, undefined, State}.
@@ -176,7 +187,9 @@ handle_cast(Msg, State) ->
     {noreply, #state{}, Timeout :: non_neg_integer()} |
     {stop, Reason :: any(), #state{}}.
 
-handle_info({'EXIT', Pid, Reason}, State) ->
+handle_info({'EXIT', Pid, Reason}, #state{
+    process_exit_callback = ProcessExitCallback
+} = State) ->
     %% do not lock backbone
     spawn(fun() ->
         %% check if pid is in table
@@ -186,7 +199,7 @@ handle_info({'EXIT', Pid, Reason}, State) ->
                     normal -> ok;
                     killed -> ok;
                     _ ->
-                        error_logger:warning_msg("Received a crash message from an unlinked process ~p with reason: ~p~n", [Pid, Reason])
+                        error_logger:warning_msg("Received an exit message from an unlinked process ~p with reason: ~p~n", [Pid, Reason])
                 end;
             Key ->
                 %% delete from table
@@ -195,7 +208,13 @@ handle_info({'EXIT', Pid, Reason}, State) ->
                 case Reason of
                     normal -> ok;
                     killed -> ok;
-                    _ -> error_logger:error_msg("Process with key ~p crashed with reason: ~p~n", [Key, Reason])
+                    _ ->
+                        error_logger:error_msg("Process with key ~p exited with reason: ~p~n", [Key, Reason])
+                end,
+                %% callback
+                case ProcessExitCallback of
+                    undefined -> ok;
+                    _ -> spawn(fun() -> ProcessExitCallback(Key, Pid, Reason) end)
                 end
         end
     end),

+ 28 - 0
test/syn_register_processes_SUITE.erl

@@ -41,6 +41,7 @@
     single_node_when_mnesia_is_ram_re_register_error/1,
     single_node_when_mnesia_is_ram_unregister/1,
     single_node_when_mnesia_is_ram_process_count/1,
+    single_node_when_mnesia_is_ram_callback_on_process_exit/1,
     single_node_when_mnesia_is_disc_find_by_key/1
 ]).
 -export([
@@ -90,6 +91,7 @@ groups() ->
             single_node_when_mnesia_is_ram_re_register_error,
             single_node_when_mnesia_is_ram_unregister,
             single_node_when_mnesia_is_ram_process_count,
+            single_node_when_mnesia_is_ram_callback_on_process_exit,
             single_node_when_mnesia_is_disc_find_by_key
         ]},
         {two_nodes_process_registration, [shuffle], [
@@ -286,6 +288,32 @@ single_node_when_mnesia_is_ram_process_count(_Config) ->
     %% count
     0 = syn:count().
 
+single_node_when_mnesia_is_ram_callback_on_process_exit(_Config) ->
+    %% set schema location
+    application:set_env(mnesia, schema_location, ram),
+    %% start
+    ok = syn:start(),
+    %% define callback
+    Self = self(),
+    CallbackFun = fun(Key, Pid, Reason) ->
+        Self ! {exited, Key, Pid, Reason}
+    end,
+    syn:options([
+        {process_exit_callback, CallbackFun}
+    ]),
+    %% start process
+    Pid = syn_test_suite_helper:start_process(),
+    %% register
+    ok = syn:register(<<"my proc">>, Pid),
+    %% kill process
+    syn_test_suite_helper:kill_process(Pid),
+    %% check callback was triggered
+    receive
+        {exited, <<"my proc">>, Pid, killed} -> ok
+    after 2000 ->
+        ok = process_exit_callback_was_not_called
+    end.
+
 single_node_when_mnesia_is_disc_find_by_key(_Config) ->
     %% set schema location
     application:set_env(mnesia, schema_location, disc),