Add src/hk_browser_registry.erl
Browse files- src/hk_browser_registry.erl +70 -0
src/hk_browser_registry.erl
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
%%%-------------------------------------------------------------------
|
| 2 |
+
%%% @doc HK-BROWSER :: session_id -> pid directory.
|
| 3 |
+
%%%
|
| 4 |
+
%%% `hk_browser_session' processes are anonymous (started under
|
| 5 |
+
%%% `simple_one_for_one', so they can't be `{local, Name}' registered
|
| 6 |
+
%%% by session id -- ids are runtime data, not compile-time atoms).
|
| 7 |
+
%%% This registry is the lookup table the API and agent runtime use
|
| 8 |
+
%%% instead. A session registers itself once Chromium is confirmed
|
| 9 |
+
%%% ready; the registry monitors it and removes the entry on exit
|
| 10 |
+
%%% (including the gap between a crash and `hk_browser_sup' bringing
|
| 11 |
+
%%% a replacement back up under the same session id, which
|
| 12 |
+
%%% re-registers when *it* becomes ready).
|
| 13 |
+
%%% @end
|
| 14 |
+
%%%-------------------------------------------------------------------
|
| 15 |
+
-module(hk_browser_registry).
|
| 16 |
+
-behaviour(gen_server).
|
| 17 |
+
|
| 18 |
+
-export([start_link/0, register/2, unregister/1, lookup/1, list_sessions/0]).
|
| 19 |
+
-export([init/1, handle_call/3, handle_cast/2, handle_info/2]).
|
| 20 |
+
|
| 21 |
+
-define(TAB, hk_browser_registry_tab).
|
| 22 |
+
|
| 23 |
+
start_link() ->
|
| 24 |
+
hk_sup_util:start_link_retry(
|
| 25 |
+
fun() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []) end).
|
| 26 |
+
|
| 27 |
+
-spec register(binary(), pid()) -> ok.
|
| 28 |
+
register(SessionId, Pid) ->
|
| 29 |
+
gen_server:call(?MODULE, {register, SessionId, Pid}).
|
| 30 |
+
|
| 31 |
+
-spec unregister(binary()) -> ok.
|
| 32 |
+
unregister(SessionId) ->
|
| 33 |
+
gen_server:call(?MODULE, {unregister, SessionId}).
|
| 34 |
+
|
| 35 |
+
-spec lookup(binary()) -> {ok, pid()} | {error, not_found}.
|
| 36 |
+
lookup(SessionId) ->
|
| 37 |
+
case ets:lookup(?TAB, SessionId) of
|
| 38 |
+
[{SessionId, Pid}] -> {ok, Pid};
|
| 39 |
+
[] -> {error, not_found}
|
| 40 |
+
end.
|
| 41 |
+
|
| 42 |
+
-spec list_sessions() -> [{binary(), pid()}].
|
| 43 |
+
list_sessions() ->
|
| 44 |
+
ets:tab2list(?TAB).
|
| 45 |
+
|
| 46 |
+
init([]) ->
|
| 47 |
+
ets:new(?TAB, [set, named_table, protected]),
|
| 48 |
+
{ok, #{monitors => #{}}}.
|
| 49 |
+
|
| 50 |
+
handle_call({register, SessionId, Pid}, _From, State = #{monitors := Mons}) ->
|
| 51 |
+
ets:insert(?TAB, {SessionId, Pid}),
|
| 52 |
+
MonRef = erlang:monitor(process, Pid),
|
| 53 |
+
{reply, ok, State#{monitors := Mons#{MonRef => SessionId}}};
|
| 54 |
+
handle_call({unregister, SessionId}, _From, State) ->
|
| 55 |
+
ets:delete(?TAB, SessionId),
|
| 56 |
+
{reply, ok, State}.
|
| 57 |
+
|
| 58 |
+
handle_cast(_Msg, State) ->
|
| 59 |
+
{noreply, State}.
|
| 60 |
+
|
| 61 |
+
handle_info({'DOWN', MonRef, process, _Pid, _Reason}, State = #{monitors := Mons}) ->
|
| 62 |
+
case maps:take(MonRef, Mons) of
|
| 63 |
+
{SessionId, Mons2} ->
|
| 64 |
+
ets:delete(?TAB, SessionId),
|
| 65 |
+
{noreply, State#{monitors := Mons2}};
|
| 66 |
+
error ->
|
| 67 |
+
{noreply, State}
|
| 68 |
+
end;
|
| 69 |
+
handle_info(_Info, State) ->
|
| 70 |
+
{noreply, State}.
|