[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