I changed the original f4.erl
server code to contain an additonal clause in loop()
:
-export([loop/1]).
loop(Frequencies) ->
receive
{request, _Pid, switch_code} ->
f4:loop(Frequencies);
That is straight out of the erlang Compilation and Code Loading docs.
The new f4.erl
with the inject
functionality looks like this:
%% Based on code from
%% Erlang Programming
%% Francecso Cesarini and Simon Thompson
%% O'Reilly, 2008
%% http://oreilly.com/catalog/9780596518189/
%% http://www.erlangprogramming.org/
%% (c) Francesco Cesarini and Simon Thompson
-module(f4).
-export([start/0,allocate/0,deallocate/1,inject/1,stop/0]).
-export([init/0, loop/1]).
%% These are the start functions used to create and
%% initialize the server.
start() ->
register(f4,
spawn(f4, init, [])).
init() ->
Frequencies = {get_frequencies(), []},
loop(Frequencies).
get_frequencies() -> [10,11].
%% The Main Loop
loop(Frequencies) ->
receive
{request, Pid, allocate} ->
{NewFrequencies, Reply} = allocate(Frequencies, Pid),
Pid ! {reply, Reply},
loop(NewFrequencies);
{request, Pid , {deallocate, Freq}} ->
NewFrequencies = deallocate(Frequencies, Freq),
Pid ! {reply, ok},
loop(NewFrequencies);
{request, Pid, {inject, AddFreqs}} -> %<******* ADDED CODE *****
NewFrequencies = inject(Frequencies, AddFreqs),
Pid ! {reply, ok},
loop(NewFrequencies);
{request, Pid, stop} ->
Pid ! {reply, stopped}
end.
%% Functional interface
allocate() ->
f4 ! {request, self(), allocate},
receive
{reply, Reply} -> Reply
end.
deallocate(Freq) ->
f4 ! {request, self(), {deallocate, Freq}},
receive
{reply, Reply} -> Reply
end.
inject(AddFreqs) -> %<******** ADDED CODE ******
f4 ! {request, self(), {inject, AddFreqs}},
receive
{reply, Reply} -> Reply
end.
stop() ->
f4 ! {request, self(), stop},
receive
{reply, Reply} -> Reply
end.
%% The Internal Help Functions used to allocate and
%% deallocate frequencies.
allocate({[], Allocated}, _Pid) ->
{{[], Allocated}, {error, no_frequency}};
allocate({[Freq|Free], Allocated}, Pid) ->
{{Free, [{Freq, Pid}|Allocated]}, {ok, Freq}}.
deallocate({Free, Allocated}, Freq) ->
NewAllocated=lists:keydelete(Freq, 1, Allocated),
{[Freq|Free], NewAllocated}.
inject({Free, Taken}, AddFreqs) -> %<****** ADDED CODE ****
{Free ++ AddFreqs, Taken}.
A minimal shell session:
1> c(f4).
{ok,f4}
2> f4:start().
true
3> f4:allocate().
{ok,10}
4> f4:allocate().
{ok,11}
5> f4:allocate().
{ok,12}
6> f4:allocate().
{error,no_frequency}
7> cd("./hotcode").
/Users/me/path/to/hotcode
ok
8> ls().
f4.erl
ok
9> c(f4).
{ok,f4}
10> f4 ! {request, self(), switch_code}.
{request,<0.32.0>,switch_code}
11> f4:inject([41,42]).
ok
12> f4:allocate().
{ok,41}
13>
Now, following the steps in the exercise:
1> c(f4).
{ok,f4}
2> f4:start().
true
3> f4:allocate().
{ok,10}
4> f4:allocate().
{ok,11}
5> f4:allocate().
{ok,12}
6> f4:allocate().
{error,no_frequency}
7> cd("./hotcode").
/Users/7stud/erlang_programs/fl_course2/2week/a5/hotcode
ok
8> ls().
f4.beam f4.erl
ok
9> c(f4).
{ok,f4}
10> code:load_file(f4).
{error,not_purged}
11>
=ERROR REPORT==== 30-Apr-2017::11:55:39 ===
Loading of /Users/7stud/erlang_programs/fl_course2/2week/a5/hotcode/f4.beam failed: not_purged
code:soft_purge(f4).
false
12> f4 ! {request, self(), switch_code}.
{request,<0.32.0>,switch_code}
13> code:load_file(f4).
=ERROR REPORT==== 30-Apr-2017::11:56:24 ===
Loading of /Users/7stud/erlang_programs/fl_course2/2week/a5/hotcode/f4.beam failed: not_purged
{error,not_purged}
14> code:soft_purge(f4).
true
15> f4:inject([41,42]).
ok
16> f4:allocate().
{ok,41}
17>
-
I found it surprising that
code:load_file(f4)
on line 8 caused an error. Everything I've read says that two versions of a module can exist at the same time, but the error seems to bely those statements. Edit:code:load_file(f4)
tries to load the module for the third time, and instead of the original version of the code being purged and all processes using that code being killed, as stated in the lecture,code:load_file(f4)
returns an error tuple. -
Initially,
soft_purge()
doesn't work because there is still code lingering in the oringinalloop()
. -
Sending a
switch_code
request to the originalloop()
facilitates the switch to the new version of the code because of the fully qualified call tof4:loop()
in the clause handling theswitch_code
request. -
Once again, in line 11 f4.beam won't load. Subsequently,
soft_purge()
worked because execution was not lingering in the orignal version of the code.