erlanggen-serverhotswap

Erlang code_change and local function call


I am not sure how to call a local function in a module so that after the code change the latest version of the code will be used. See the following example:

1  -module(test).
2
3  -export([start/0, call/1]).
4  -export([loop/0, add/1]).
5
6  start() ->
7      register(foo, spawn(test, loop, [])).
8
9  call(X) ->
10     foo ! {self(), X},
11     receive
12         Y -> Y
13 end.
14
15 loop() ->
16     receive
17         {Pid, Z} -> Pid ! add(Z)
18     end,
19     loop().
20
21 add(N) ->
22     N + 1.

The function that will be changed is add/1. In order to use the latest version of the function, the call of add/1 (line 17) should be fully qualified function call {Pid, Z} -> Pid ! ?MODULE:add(Z). When I try it, I get this:

1> c(test). 
{ok,test}
2> test:start(). 
true
3> test:call(1).
2

line 22 changed to N + 2

4> c(test).     
{ok,test}
5> test:call(1).
3

line 22 changed again to N + 3

6> c(test).     
{ok,test}
7> test:call(1).
** exception error: bad argument
    in function  test:call/1 (test.erl, line 10)

Why do I get this error?


Solution

  • I believe that you need to eventually call the fully qualified version of the loop/0 function instead of the add/1 function in order to load and use the new module. The code loading mechanism is prepared to handle two running versions of a module at once, and your example with N+3 is the third load of the module -- and the first versions is forcibly removed.

    Try instead this loop:

    15 loop() ->
    16     receive
    17         {Pid, Z} -> Pid ! add(Z)
    18     end,
    19     ?MODULE:loop().
    

    I've changed it to reload the newest version on next execution of the loop/0.

    I believe more common is to use a reload message or similar that will directly call the main loop explicitly, to avoid the overhead of constantly reloading the module on every request.