ensure_started

classic Classic list List threaded Threaded
27 messages Options
12
Reply | Threaded
Open this post in threaded view
|

ensure_started

Vlad Dumitrescu (EAW)
Hi,

I wonder about a thing. The following fun is an often used way to ensure that one uses "the one and only" server called ?MODULE.

ensure_started() ->
    case whereis(?MODULE) of
        undefined ->
            Pid = proc_lib:spawn(?MODULE, init, []),
            register(?MODULE, Pid),
            {ok, Pid};
        Pid ->
            {ok, Pid}
    end.

Now I just had to implement this functionality and I noticed that the above isn't really correct. In fact, I even got a case during tests when the error showed up.

start() ->
    case whereis(?SERVER) of
        undefined ->
            Pid = spawn(fun init/0),
            %% some other process might have already registered it
            %% since we last checked
            case catch register(?SERVER, Pid) of
                true ->
                    %% we're the real thing
                    Pid;
                _ ->
                    %% other process got there first, use that one
                    Pid ! stop,
                    %% try again!
                    start()
            end;
        Pid ->
            Pid
    end.

I thought to share this, as it might cause confusion. Maybe it's not so often the scheduler will interrrupt start() just in the wrong place, but as I said, it happened to me (in relation with the debugger running).

best regards,
Vlad


Reply | Threaded
Open this post in threaded view
|

ensure_started

Björn Gustavsson-3
"Vlad Dumitrescu (EAW)" <Vlad.Dumitrescu> writes:

> Hi,
>
> I wonder about a thing. The following fun is an often used way to ensure that one uses "the one and only" server called ?MODULE.
>
> ensure_started() ->
>     case whereis(?MODULE) of
> undefined ->
>    Pid = proc_lib:spawn(?MODULE, init, []),
>    register(?MODULE, Pid),
>    {ok, Pid};
> Pid ->
>    {ok, Pid}
>     end.
>

A safer way is to move the call to register/2 into the init/0 function.

If two processes happens to start at almost the same time, the second
process will crash when calling register/2.

/Bjorn

--
Bj?rn Gustavsson            Ericsson Utvecklings AB
bjorn      ?T2/UAB/F/P
                            BOX 1505
+46 8 727 56 87    125 25 ?lvsj?


Reply | Threaded
Open this post in threaded view
|

ensure_started

Richard Carlsson-4
In reply to this post by Vlad Dumitrescu (EAW)

Yes, I've found that this situation will typically happen every time you
write something like a test script that launches a number of clients;
then each of them will try to make sure the server is started, and since
calling 'register' seems to always cause the process to be swapped out,
all but one of them will fail in 'register'.

        /Richard

On Thu, 27 Feb 2003, Vlad Dumitrescu (EAW) wrote:

> Now I just had to implement this functionality and I noticed that the
> above isn't really correct. In fact, I even got a case during tests
> when the error showed up.
>
> start() ->
>     case whereis(?SERVER) of
> undefined ->
>    Pid = spawn(fun init/0),
>    %% some other process might have already registered it
>    %% since we last checked
>    case catch register(?SERVER, Pid) of
> true ->
>    %% we're the real thing
>    Pid;
> _ ->
>    %% other process got there first, use that one
>    Pid ! stop,
>    %% try again!
>    start()
>    end;
> Pid ->
>    Pid
>     end.
>
> I thought to share this, as it might cause confusion. Maybe it's not
> so often the scheduler will interrrupt start() just in the wrong
> place, but as I said, it happened to me (in relation with the debugger
> running).
>
> best regards,
> Vlad
>

Richard Carlsson (richardc)   (This space intentionally left blank.)
E-mail: Richard.Carlsson WWW: http://user.it.uu.se/~richardc/


Reply | Threaded
Open this post in threaded view
|

ensure_started

Luke Gorrie-3
In reply to this post by Vlad Dumitrescu (EAW)
"Vlad Dumitrescu (EAW)" <Vlad.Dumitrescu> writes:

> Hi,
>
> I wonder about a thing. The following fun is an often used way to ensure that one uses "the one and only" server called ?MODULE.
>
> ensure_started() ->
>     case whereis(?MODULE) of
> undefined ->
>    Pid = proc_lib:spawn(?MODULE, init, []),
>    register(?MODULE, Pid),
>    {ok, Pid};
> Pid ->
>    {ok, Pid}
>     end.

This looks like code from fdoc. Sadly, I knew this was incorrect when
I wrote it, as is my ref()-less and monitor()-less 'call'
implementation when you get right down to it. Oops :-(

The trouble with registering in the server's init() function is that
then you need to synchronize the guy calling ensure_started() somehow,
since he needs to get the pid of the process that "wins the race." So
for "ensure_started" functions, I do like your second alternative with
the catch-and-retry.

Cheers,
Luke



Reply | Threaded
Open this post in threaded view
|

ensure_started

Ulf Wiger-4
In reply to this post by Vlad Dumitrescu (EAW)

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.

(http://www.erlang.org/ml-archive/erlang-questions/200302/msg00391.html)

/Uffe

On Thu, 27 Feb 2003, Vlad Dumitrescu (EAW) wrote:

>Hi,
>
>I wonder about a thing. The following fun is an often used
>way to ensure that one uses "the one and only" server
>called ?MODULE.
>
>ensure_started() ->
>    case whereis(?MODULE) of
> undefined ->
>    Pid = proc_lib:spawn(?MODULE, init, []),
>    register(?MODULE, Pid),
>    {ok, Pid};
> Pid ->
>    {ok, Pid}
>    end.
>
>Now I just had to implement this functionality and I noticed that the above isn't really correct. In fact, I even got a case during tests when the error showed up.
>
>start() ->
>    case whereis(?SERVER) of
> undefined ->
>    Pid = spawn(fun init/0),
>    %% some other process might have already registered it
>    %% since we last checked
>    case catch register(?SERVER, Pid) of
> true ->
>    %% we're the real thing
>    Pid;
> _ ->
>    %% other process got there first, use that one
>    Pid ! stop,
>    %% try again!
>    start()
>    end;
> Pid ->
>    Pid
>    end.
>
>I thought to share this, as it might cause confusion. Maybe it's not so often the scheduler will interrrupt start() just in the wrong place, but as I said, it happened to me (in relation with the debugger running).
>
>best regards,
>Vlad
>

--
Ulf Wiger, Senior Specialist,
   / / /   Architecture & Design of Carrier-Class Software
  / / /    Strategic Product & System Management
 / / /     Ericsson AB, Connectivity and Control Nodes




Reply | Threaded
Open this post in threaded view
|

ensure_started

Luke Gorrie-3
Ulf Wiger <etxuwig> 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.
>
> (http://www.erlang.org/ml-archive/erlang-questions/200302/msg00391.html)

Sounds like something for the Jungerl, maybe a catchy 'procutil'
module in the 'msc' application?

Can any of you guys at Ericsson get into the Jungerl through your
firewall? If not I'm keen to help sort it out.

Cheers,
Luke



Reply | Threaded
Open this post in threaded view
|

ensure_started

Ulf Wiger-4
On 27 Feb 2003, Luke Gorrie wrote:

>Can any of you guys at Ericsson get into the Jungerl
>through your firewall? If not I'm keen to help sort it out.

It scares me to think that someone at Nortel is capable of
messing with our firewall at Ericsson... ;-)

I know Bj?rn G is able to use SourceForge from his network,
but I've not been able to do it from mine. I started
thinking that it was just I who was incompetent, but when I
tried it at home (from cygwin, even), it worked like a
charm.


Now, I try not to work too much when I'm at home... (:

/Uffe
--
Ulf Wiger, Senior Specialist,
   / / /   Architecture & Design of Carrier-Class Software
  / / /    Strategic Product & System Management
 / / /     Ericsson AB, Connectivity and Control Nodes



Reply | Threaded
Open this post in threaded view
|

ensure_started

Luke Gorrie-3
Ulf Wiger <etxuwig> writes:

> On 27 Feb 2003, Luke Gorrie wrote:
>
> >Can any of you guys at Ericsson get into the Jungerl
> >through your firewall? If not I'm keen to help sort it out.
>
> It scares me to think that someone at Nortel is capable of
> messing with our firewall at Ericsson... ;-)

I was thinking of tweaking configs at sourceforge. Honest. :-)

> I know Bj?rn G is able to use SourceForge from his network,
> but I've not been able to do it from mine. I started
> thinking that it was just I who was incompetent, but when I
> tried it at home (from cygwin, even), it worked like a
> charm.
>
>
> Now, I try not to work too much when I'm at home... (:

The basic firewall requirement should be to let SSH through, since
it's the usual transport for CVS on sourceforge. I would guess this is
being blocked by your firewall.

Two other less attractive possibilities:

Anonymous (read-only) access uses a different port (2401), so it may
get through even if SSH is blocked. For anonymous access, you'd use
this cvsroot:

  :pserver:anonymous:/cvsroot/jungerl

And would need to "cvs -d :pserver:... login" with an empty password,
just the once, before doing any checkouts.

Yet less attractive, you can download a daily snapshot of the whole
CVS repository from:

  http://cvs.sourceforge.net/cvstarballs/jungerl-cvsroot.tar.gz

Then unpack that and do a checkout from it locally.

In both of those cases, you'd need to make diffs and commit them from
some machine that has access.

Also, I'll probably be laughed at for this, but I just asked the
sourceforge guys to consider accepting SSH connections on port 80 of
cvs.sourceforge.net. Disgusting.

Cheers,
Luke



Reply | Threaded
Open this post in threaded view
|

ensure_started

Luke Gorrie-3
Luke Gorrie <luke> writes:

> Also, I'll probably be laughed at for this, but I just asked the
> sourceforge guys to consider accepting SSH connections on port 80 of
> cvs.sourceforge.net. Disgusting.

It turns out they have already done this, and
'cvs-ssh.sourceforge.net' will accept CVS-over-SSH connections on
ports 80 and 443. The details are at
https://sourceforge.net/docman/display_doc.php?docid=768&group_id=1#firewall

Of course this won't help if that whole IP address has been especially
firewalled off.

Cheers,
Luke



Reply | Threaded
Open this post in threaded view
|

ensure_started

Kent Boortz-2
In reply to this post by Ulf Wiger-4

Ulf Wiger <etxuwig> 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


Reply | Threaded
Open this post in threaded view
|

ensure_started

Peter H|gfeldt

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> 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
>



Reply | Threaded
Open this post in threaded view
|

ensure_started

Martin Bjorklund-2
Peter H|gfeldt <peter> wrote:
>
> On Feb 1, 1997 I wrote the following, addressed to the OTP developing
> team. I think it is still valid.

I was just writing something along these lines.  I fully agree!  Don't
introduce another bif for something you can do anyway. (which I hope
was your point ;)


/martin


Reply | Threaded
Open this post in threaded view
|

ensure_started

Chris Pressey
In reply to this post by Peter H|gfeldt
On Fri, 21 Mar 2003 16:22:12 +0100 (MET)
Peter H|gfeldt <peter> wrote:

> On Feb 1, 1997 I wrote the following, addressed to the OTP developing
> team. I think it is still valid.
> [...]
>         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'.

Wow, six years...

This got me thinking.  Although this may sound a bit harsh - isn't
register/2 perhaps a design flaw?  If it were register/1, and always
applied to self(), it would force you code process registration this way,
by the newly created process.

Is there ever a truly convincing reason to register a process that isn't
self()?

> [...]
> Concurrency is more difficult than you believe."

I believe it :)

I was wondering if there's a similar case when a process dies.  Is there a
way to absolutely ensure that a process'es mailbox is empty before it
dies?  Especially if more than one process is sending messages to the
process.  Example:

  server() ->
    receive
      {Pid, Data} ->
        Reply = do_stuff(Data),
        Pid ! Reply,
        server();
      shut_yourself_down ->
        % --> mightn't we get another message right here?
        % --> if we do, the sender will never get a reply
        exit(was_shut_down)
    end.

-Chris


Reply | Threaded
Open this post in threaded view
|

ensure_started

Vladimir Sekissov
Good day,

loop() ->
  receive
   Msg ->
    do_something,
    loop();
   please_die ->
     receive
       Msg -> % there are some messages, continue
         loop()
     after 0 -> % nobody wants to talk to me
       exit(shutdown)
     end
  end.

Best Regards,
Vladimir Sekissov

cpressey> I was wondering if there's a similar case when a process dies.  Is there a
cpressey> way to absolutely ensure that a process'es mailbox is empty before it
cpressey> dies?  Especially if more than one process is sending messages to the
cpressey> process.  Example:
cpressey>
cpressey>   server() ->
cpressey>     receive
cpressey>       {Pid, Data} ->
cpressey>         Reply = do_stuff(Data),
cpressey>         Pid ! Reply,
cpressey>         server();
cpressey>       shut_yourself_down ->
cpressey>         % --> mightn't we get another message right here?
cpressey>         % --> if we do, the sender will never get a reply
cpressey>         exit(was_shut_down)
cpressey>     end.
cpressey>
cpressey> -Chris


Reply | Threaded
Open this post in threaded view
|

ensure_started

Ulf Wiger-4
In reply to this post by Chris Pressey
On Sun, 23 Mar 2003, Chris Pressey wrote:

>I was wondering if there's a similar case when a process
>dies.  Is there a way to absolutely ensure that a
>process'es mailbox is empty before it dies?  Especially if
>more than one process is sending messages to the process.

The mdisp contrib has a pattern for this. The simplest way
to check from the outside if a process has any messages
pending is process_info(P, message_queue_len), which
returns the length of P's message queue.

The mdisp 'sleep' protocol was designed to handle the
killing of temporary threads without losing any messages,
and without consuming any messages detected during the
'sleep' negotiation. That last one may be of minor
importance, but I think that the administrative code should
leave it to the programmer to receive his own messages.


/Uffe
--
Ulf Wiger, Senior Specialist,
   / / /   Architecture & Design of Carrier-Class Software
  / / /    Strategic Product & System Management
 / / /     Ericsson AB, Connectivity and Control Nodes



Reply | Threaded
Open this post in threaded view
|

ensure_started

Chris Pressey
In reply to this post by Vladimir Sekissov
On Sun, 23 Mar 2003 23:07:23 +0500 (YEKT)
Vladimir Sekissov <svg> wrote:

> Good day,
>
> loop() ->
>   receive
>    Msg ->
>     do_something,
>     loop();
>    please_die ->
>      receive
>        Msg -> % there are some messages, continue
>          loop()
>      after 0 -> % nobody wants to talk to me

Is there a guarantee that you won't get a message right here?

>        exit(shutdown)
>      end
>   end.
>
> Best Regards,
> Vladimir Sekissov

-Chris


Reply | Threaded
Open this post in threaded view
|

ensure_started

Vladimir Sekissov
Good day,

cpressey> >      after 0 -> % nobody wants to talk to me
cpressey>
cpressey> Is there a guarantee that you won't get a message right here?

>From "Concurent Programming in Erlang" p.76:

        A timeout of 0 means that the timeout will occur immediately,
        but the system tries all messages currently in the mailbox first

If your mailbox is empty you haven't time to receive any other message
if you exit immediately.

I believe that mailbox scanning is atomic operation. There were some
explanations in this list how system changes management policy for
processes with huge mailboxes.

Best Regards,
Vladimir Sekissov

cpressey> > loop() ->
cpressey> >   receive
cpressey> >    Msg ->
cpressey> >     do_something,
cpressey> >     loop();
cpressey> >    please_die ->
cpressey> >      receive
cpressey> >        Msg -> % there are some messages, continue
cpressey> >          loop()
cpressey> >      after 0 -> % nobody wants to talk to me
cpressey>
cpressey> Is there a guarantee that you won't get a message right here?
cpressey>
cpressey> >        exit(shutdown)
cpressey> >      end
cpressey> >   end.
cpressey> >
cpressey> > Best Regards,
cpressey> > Vladimir Sekissov
cpressey>
cpressey> -Chris


Reply | Threaded
Open this post in threaded view
|

ensure_started

Ulf Wiger-4
On Mon, 24 Mar 2003, Vladimir Sekissov wrote:

>Good day,
>
>cpressey> >      after 0 -> % nobody wants to talk to me
>cpressey>
>cpressey> Is there a guarantee that you won't get a message right here?
>
>From "Concurent Programming in Erlang" p.76:
>
> A timeout of 0 means that the timeout will occur immediately,
> but the system tries all messages currently in the mailbox first
>
>If your mailbox is empty you haven't time to receive any
>other message if you exit immediately.
>
>I believe that mailbox scanning is atomic operation. There
>were some explanations in this list how system changes
>management policy for processes with huge mailboxes.

Mailbox scanning is an atomic operation, yes, but there is
of course still a chance that you will be scheduled out
before the exit(shutdown).

In the case of mdisp, this is not considered a problem,
since the process should only go to sleep in places where
the next message always comes through mdisp (the rule being:
it's OK for temporary processes to talk directly to each
other in transition states, but in stable state, messages
need to go through the mdisp process; alternatively, all
messages always go through the mdisp process.)

/Uffe
--
Ulf Wiger, Senior Specialist,
   / / /   Architecture & Design of Carrier-Class Software
  / / /    Strategic Product & System Management
 / / /     Ericsson AB, Connectivity and Control Nodes



Reply | Threaded
Open this post in threaded view
|

ensure_started

Joe Williams-2
In reply to this post by Peter H|gfeldt
On Fri, 21 Mar 2003, Peter H|gfeldt wrote:

>
> 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:
>
... etc.

  Indeed it is :-) - I often  wonder why we made spawn/3 a primitive and
not just spawn a "universal" empty  process and then send it a message
telling it what to do.


        Pid = spawn()
        Pid ! Fun()

where

        spawn() ->
           spawn(fun() ->
                   receive
                        Fun ->
                           Fun()
                   end
                 end).

  This  is  particularly useful  when  you  need  to setup  sets  of
mutually recursive processes, then you can say:

        Pid1 = spawn()
        Pid2 = spawn(),
        Pid1 ! fun() -> ... Pid2 ... end,
        Pid2 ! fun() -> ... Pid1 ... end,

  /Joe





Reply | Threaded
Open this post in threaded view
|

ensure_started

Ulf Wiger-4
On Mon, 24 Mar 2003, Joe Armstrong wrote:

>I often wonder why we made spawn/3 a primitive and not just
>spawn a "universal" empty process and then send it a
>message telling it what to do.
>
>
> Pid = spawn()
> Pid ! Fun()
>
>where
>
> spawn() ->
>   spawn(fun() ->
>   receive
> Fun ->
>   Fun()
>   end
> end).
>
>  This is particularly useful when you need to setup sets
>of mutually recursive processes, then you can say:
>
> Pid1 = spawn()
> Pid2 = spawn(),
> Pid1 ! fun() -> ... Pid2 ... end,
> Pid2 ! fun() -> ... Pid1 ... end,
>
>  /Joe

Interesting... but how long should a universal process live
before it gives up waiting for a fun thing to do?

Or should empty unreferenced processes be garbage-collected?
This would require a unified heap, or reference-counted
pids. Oh well, nothing that couldn't have been solved. (:

BTW, the reason you didn't do this from the beginning was
that Erlang didn't have funs back then.  ;-)

/Uffe
--
Ulf Wiger, Senior Specialist,
   / / /   Architecture & Design of Carrier-Class Software
  / / /    Strategic Product & System Management
 / / /     Ericsson AB, Connectivity and Control Nodes



12