diff options
Diffstat (limited to 'learn-you-some-erlang/processquest/apps/regis-1.0.0/src')
3 files changed, 145 insertions, 0 deletions
diff --git a/learn-you-some-erlang/processquest/apps/regis-1.0.0/src/regis.erl b/learn-you-some-erlang/processquest/apps/regis-1.0.0/src/regis.erl new file mode 100644 index 0000000..0b37c7c --- /dev/null +++ b/learn-you-some-erlang/processquest/apps/regis-1.0.0/src/regis.erl @@ -0,0 +1,29 @@ +%%% Application wrapper module for regis, +%%% a process registration application. +%%% +%%% This was added because the standard process registry has a precise +%%% meaning of representing VM-global, non-dynamic processes. +%%% However, for this, we needed dynamic names and so we had to write +%%% one ourselves. Of course we could have used 'global' (but we +%%% didn't see distributed Erlang yet) or 'gproc' (I don't want to +%%% depend on external libs for this guide), so checkthem out +%%% if you're writing your own app. +-module(regis). +-behaviour(application). +-export([start/2, stop/1]). +-export([register/2, unregister/1, whereis/1, get_names/0]). + + +start(normal, []) -> + regis_sup:start_link(). + +stop(_) -> + ok. + +register(Name, Pid) -> regis_server:register(Name, Pid). + +unregister(Name) -> regis_server:unregister(Name). + +whereis(Name) -> regis_server:whereis(Name). + +get_names() -> regis_server:get_names(). diff --git a/learn-you-some-erlang/processquest/apps/regis-1.0.0/src/regis_server.erl b/learn-you-some-erlang/processquest/apps/regis-1.0.0/src/regis_server.erl new file mode 100644 index 0000000..a668b02 --- /dev/null +++ b/learn-you-some-erlang/processquest/apps/regis-1.0.0/src/regis_server.erl @@ -0,0 +1,98 @@ +%%% The core of the app: the server in charge of tracking processes. +-module(regis_server). +-behaviour(gen_server). + +-export([start_link/0, stop/0, register/2, unregister/1, whereis/1, + get_names/0]). +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + code_change/3, terminate/2]). + +%% We have two indexes: one by name and one by pid, for +%% MAXIMUM SPEED (not actually measured). +-record(state, {pid, name}). + +%%%%%%%%%%%%%%%%% +%%% INTERFACE %%% +%%%%%%%%%%%%%%%%% +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +stop() -> + gen_server:call(?MODULE, stop). + +%% Give a name to a process +register(Name, Pid) when is_pid(Pid) -> + gen_server:call(?MODULE, {register, Name, Pid}). + +%% Remove the name from a process +unregister(Name) -> + gen_server:call(?MODULE, {unregister, Name}). + +%% Find the pid associated with a process +whereis(Name) -> + gen_server:call(?MODULE, {whereis, Name}). + +%% Find all the names currently registered. +get_names() -> + gen_server:call(?MODULE, get_names). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%%% GEN_SERVER CALLBACKS %%% +%%%%%%%%%%%%%%%%%%%%%%%%%%%% +init([]) -> + %% Using gb_trees to store items. gb_trees generally have + %% good overall performance. + {ok, #state{pid = gb_trees:empty(), + name = gb_trees:empty()}}. + +handle_call({register, Name, Pid}, _From, S = #state{pid=P, name=N}) -> + case {gb_trees:is_defined(Pid, P), gb_trees:is_defined(Name, N)} of + {true, _} -> + {reply, {error, already_named}, S}; + {_, true} -> + {reply, {error, name_taken}, S}; + {false, false} -> + Ref = erlang:monitor(process, Pid), + {reply, ok, S#state{pid=gb_trees:insert(Pid, {Name,Ref}, P), + name=gb_trees:insert(Name, {Pid,Ref}, N)}} + end; +handle_call({unregister, Name}, _From, S = #state{pid=P, name=N}) -> + case gb_trees:lookup(Name, N) of + {value, {Pid,Ref}} -> + erlang:demonitor(Ref, [flush]), + {reply, ok, S#state{pid=gb_trees:delete(Pid, P), + name=gb_trees:delete(Name, N)}}; + none -> + {reply, ok, S} + end; +handle_call({whereis, Name}, _From, S = #state{name=N}) -> + case gb_trees:lookup(Name, N) of + {value, {Pid,_}} -> + {reply, Pid, S}; + none -> + {reply, undefined, S} + end; +handle_call(get_names, _From, S = #state{name=N}) -> + {reply, gb_trees:keys(N), S}; +handle_call(stop, _From, State) -> + {stop, normal, ok, State}; +handle_call(_Event, _From, State) -> + {noreply, State}. + +handle_cast(_Event, State) -> + {noreply, State}. + +handle_info({'DOWN', Ref, process, Pid, _Reason}, S = #state{pid=P,name=N}) -> + {value, {Name, Ref}} = gb_trees:lookup(Pid, P), + {noreply, S#state{pid = gb_trees:delete(Pid, P), + name = gb_trees:delete(Name, N)}}; +handle_info(_Event, State) -> + {noreply, State}. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +terminate(_Reason, _State) -> + ok. + + diff --git a/learn-you-some-erlang/processquest/apps/regis-1.0.0/src/regis_sup.erl b/learn-you-some-erlang/processquest/apps/regis-1.0.0/src/regis_sup.erl new file mode 100644 index 0000000..be333e6 --- /dev/null +++ b/learn-you-some-erlang/processquest/apps/regis-1.0.0/src/regis_sup.erl @@ -0,0 +1,18 @@ +%%% The top-level supervisor of the registration +%%% server. +-module(regis_sup). +-behaviour(supervisor). +-export([start_link/0]). +-export([init/1]). + +start_link() -> + supervisor:start_link(?MODULE, []). + +init([]) -> + {ok, {{one_for_one, 1, 3600}, + [{server, + {regis_server, start_link, []}, + permanent, + 500, + worker, + [regis_server]}]}}. |