ensure_started

Peter H|gfeldt peter@REDACTED
Fri Mar 21 16:22:12 CET 2003


On Feb 1, 1997 I wrote the following, addressed to the OTP developing
team. I think it is still valid. 

/Peter

"Sometimes I see code like the following (in real applications or in
examples in documents):

start(Name, Module, Args) ->
    case whereis(Name) of
        undefined ->
            Pid = spawn(Module, init, Args),
            register(Name, Pid),
            Pid;
        Other ->
            Other
    end.

That is bad programming:

1.      Concurrency: You do not know what happens between whereis/1 
        and spawn/3. Some other process might be scheduled in between, 
        and call start/2.

2.      If register/2 fails, we already have a process registered
        as Name, and an additional process (it was spawned before we
        tried to register) doing the same things as the registered one,
        but not known by a registered name.

        If the purpose of the newly spawned process is to control 
        system unique resources, we might have two processes
        competing for the same resources (think e.g. of two Erlang
        shells competing for user input).

What to do:

        Put the register/2 part in the Module:init function, so that
        the newly created process fails (and terminates if properly
        programmed) if the name is already registered. Then there 
        will be at most one process doing 'the thing'.

In fact, in the good ol'BOS, we imposed the rule that registration
should be done by the process itself in its "init-phase", i.e. before
it enters its service loop (the last thing to do in the init-phase was
to acknowlegde the start to the supervisor by a start-ack message).

        (The above code *might* be acceptable if the process started
        is controlled by (an OTP) supervisor (the spawn should then be
        replace by a spawn_link) and if it is obvious that only the
        supervisor calls Module:start/1. But then the whereis/1 
        is superfluous provided the supervisor knows what it is doing,
etc).

The difficulty is doing a thing precisely once: not doing it at all, or 
doing it several times is easy.

By having the register/2 in the process itself, we assure that
there is at most one process of the kind running, and by having
it controlled by a supervisor we will have precisely one running
most of the time (the supervisor will restart a process that
terminates).

Concurrency is more difficult than you believe."


On 21 Mar 2003, Kent Boortz wrote:

> 
> Ulf Wiger <etxuwig@REDACTED> writes:
> > I posted the following suggestion a few weeks ago, now
> > slightly modified:
> > 
> > ensure_started() ->
> >    Id = {{?MODULE,ensure_started}, self()},
> >    global:trans(
> >        Id, fun() ->
> >                case whereis(?MODULE) of
> >                   undefined ->
> >                      Pid = proc_lib:spawn(?MODULE,init,[]),
> >                      register(?MODULE, Pid),
> >                      {ok, Pid};
> >                   Pid when pid(Pid) ->
> >                      {ok, Pid}
> >                end
> >            end, [node()]).
> > 
> > 
> > Since you know that the ensure_started() call is always
> > local you can use global:trans(Id, Fun, [node()]) which will
> > cut it down to two gen_server calls to the nearest global
> > name server.
> 
> As an experiment I wrote a new bif that could simplify coding of
> things like this. The bif will as an "atomic operation" start and
> register a process if a process with that name doesn't exist.
> If it does exist the pid of the existing process will be returned.
> If 'link' is in SpawnOpts a link is created to the started/existing
> process.
> 
> Haven't implemented the user API but it should look something like
> 
>   erlang:ensure_started(RegisteredName, FunToRun, SpawnOpts)
> 
> and maybe a version for remote start
> 
>   erlang:ensure_started(RegisteredName, Node, FunToRun, SpawnOpts)
> 
> With these functions it should be easy to implement a new function
> proc_lib:ensure_started(...) that should cover what the code above
> try to do.
> 
> Any thoughts?
> 
> kent
> 




More information about the erlang-questions mailing list