From b72fe3e67e65c66d979a9651ebc815bdc553601c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Hoguin?= Date: Tue, 24 Jul 2012 12:58:54 +0200 Subject: Introduce the ranch_server registry, make it handle listeners --- src/ranch_server.erl | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 src/ranch_server.erl (limited to 'src/ranch_server.erl') diff --git a/src/ranch_server.erl b/src/ranch_server.erl new file mode 100644 index 0000000..bafdeb5 --- /dev/null +++ b/src/ranch_server.erl @@ -0,0 +1,92 @@ +%% Copyright (c) 2012, Loïc Hoguin +%% +%% Permission to use, copy, modify, and/or distribute this software for any +%% purpose with or without fee is hereby granted, provided that the above +%% copyright notice and this permission notice appear in all copies. +%% +%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +%% @private +-module(ranch_server). +-behaviour(gen_server). + +%% API. +-export([start_link/0]). +-export([insert_listener/2]). +-export([lookup_listener/1]). + +%% gen_server. +-export([init/1]). +-export([handle_call/3]). +-export([handle_cast/2]). +-export([handle_info/2]). +-export([terminate/2]). +-export([code_change/3]). + +-define(TAB, ?MODULE). + +-type key() :: {listener, any()}. +-record(state, { + monitors = [] :: [{{reference(), pid()}, key()}] +}). + +%% API. + +%% @doc Start the ranch_server gen_server. +-spec start_link() -> {ok, pid()}. +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +%% @doc Insert a listener into the database. +-spec insert_listener(any(), pid()) -> ok. +insert_listener(Ref, Pid) -> + true = ets:insert_new(?TAB, {{listener, Ref}, Pid}), + gen_server:cast(?MODULE, {insert_listener, Ref, Pid}). + +%% @doc Lookup a listener in the database. +-spec lookup_listener(any()) -> pid(). +lookup_listener(Ref) -> + ets:lookup_element(?TAB, {listener, Ref}, 2). + +%% gen_server. + +%% @private +init([]) -> + ?TAB = ets:new(?TAB, [ + ordered_set, public, named_table, {read_concurrency, true}]), + {ok, #state{}}. + +%% @private +handle_call(_Request, _From, State) -> + {reply, ignore, State}. + +%% @private +handle_cast({insert_listener, Ref, Pid}, State=#state{monitors=Monitors}) -> + MonitorRef = erlang:monitor(process, Pid), + {noreply, State#state{ + monitors=[{{MonitorRef, Pid}, {listener, Ref}}|Monitors]}}; +handle_cast(_Request, State) -> + {noreply, State}. + +%% @private +handle_info({'DOWN', Ref, process, Pid, _}, State=#state{monitors=Monitors}) -> + {_, Key} = lists:keyfind({Ref, Pid}, 1, Monitors), + true = ets:delete(?TAB, Key), + Monitors2 = lists:keydelete({Ref, Pid}, 1, Monitors), + {noreply, State#state{monitors=Monitors2}}; +handle_info(_Info, State) -> + {noreply, State}. + +%% @private +terminate(_Reason, _State) -> + ok. + +%% @private +code_change(_OldVsn, State, _Extra) -> + {ok, State}. -- cgit v1.2.3