Permalink
Browse files

added proc_reg

  • Loading branch information...
uwiger
uwiger committed Jul 8, 2004
1 parent b24b69a commit 20d8678bd317d52f725d7eaa75a37e2e081b9d6b
View
@@ -17,6 +17,7 @@ LIBS := \
pan \
pico \
posix_drv \
+ proc_reg \
quickcheck \
rdbms \
rpc \
View
@@ -0,0 +1,31 @@
+
+SUBDIRS = src
+APPNAME=proc_reg
+BINDIR=ebin
+#INCDIR=inc
+#DOCDIR=doc
+#EDOCDIR="../edoc/ebin"
+#STOOLSDIR="../syntax_tools/ebin"
+#XMERLDIR="../xmerl/ebin"
+#DOC_OPTS=[{title,"The 'proc_reg' application"}]
+
+all: all-subdirs
+
+# Commented out since the new edoc is not yet in jungerl. See the README
+#docs:
+# erl -noshell -pa $(EDOCDIR) -pz $(STOOLSDIR) -pz $(XMERLDIR) -run edoc_run application "'$(APPNAME)'" '"."' '$(DOC_OPTS)' -s erlang halt
+
+all-subdirs:
+ @for dir in $(SUBDIRS); do \
+ (cd $$dir; $(MAKE) $(MAKETARGET) EBIN=../$(BINDIR) INCLUDE=../$(INCDIR)); \
+ done
+
+clean:
+ $(MAKE) MAKETARGET="distclean" all-subdirs
+
+distclean:
+ $(MAKE) MAKETARGET="distclean" all-subdirs
+
+realclean:
+ $(MAKE) MAKETARGET="realclean" all-subdirs
+ -rm -f $(DOCDIR)/*.html $(DOCDIR)/edoc-info $(DOCDIR)/stylesheet.css
View
@@ -0,0 +1,91 @@
+proc_reg - a generic process registration facility
+--------------------------------------------------
+
+Author: Ulf Wiger <ulf.wiger@ericsson.com>
+Date : 2004-07-08
+Requires : OTP R9C-2 or later (requires the ets:insert_new/2 function)
+
+
+This application supports local registration of processes using any erlang
+term. Furthermore, each process can register by several different
+names at the same time.
+
+The application must be started for the registration functions to
+work. Two permanent processes are created: a registry administration
+process (proc_reg), and a table owner process. The proc_reg process is
+designed to be fault-tolerant, and is able to restart without losing
+track of registered processes. If the table owner process dies, the
+application is terminated and, if the application were started as
+'permanent', so is the node. This is the recommended setup.
+The registration functions will stop working if the tables are not
+present.
+
+
+Functions:
+----------
+reg(Name, Pid) -> true | exit(badarg)
+unreg(Name) -> true | exit(badarg)
+where(Name) -> pid() | undefined
+send(Name, Msg) -> Msg | exit(badarg)
+
+Semantics:
+----------
+The functions have been designed to have the same semantics as the
+built-in registration functions as far as possible (except for
+allowing arbitrary terms and multiple registrations per process.)
+For example, the following function is guaranteed to return
+'undefined':
+
+serialized(Name, Pid) ->
+ true = proc_reg:reg(Name, Pid),
+ exit(Pid, foo),
+ proc_reg:where(Name).
+
+That is, the application is written to honour the 'Principle of Least
+Astonishment', without sacrificing performance. The overhead should be
+very small, and reg/unreg/where/send do not cause and synchronous
+calls to the proc_reg process, EXCEPT IN ONE CASE:
+
+If a name has been registered, the process dies, and another process
+immediately thereafter tries to register the same name, it could
+happen that the proc_reg process has not yet been informed that the
+first process died. In this case, the re-registration will first fail,
+then the reg/2 function will notice that the name is registered to a
+non-existing process, and make a synchronous call to the proc_reg
+process (which runs on high priority), telling it to immediately audit
+the dead process; then, the reg/2 function will try the registration
+once more. It is not impossible, but highly unlikely, that the
+registration attempt will fail even the second time, but this is then
+due to a race condition with another process, and not the fault of
+proc_reg.
+
+BTW, Pid must be local, just like with the built-in functions.
+
+Test suite:
+-----------
+In proc_reg/test/ there is a test suite, designed to work with the OTP
+test server. Please note that there is a hard-coded path in the
+*_SUITE.erl modules (in init_per_testcase/2.) This must be changed
+before the test suites can be run. Wiewing the source code in the test
+suite should give some detailed insight into the exact semantics of
+proc_reg.
+
+Builder:
+--------
+The proc_reg application was designed to work with 'builder'.
+Cd to the proc_reg/ directory, start an erlang node with 'builder' in
+the path, and type builder:go(), and you should get a working start
+script in priv/ (works well in UNIX, YMMV on Win32). This start script
+will start an erlang node with kernel, stdlib, sasl, and proc_reg all
+running - quite handy for performing simple tests.
+
+Future work:
+------------
+- Write edoc-based documentation
+- Add utility functions, like registered_names()
+- Measure performance (should be excellent)
+- Make the test suites relocatable.
+
+Please send comments and improvement suggestions.
+
+Ulf Wiger
@@ -0,0 +1,9 @@
+{application,proc_reg,
+ [{vsn,"BLDR"},
+ {description,"Local process registration facility"},
+ {id,[]},
+ {modules,[proc_reg_sup,proc_reg,proc_reg_tabs]},
+ {registered,[]},
+ {applications,[kernel,stdlib]},
+ {env,[]},
+ {mod,{proc_reg_sup,[]}}]}.
View
@@ -0,0 +1,27 @@
+EBIN=../ebin
+INCLUDE=../inc
+EMULATOR=beam
+ERL_COMPILE_FLAGS += -I $(INCLUDE) +warn_unused_vars +nowarn_shadow_vars +warn_unused_import
+
+SOURCES= proc_reg.erl \
+ proc_reg_tabs.erl \
+ proc_reg_sup.erl
+
+OBJECTS=$(SOURCES:%.erl=$(EBIN)/%.$(EMULATOR))
+
+
+.SUFFIXES: .erl .$(EMULATOR)
+
+
+all: $(OBJECTS)
+
+clean:
+ -rm -f $(OBJECTS)
+
+distclean: clean
+
+realclean: clean
+
+$(EBIN)/%.$(EMULATOR):%.erl
+ erlc -W $(ERL_COMPILE_FLAGS) -o $(EBIN) $<
+
@@ -0,0 +1,5 @@
+[
+ {description, "Local process registration facility"},
+ {mod, {proc_reg_sup, []}}
+].
+
@@ -0,0 +1,173 @@
+%%%-------------------------------------------------------------------
+%%% File : proc_reg.erl
+%%% Author : Ulf Wiger <etxuwig@wsb221>
+%%% Description :
+%%%
+%%% Created : 2 Jul 2004 by Ulf Wiger <etxuwig@wsb221>
+%%%-------------------------------------------------------------------
+-module(proc_reg).
+-behaviour(gen_server).
+
+-export([reg/2,
+ unreg/1,
+ where/1,
+ send/2]).
+
+
+-export([start_link/0]).
+-export([init/1,
+ handle_call/3,
+ handle_cast/2,
+ handle_info/2,
+ terminate/2,
+ code_change/3]).
+
+
+reg(Id, Pid) when pid(Pid), node(Pid) == node() ->
+ case do_reg(Id, Pid) of
+ false ->
+ case where(Id) of
+ undefined ->
+ gen_server:call(proc_reg, {check_id, Id}),
+ case do_reg(Id, Pid) of
+ false ->
+ exit(badarg);
+ true ->
+ true
+ end;
+ OtherPid when pid(OtherPid) ->
+ exit(badarg)
+ end;
+ true ->
+ true
+ end;
+reg(_Id, _RemoteOrBadPid) ->
+ exit(badarg).
+
+
+do_reg(Id, Pid) ->
+ Now = erlang:now(),
+ RegEntry = {Id, Pid, Now},
+ case ets:insert_new(proc_reg, RegEntry) of
+ false ->
+ false;
+ true ->
+ gen_server:cast(proc_reg, {new_reg, Id, Pid, Now}),
+ true
+ end.
+
+
+unreg(Id) ->
+ case ets:lookup(proc_reg, Id) of
+ [] ->
+ exit(badarg);
+ [{_, Pid, Check} = Obj] ->
+ ets:delete_object(proc_reg, Obj), % Safe because of Check
+ gen_server:cast(proc_reg, {unreg, Id, Pid, Check}),
+ true
+ end.
+
+
+where(Id) ->
+ case ets:lookup(proc_reg, Id) of
+ [{_, Pid, _}] ->
+ case erlang:is_process_alive(Pid) of
+ true ->
+ Pid;
+ false ->
+ %% This could happen for two reasons:
+ %% - race condition; pid just died, but proc_reg has not yet
+ %% been notified. This is not necessarily an uncommon case, esp.
+ %% if we have code like:
+ %% exit(Pid, kill), proc_reg:whereis(foo).
+ %% - A bug in this module, causing an inconsistency.
+ %% We will not write code to address the second possiblity.
+ undefined
+ end;
+ [] ->
+ undefined
+ end.
+
+
+send(Id, Msg) ->
+ case where(Id) of
+ Pid when pid(Pid) ->
+ Pid ! Msg;
+ undefined ->
+ exit(badarg)
+ end.
+
+
+
+%%%-------------------------------------------------------------------
+
+start_link() ->
+ gen_server:start_link({local, proc_reg}, ?MODULE, [], []).
+
+init([]) ->
+ process_flag(priority, high),
+ proc_reg_tabs:attach(),
+ ok = set_monitors(),
+ {ok, []}.
+
+
+handle_call({check_id, Id}, _From, S) ->
+ case ets:lookup(proc_reg, Id) of
+ [] ->
+ ok;
+ [{_Id, Pid, _Now}] ->
+ case erlang:is_process_alive(Pid) of
+ true ->
+ ok;
+ false ->
+ process_is_down(Pid)
+ end
+ end,
+ {reply, ok, S};
+handle_call(_Req, _From, S) ->
+ {stop, unknown_call, S}.
+
+handle_cast({new_reg, Id, Pid, Check}, S) ->
+ MRef = erlang:monitor(process, Pid),
+ ets:insert(proc_reg_rev, {{Pid, Id}, MRef, Check}),
+ {noreply, S};
+handle_cast({unreg, Id, Pid, Check}, S) ->
+ case ets:lookup(proc_reg_rev, {Pid, Id}) of
+ [{_, MRef, Check}] -> % Here, we use Check
+ erlang:demonitor(MRef),
+ ets:delete(proc_reg_rev, {Pid, Id});
+ _ ->
+ skip
+ end,
+ {noreply, S}.
+
+handle_info({'DOWN', _MRef, process, Pid, _Why}, S) ->
+ process_is_down(Pid),
+ {noreply, S}.
+
+process_is_down(Pid) ->
+ Pattern = {{Pid,'_'},'_','_'},
+ Regs = ets:match_object(proc_reg_rev, Pattern),
+ lists:foreach(
+ fun({{_,Id},_,_}) ->
+ ets:delete(proc_reg, Id)
+ end, Regs),
+ ets:match_delete(proc_reg_rev, Pattern).
+
+terminate(_Reason, _S) ->
+ ok.
+
+code_change(_FromVsn, State, _Extra) ->
+ {ok, State}.
+
+%%%-------------------------------------------------------------------
+
+set_monitors() ->
+ io:format("set_monitors...~n", []),
+ ets:delete_all_objects(proc_reg_rev),
+ ets:foldl(fun({Id, Pid, Check}, Acc) ->
+ MRef = erlang:monitor(process, Pid),
+ ets:insert(proc_reg_rev, {{Pid, Id}, MRef, Check}),
+ Acc
+ end, ok, proc_reg).
+
@@ -0,0 +1,2 @@
+{release, {"proc_reg","1.0"},
+ [kernel, stdlib, sasl]}.
Oops, something went wrong.

0 comments on commit 20d8678

Please sign in to comment.