|
@@ -0,0 +1,159 @@
|
|
|
+%% ==========================================================================================================
|
|
|
+%% Syn - A global process registry.
|
|
|
+%%
|
|
|
+%% The MIT License (MIT)
|
|
|
+%%
|
|
|
+%% Copyright (c) 2016 Roberto Ostinelli <roberto@ostinelli.net> and Neato Robotics, Inc.
|
|
|
+%%
|
|
|
+%% Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
+%% of this software and associated documentation files (the "Software"), to deal
|
|
|
+%% in the Software without restriction, including without limitation the rights
|
|
|
+%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
+%% copies of the Software, and to permit persons to whom the Software is
|
|
|
+%% furnished to do so, subject to the following conditions:
|
|
|
+%%
|
|
|
+%% The above copyright notice and this permission notice shall be included in
|
|
|
+%% all copies or substantial portions of the Software.
|
|
|
+%%
|
|
|
+%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
+%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
+%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
+%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
+%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
+%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
+%% THE SOFTWARE.
|
|
|
+%% ==========================================================================================================
|
|
|
+-module(syn_pg).
|
|
|
+
|
|
|
+%% API
|
|
|
+-export([start_link/0]).
|
|
|
+-export([add_to_pg/2]).
|
|
|
+-export([pids_of_pg/1]).
|
|
|
+
|
|
|
+%% gen_server callbacks
|
|
|
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
|
|
|
+
|
|
|
+%% records
|
|
|
+-record(state, {}).
|
|
|
+
|
|
|
+%% include
|
|
|
+-include("syn.hrl").
|
|
|
+
|
|
|
+
|
|
|
+%% ===================================================================
|
|
|
+%% API
|
|
|
+%% ===================================================================
|
|
|
+-spec start_link() -> {ok, pid()} | {error, any()}.
|
|
|
+start_link() ->
|
|
|
+ Options = [],
|
|
|
+ gen_server:start_link({local, ?MODULE}, ?MODULE, [], Options).
|
|
|
+
|
|
|
+-spec add_to_pg(Name :: any(), Pid :: pid()) -> ok | {error, pid_already_in_group}.
|
|
|
+add_to_pg(Name, Pid) ->
|
|
|
+ Node = node(Pid),
|
|
|
+ gen_server:call({?MODULE, Node}, {add_to_pg, Name, Pid}).
|
|
|
+
|
|
|
+-spec pids_of_pg(Name :: any()) -> [pid()].
|
|
|
+pids_of_pg(Name) ->
|
|
|
+ i_pids_of_pg(Name).
|
|
|
+
|
|
|
+%% ===================================================================
|
|
|
+%% Callbacks
|
|
|
+%% ===================================================================
|
|
|
+
|
|
|
+%% ----------------------------------------------------------------------------------------------------------
|
|
|
+%% Init
|
|
|
+%% ----------------------------------------------------------------------------------------------------------
|
|
|
+-spec init([]) ->
|
|
|
+ {ok, #state{}} |
|
|
|
+ {ok, #state{}, Timeout :: non_neg_integer()} |
|
|
|
+ ignore |
|
|
|
+ {stop, Reason :: any()}.
|
|
|
+init([]) ->
|
|
|
+ %% trap linked processes signal
|
|
|
+ process_flag(trap_exit, true),
|
|
|
+
|
|
|
+ %% build state
|
|
|
+ {ok, #state{}}.
|
|
|
+
|
|
|
+%% ----------------------------------------------------------------------------------------------------------
|
|
|
+%% Call messages
|
|
|
+%% ----------------------------------------------------------------------------------------------------------
|
|
|
+-spec handle_call(Request :: any(), From :: any(), #state{}) ->
|
|
|
+ {reply, Reply :: any(), #state{}} |
|
|
|
+ {reply, Reply :: any(), #state{}, Timeout :: non_neg_integer()} |
|
|
|
+ {noreply, #state{}} |
|
|
|
+ {noreply, #state{}, Timeout :: non_neg_integer()} |
|
|
|
+ {stop, Reason :: any(), Reply :: any(), #state{}} |
|
|
|
+ {stop, Reason :: any(), #state{}}.
|
|
|
+
|
|
|
+handle_call({add_to_pg, Name, Pid}, _From, State) ->
|
|
|
+ PidsOfPg = i_pids_of_pg(Name),
|
|
|
+ case lists:member(Pid, PidsOfPg) of
|
|
|
+ false ->
|
|
|
+ %% add to table
|
|
|
+ mnesia:dirty_write(#syn_pg_table{
|
|
|
+ name = Name,
|
|
|
+ pid = Pid,
|
|
|
+ node = node()
|
|
|
+ }),
|
|
|
+ %% link
|
|
|
+ erlang:link(Pid),
|
|
|
+ %% return
|
|
|
+ {reply, ok, State};
|
|
|
+ _ ->
|
|
|
+ {reply, pid_already_in_group, State}
|
|
|
+ end;
|
|
|
+
|
|
|
+handle_call(Request, From, State) ->
|
|
|
+ error_logger:warning_msg("Received from ~p an unknown call message: ~p", [Request, From]),
|
|
|
+ {reply, undefined, State}.
|
|
|
+
|
|
|
+%% ----------------------------------------------------------------------------------------------------------
|
|
|
+%% Cast messages
|
|
|
+%% ----------------------------------------------------------------------------------------------------------
|
|
|
+-spec handle_cast(Msg :: any(), #state{}) ->
|
|
|
+ {noreply, #state{}} |
|
|
|
+ {noreply, #state{}, Timeout :: non_neg_integer()} |
|
|
|
+ {stop, Reason :: any(), #state{}}.
|
|
|
+
|
|
|
+handle_cast(Msg, State) ->
|
|
|
+ error_logger:warning_msg("Received an unknown cast message: ~p", [Msg]),
|
|
|
+ {noreply, State}.
|
|
|
+
|
|
|
+%% ----------------------------------------------------------------------------------------------------------
|
|
|
+%% All non Call / Cast messages
|
|
|
+%% ----------------------------------------------------------------------------------------------------------
|
|
|
+-spec handle_info(Info :: any(), #state{}) ->
|
|
|
+ {noreply, #state{}} |
|
|
|
+ {noreply, #state{}, Timeout :: non_neg_integer()} |
|
|
|
+ {stop, Reason :: any(), #state{}}.
|
|
|
+
|
|
|
+handle_info(Info, State) ->
|
|
|
+ error_logger:warning_msg("Received an unknown info message: ~p", [Info]),
|
|
|
+ {noreply, State}.
|
|
|
+
|
|
|
+%% ----------------------------------------------------------------------------------------------------------
|
|
|
+%% Terminate
|
|
|
+%% ----------------------------------------------------------------------------------------------------------
|
|
|
+-spec terminate(Reason :: any(), #state{}) -> terminated.
|
|
|
+terminate(Reason, _State) ->
|
|
|
+ error_logger:info_msg("Terminating syn pg with reason: ~p", [Reason]),
|
|
|
+ terminated.
|
|
|
+
|
|
|
+%% ----------------------------------------------------------------------------------------------------------
|
|
|
+%% Convert process state when code is changed.
|
|
|
+%% ----------------------------------------------------------------------------------------------------------
|
|
|
+-spec code_change(OldVsn :: any(), #state{}, Extra :: any()) -> {ok, #state{}}.
|
|
|
+code_change(_OldVsn, State, _Extra) ->
|
|
|
+ {ok, State}.
|
|
|
+
|
|
|
+%% ===================================================================
|
|
|
+%% Internal
|
|
|
+%% ===================================================================
|
|
|
+-spec i_pids_of_pg(Name :: any()) -> [Process :: #syn_global_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).
|