4
votes

I'm a beginner at erlang programming. To understand hot code loading better, I used the example from Wikipedia (I added responses to the sending Pid for debugging):

%% A process whose only job is to keep a counter.
  %% First version
  -module(counter).
  -export([start/0, codeswitch/2]).

  start() -> loop(0).

  loop(Sum) ->
    receive
       {increment, Count} ->
          loop(Sum+Count);
        %% modified code, which will be loaded:
        % reset -> 
        %     loop(0);
       {counter, Pid} ->
          Pid ! {counter, Sum},
          loop(Sum);
        {code_switch, Pid} ->
          Pid ! {switch, Sum},
          ?MODULE:codeswitch(Pid, Sum)
          % Force the use of 'codeswitch/2' from the latest MODULE version
    end.

  codeswitch(FromPid, Sum) -> 
      FromPid ! {switched, Sum},
      loop(Sum).

All is good. I can load the module via c(counter). in the shell, spawn a new process via Pid = spawn(fun counter:start/0). and send messages to the spawned process. When I now add a new pattern to the receive expression reset -> loop(0) and reload the code via c(counter)., everything works as expected, new code is loaded, Sum keeps its incremented value etc.

But when I send the {code_switch, self()} message, Sum gets reset to 0 when loop(Sum) is called (FromPid ! {switched, Sum} in the call to FromPid ! {switched, Sum} still returns correct state).

What am I missing, why does my state go away after the first call to an codeswitched function?

Thanks for your help!

| 18 | Pid ! {counter, self()}.
{counter,<0.49.0>}
| 19 | flush().
Shell got {counter,6}
ok
| 20 | Pid ! {code_switch, self()}.
{code_switch,<0.49.0>}
| 21 | flush().
Shell got {switch,6}
Shell got {switched,6}
ok
| 22 | Pid ! {counter, self()}.
{counter,<0.49.0>}
| 23 | flush().
Shell got {counter,0}
ok

I put io:format("DebugInfo:~p~n", [Sum]) as the first expression in loop. Result is:

12> Pid ! {code_switch, self()}.
DebugInfo:3
{code_switch,<0.33.0>}
DebugInfo:0
13> flush().
Shell got {switch,3}
Shell got {switched,3}
ok

EDIT: I found that when I spawn the process via spawn/3, aka spawn(counter, start, [])., this works. When I spawn the process via spawn/1, aka spawn(fun counter:start/0), this doesn't work. Is this expected behavior? What am I missing?

Documentation states for spawn/1:

Returns the process identifier of a new process started by the application of Fun to the empty list []. Otherwise works like spawn/3.

EDIT: .... Aaaand after trying to replicate this on an Ubuntu virtual machine (where it didn't happen), I am now also unable to reproduce this (and will test my memory for corruption now..)

1

1 Answers

2
votes

This is not the behavior that I am seeing when testing your program:

25> LPid ! {counter, self()}.
{counter,<0.39.0>}
26> flush().
Shell got {counter,6}
ok
27> c(counter).
{ok,counter}
28> LPid ! {counter, self()}.
{counter,<0.39.0>}
29> flush().                 
Shell got {counter,6}
ok
30> LPid ! {increment, 2}.
{increment,2}
31> LPid ! {counter, self()}.
{counter,<0.39.0>}
32> flush().                 
Shell got {counter,8}
ok
33> LPid ! {code_switch, self()}.
{code_switch,<0.39.0>}
34> flush().                     
Shell got {switch,8}
Shell got {switched,8}
ok
35> LPid ! {counter, self()}.    
{counter,<0.39.0>}
36> flush().                 
Shell got {counter,8}
ok

Can you maybe add some logs like io:format("DebugInfo:~p~n", [Sum]). to some of the functions to see what's going on?