[erlang-questions] Is this code tail recursive?

Ulf Wiger ulf.wiger@REDACTED
Mon Sep 20 20:33:59 CEST 2010


On 20/09/2010 19:33, David Mercer wrote:
> On September 20, 2010, Anthony Molinaro wrote:
>> So for code change does this mean the new version of an imported module is
>> called?
>
> Yes.

But there is one case that is still a bit tricky.

Consider a module where you e.g. spawn some connector processes.
These would do some initialization, then call a blocking function,
placing the current module on the call stack until the function
returns.

If the module is purged during this time, the processes will be
killed.

Example:

dies() ->
     spawn_link(fun() ->
                        timer:sleep(10000),
                        io:fwrite("still alive~n", [])
                end).

Eshell V5.7.5  (abort with ^G)
1> c(mymod).
{module, mymod}
2> mymod:dies().
<0.61.0>
3> l(mymod).
{module,mymod}
4> l(mymod).
** exception exit: killed


In http:github.com/esl/plain_fsm/ I recently added a special
function to allow such blocking calls to be made in a tail_call
fashion to allow for soft upgrade.

Example (module tail_call_test):

p() ->
     spawn_link(fun() -> wait(mystate) end).

wait(S) ->
     ?tail_apply(fun() -> timer:sleep(10000) end, reply, S).

reply(ok, R, S) ->
     io:fwrite("reply -> ~p (S=~p)~n", [R,S]).

The macro expands to a slightly more complex function,
plain_fsm:tail_apply/5. The first argument of the macro is the
blocking function call. The second is the name of the function
that gets called to handle the return value, and S is the "state"
of the process.

5> tail_call_test:p().
<0.73.0>
reply -> ok (S=mystate)
6> tail_call_test:p().
<0.79.0>
7> c(tail_call_test,[{i,"../include"}]).
{ok,tail_call_test}
8> c(tail_call_test,[{i,"../include"}]).
{ok,tail_call_test}
9> c(tail_call_test,[{i,"../include"}]).
{ok,tail_call_test}
reply -> ok (S=mystate)

As you can tell, the process didn't die.

It is also possible to add a function, data_vsn() -> any(),
which is checked before and after the call. Plain_fsm will
call Mod:code_change/3 if the value of data_vsn() has
changed in the meantime.

code_change(From,S,Xtra) ->
     io:fwrite("code_change(~p, ~p, ~p)~n", [From,S,Xtra]),
     {ok, {upgraded, S}}.

10> tail_call_test:p().
<0.270.0>
11> c(tail_call_test,[{i,"../include"}]).
{ok,tail_call_test}
12> c(tail_call_test,[{i,"../include"}]).
{ok,tail_call_test}
code_change(0, mystate, tail_apply)
reply -> ok (S={upgraded,mystate})

Plain_fsm does something similar with its own version of hibernate(),
and includes a few other goodies that at least I find convenient.

BR,
Ulf W
-- 
Ulf Wiger
CTO, Erlang Solutions Ltd, formerly Erlang Training & Consulting Ltd
http://www.erlang-solutions.com


More information about the erlang-questions mailing list