%% @doc This module is responsible for monitoring processes on remote nodes. -module(partisan_monitor). -behaviour(partisan_gen_server). % API -export([start_link/0, monitor/1, demonitor/1, demonitor/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). %%%=================================================================== %%% API %%%=================================================================== start_link() -> partisan_gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). %% @doc when you attempt to monitor a partisan_remote_reference, it is not %% guaranteed that you will receive the DOWN message. A few reasons for not %% receiving the message are message loss, tree reconfiguration and the node %% is no longer reachable. monitor(Pid) when is_pid(Pid) -> erlang:monitor(pid); monitor({partisan_remote_reference, Node, {partisan_process_reference, PidAsList}}) -> partisan_gen_server:call({?MODULE, Node}, {monitor, PidAsList}). demonitor(Ref) when is_reference(Ref) -> erlang:demonitor(Ref); demonitor({partisan_remote_reference, Node, {partisan_encoded_reference, _}} = PartisanRef) -> partisan_gen_server:call({?MODULE, Node}, {demonitor, PartisanRef}). demonitor(Ref, Opts) when is_reference(Ref) -> erlang:demonitor(Ref, Opts); demonitor({partisan_remote_reference, Node, {partisan_encoded_reference, _}} = PartisanRef, _Opts) -> partisan_gen_server:call({?MODULE, Node}, {demonitor, PartisanRef}). %%%=================================================================== %%% gen_server callbacks %%%=================================================================== %% @private init([]) -> {ok, #{}}. %% @private handle_call({monitor, PidAsList}, {PartisanRemote, _PartisanRemoteRef}, State) -> Ref = erlang:monitor(process, list_to_pid(PidAsList)), PartisanRef = partisan_util:ref(Ref), State1 = maps:put(Ref, {PartisanRef, PartisanRemote}, State), StateFinal = maps:put(PartisanRef, Ref, State1), {reply, PartisanRef, StateFinal}; handle_call({demonitor, PartisanRef}, _From, State) -> Ref = maps:get(PartisanRef, State), erlang:demonitor(Ref, [flush]), State1 = maps:remove(PartisanRef, State), StateFinal = maps:remove(Ref, State1), {reply, true, StateFinal}; handle_call(_Msg, _From, State) -> {reply, ok, State}. %% @private handle_cast(_Msg, State) -> {noreply, State}. %% @private handle_info({'DOWN', Ref, process, Pid, Reason}, State) -> {PartisanRef, PartisanRemote} = maps:get(Ref, State), State1 = maps:remove(Ref, State), StateFinal = maps:remove(PartisanRef, State1), Resp = {'DOWN', PartisanRef, process, partisan_util:pid(Pid), Reason}, partisan_peer_service_manager:forward_message(PartisanRemote, Resp), {noreply, StateFinal}; handle_info(_Msg, State) -> {noreply, State}. %% @private terminate(_Reason, _State) -> ok. %% @private code_change(_OldVsn, State, _Extra) -> {ok, State}.