[erlang-questions] what is the correct syntax to spawm a MFA with no arguments?

Richard Carlsson richardc@REDACTED
Thu May 1 22:12:29 CEST 2008


Hynek Vychodil wrote:
> Really? Are you sure? Well, then describe us how long live running 
> server turning around one loop/x function can change code of loop
> when you don't export loop/x function or some other function called
> from its loop. Just explain.

Don't mix up two different things: 1) starting a new process, and 2)
keeping a process running in a loop. What I'm saying is that to start
a process, there is no problem at all using a fun, or at least, using
a literal fun that just contains a tail call. The following would
be a bad idea:

     spawn(fun () -> loop(), io:format("done") end)

because the return address to the io:format call would be pushed on
the stack (the call to loop() is not tail recursive), and therefore,
this fun cannot be discarded as long as loop() is running. Not good.
But for spawn(fun loop/0) or spawn(fun () -> loop() end), there is no
reference left to the fun, as soon as the call to loop() is entered.

But _after_ the process has started, you still need the server loop
to execute a tail call to an exported function, using M:F(...) syntax
(or apply), in order to support code upgrade. This is a completely
different thing.

Normally, the starting point for a process is not the actual loop
function itself, but typically an init function which in its turn
enters the main loop (which could be an exported function). But even
if you start the process directly in the loop function, you can use
another function name for code upgrade.
For example:

   -export([start/0, upgrade/1]).
   start() -> spawn(fun loop(0) end).
   upgrade(S) -> loop(S).
   loop(S) ->
     receive
       X when is_integer(X) -> loop(S+X);
       upgrade -> ?MODULE:upgrade(S)
     end.

But preferably, the user interface (start, stop, etc.) and the server
loop with its entry point for upgrading should be in separate modules
to keep the interface cleaner. And in that case you might as well put
the init function in that other module as well. But you could still
use spawn(fun moo:init/0) or spawn(fun () -> moo:init(X, Y, Z) end)
to start the process; there is no need to use spawn/3.

Here are a couple of examples of mistakes you might make if you just
focus on the spawning of the process. The first one is so easy that
only beginners make it:

   -export([start/0, loop/1]).
   start() -> spawn(?MODULE, loop, [0]).
   loop(S) ->
     receive
       X when is_integer(X) -> loop(S+X)
     end.

(the actual server loop never checks for code upgrades, even though
it is using an exported function).

The second is one you might make and think that it's not a problem
because you're only using spawn/3 and fully qualified calls:

   -module(myserver).
   -export([start/0]).
   start() -> spawn(myserver_impl, init, [0]).

   -module(myserver_impl).
   -export([init/1, loop/1]).
   init(S) -> loop(S), ok.
   loop(S) ->
     receive
       X when is_integer(X) -> ?MODULE:loop(S+X)
     end.

Here, code upgrade will work once, but the second time you try it
your server process will be killed, because it is still holding on
to a return pointer to the first version of the module (note that
the call from init/1 to loop/1 is not a tail call), and Erlang
only supports two parallel versions of the code, and all processes
that need even older code will be killed.

There are two main points to understand when it comes to making
code upgrade work:

   - Your process must regularly (at least each time new code has
     been loaded) execute a tail-recursive non-local call.

   - You must not leave any return addresses on the process stack
     that point to old versions of the code, or store funs in the
     process state that point to old code.

It's the second point that's hardest to grasp, because you need
a bit of understanding of how the implementation of a functional
language with tail recursion optimization and garbage collection
works.

So, it can be quite easy to screw up code upgrade without using any
funs at all (which most people seem not to worry a lot about), and
there are really no problems with spawning new processes using
spawn(fun ...), which many people seem to worry about. Just relax
and learn to love the bomb. :-)

     /Richard



More information about the erlang-questions mailing list