[erlang-questions] Singleton Process

Bernard Duggan bernie@REDACTED
Tue Feb 3 10:57:03 CET 2009


Hi Edward,
	Your code is a pretty reasonable attempt at a basic implementation (and
I don't mean that in any derogatory or rude sense - much of my code from
not very long ago follows a similar pattern).
	The most obvious problem I can see with it is the race condition in
start() - if two processes call next_id() at the same time, they could
both call whereis(counter) and receive 'undefined' as the response -
they'd both then try to spawn  and register the process, and while both
spawns would succeed, only the first register() would.  Generally I find
it's best to spawn such "singleton" (I have no idea if that's the
correct terminology, sorry) processes at the start of your app, before
any calls to them are ever made rather than trying to spawn it
dynamically on first use.
	Other than that I can't see any serious problem with it.  I'd
personally be more inclined to implement something as simple as this
with an mnesia counter (which can handle both the disc persistence and
protected get/update operations for you in very few lines of code).
Others more experienced than I can probably suggest an even better way.
 For more complex things like resource allocation, though, I've found
this kind of pattern to be perfectly good.

Cheers,

Bernard

Edward Stow wrote:
> Hi,
> 
> I have been teaching myself Erlang and had an epiphany regarding the
> use of processes to record state without variables (in the imperative
> programming sense).
> 
> However, I would like to know if this is how you would produce a
> counter object that returns an incrementally increasing value.  The
> counter needs to be unique across processes. (A singleton process --
> is that a correct term in fp / erlang ).
> 
> -module(counter).
> -export([next_id/0]).
> 
> next_id() ->
>     next_id(start()).
> 
> start() ->
>     case whereis(counter) of
>         undefined ->
>             Pid = spawn(fun () -> loop(read_next_id()) end),
>             register(counter, Pid),
>             Pid;
>         Pid ->
>             Pid
>     end.
> 
> next_id(Pid) -> rpc(Pid, next_id).
> 
> rpc(Pid, Request) ->
>     Pid ! {self(), Request},
>     receive
>         {Pid, Response} ->
>             Response
>     end.
> 
> loop(Counter) ->
>     receive
>         {From, next_id} ->
>             From ! {self(), Counter},
>             save_next_id(Counter + 1),
>             loop(Counter + 1)
>     end.
> 
> read_next_id() ->
>     {ok, [Term]} = file:consult("next_id.dat"),
>     {next_id, Number} = Term,
>     Number.
> 
> save_next_id(Number) ->
>     {ok, S} = file:open("next_id.dat", write),
>     io:format(S, "~p.~n", [{next_id, Number}]),
>     file:close(S).
> 
> 
> Thanks
> 




More information about the erlang-questions mailing list