Erlang gen_server implementation - erlang

In the code below, why is the module prime_server not loading?
-module(prime_server).
-behaviour(gen_server).
-export([new_prime/1, start_link/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
new_prime(N) ->
%% 20000 is a timeout (ms)
gen_server:call(?MODULE, {prime, N}, 20000).
init([]) ->
%% Note we must set trap_exit = true if we
%% want terminate/2 to be called when the application
%% is stopped
process_flag(trap_exit, true),
io:format("~p starting~n" ,[?MODULE]),
{ok, 0}.
handle_call({prime, K}, _From, N) ->
{reply, make_new_prime(K), N+1}.
handle_cast(_Msg, N) -> {noreply, N}.
handle_info(_Info, N) -> {noreply, N}.
terminate(_Reason, _N) ->
io:format("~p stopping~n" ,[?MODULE]),
ok.
code_change(_OldVsn, N, _Extra) -> {ok, N}.
make_new_prime(K) ->
if
K > 100 ->
alarm_handler:set_alarm(tooHot),
N = lib_primes:make_prime(K),
alarm_handler:clear_alarm(tooHot),
N;
true ->
lib_primes:make_prime(K)
end.
OUTPUT:
$ erl -boot start_sasl
1>c(prime_server).
{ok,prime_server}
2>c(my_alarm_handler).
my_alarm_handler.erl:2: Warning: undefined callback function code_change/3 (behaviour 'gen_event')
my_alarm_handler.erl:19: Warning: variable 'Reply' is unused
{ok,my_alarm_handler}
3> {ok, Pid} = prime_server:start_link().
prime_server starting
{ok, <0.73.0>}
4> prime_server:new_prime(6).
prime_server stopping
=ERROR REPORT==== 24-Mar-2014::10:59:39 ===
** Generic server prime_server terminating
** Last message in was {prime,6}
** When Server state == 0
** Reason for termination ==
** {'module could not be loaded',
[{lib_primes,make_prime,[6],[]},
{prime_server,handle_call,3,[{file,"prime_server.erl"},{line,21}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,585}]},
{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,239}]}]}
=CRASH REPORT==== 24-Mar-2014::10:59:39 ===
crasher:
initial call: prime_server:init/1
pid: <0.73.0>
registered_name: prime_server
exception exit: {undef,
[{lib_primes,make_prime,[6],[]},
{prime_server,handle_call,3,
[{file,"prime_server.erl"},{line,21}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,585}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,239}]}]}
in function gen_server:terminate/6 (gen_server.erl, line 744)
ancestors: [<0.71.0>]
messages: []
links: [<0.71.0>]
dictionary: []
trap_exit: true
status: running
heap_size: 610
stack_size: 27
reductions: 181
neighbours:
neighbour: [{pid,<0.71.0>},
{registered_name,[]},
{initial_call,{erlang,apply,2}},
{current_function,{gen,do_call,4}},
{ancestors,[]},
{messages,[]},
{links,[<0.27.0>,<0.73.0>]},
{dictionary,[]},
{trap_exit,false},
{status,waiting},
{heap_size,610},
{stack_size,36},
{reductions,1303}]
** exception exit: undef
in function lib_primes:make_prime/1
called as lib_primes:make_prime(6)
in call from prime_server:handle_call/3 (prime_server.erl, line 21)
in call from gen_server:handle_msg/5 (gen_server.erl, line 585)
in call from proc_lib:init_p_do_apply/3 (proc_lib.erl, line 239)
9> gen_server:call(prime_server, 6).
** exception exit: {noproc,{gen_server,call,[prime_server,6]}}
in function gen_server:call/2 (gen_server.erl, line 180)
10> gen_server:call(prime_server, new_prime, 6).
** exception exit: {noproc,{gen_server,call,[prime_server,new_prime,6]}}
in function gen_server:call/3 (gen_server.erl, line 188)
11> gen_server:call(prime_server, new_prime(5), []).
** exception error: undefined shell command new_prime/1
12> gen_server:call(prime_server, {prime, 4}, []).
** exception exit: {{function_clause,
[{gen,call,
[prime_server,'$gen_call',{prime,4},[]],
[{file,"gen.erl"},{line,147}]},
{gen_server,call,3,
[{file,"gen_server.erl"},{line,184}]},
{erl_eval,do_apply,6,
[{file,"erl_eval.erl"},{line,573}]},
{shell,exprs,7,[{file,"shell.erl"},{line,674}]},
{shell,eval_exprs,7,[{file,"shell.erl"},{line,629}]},
{shell,eval_loop,3,[{file,"shell.erl"},{line,614}]}]},
{gen_server,call,[prime_server,{prime,4},[]]}}

{'module could not be loaded',
[{lib_primes,make_prime,[6],[]},
{prime_server,handle_call,3,[{file,"prime_server.erl"},{line,21}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,585}]},
{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,239}]}]}
From the log, you use this module lib_primes, do you have this file lib_primes.erl?
I think you should compiule and load this module lib_primes first.

Related

Erlang: Fail to make a gen_server:call()

Given the following code:
-module(appender_server).
-behaviour(gen_server).
-export([start_link/1, stop/0]).
-export([append_synched/2]).
-export([init/1, handle_call/3, handle_cast/2]).
start_link(FilePath) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, FilePath, []).
init(FilePath) ->
{ok, TheFile} = file:open(FilePath, [append]), % File is created if it does not exist.
io_lib:print(TheFile).
append_synched(FilePath, Text) ->
gen_server:call(?MODULE, {append_synched, FilePath, Text}).
handle_call({append_synched, FilePath, Text}, _From, LoopData) ->
io:fwrite("Hello!~n", []),
io:fwrite("LoopData:~p~n", LoopData),
io:fwrite("~p~n", FilePath),
io:fwrite("~p~n", Text).
handle_cast(append_asynched, Str) ->
{ok, theFile} = file:write_file(theFile, Str).
stop() ->
gen_server:cast(?MODULE, stop).
I then use the erlang shell to call the following and the get the errors:
1> cd("c:/Users/TalTe/WebAppsProjects/fileappender/src/").
c:/Users/TalTe/WebAppsProjects/fileappender/src
ok
2> c(appender_server).
{ok,appender_server}
3> appender_server:start_link("c:/temp/file.txt").
{error,{bad_return_value,"<0.88.0>"}}
4> =CRASH REPORT==== 2-Dec-2020::23:30:19.687000 ===
crasher:
initial call: appender_server:init/1
pid: <0.87.0>
registered_name: appender_server
exception exit: {bad_return_value,"<0.88.0>"}
in function gen_server:init_it/6 (gen_server.erl, line 409)
ancestors: [<0.79.0>]
message_queue_len: 0
messages: []
links: [<0.79.0>]
dictionary: []
trap_exit: false
status: running
heap_size: 376
stack_size: 28
reductions: 257
neighbours:
neighbour:
pid: <0.79.0>
registered_name: []
initial_call: {erlang,apply,2}
current_function: {io,execute_request,2}
ancestors: []
message_queue_len: 0
links: [<0.78.0>,<0.87.0>]
trap_exit: false
status: waiting
heap_size: 1598
stack_size: 26
reductions: 5365
current_stacktrace: [{io,execute_request,2,[{file,"io.erl"},{line,571}]},
{shell,exprs,7,[{file,"shell.erl"},{line,693}]},
{shell,eval_exprs,7,[{file,"shell.erl"},{line,642}]},
{shell,eval_loop,3,[{file,"shell.erl"},{line,627}]}]
** exception error: {bad_return_value,"<0.88.0>"}
4> appender_server:append_synched("c:/temp:/file.txt", "Hello").
** exception exit: {noproc,{gen_server,call,
[appender_server,
{append_synched,"c:/temp:/file.txt","Hello"}]}}
in function gen_server:call/2 (gen_server.erl, line 238)
5>
Please help me understand what am I doing wrong here?
BTW, the file was created despite the error on step 3.
The init callback should return {ok, SomeState}. In your case the return value of init is the return value of io_lib:print()

How to run a supervisor with workers which are not gen_servers?

Hello i am trying to run a supervisor whose workers are not gen_server(s).
My supervisor is defined in the same module as the worker for brevity:
I keep getting this error and i have tried putting the MFA attribute in [ ] to no avail.I have also put the ChildSpec in [ ].
What am i missing ?
I do not want my supervisor to have any workers when starting it.
Error
> X=sup:start_link().
> ** exception error: no match of right hand side value {error,
> {bad_start_spec,[]}}
> in function sup:start_link/0 (c:/Erlang/ProcessPool/sup.erl, line 6)
> =CRASH REPORT==== 5-Apr-2020::22:20:32.918000 === crasher:
> initial call: supervisor:sup/1
> pid: <0.280.0>
> registered_name: []
> exception exit: {bad_start_spec,[]}
> in function gen_server:init_it/6 (gen_server.erl, line 358)
> ancestors: [<0.273.0>]
> message_queue_len: 0
> messages: []
> links: [<0.273.0>]
> dictionary: []
> trap_exit: true
> status: running
> heap_size: 376
> stack_size: 27
> reductions: 205 neighbours:
Module
-module(sup).
-behaviour(supervisor).
-compile([export_all]).
start_link() ->
{ok, Pid} = supervisor:start_link({local, ?MODULE}, ?MODULE, []),
Pid.
init(_Args) ->
RestartStrategy = {simple_one_for_one, 10, 60},
{ok, {RestartStrategy,[]}}.
add(Sup,Value)-> #adding child
ChildSpec = {
ch1,
{sup, start, [Value]},
permanent,
brutal_kill,
worker,
[ch1]
},
supervisor:start_child(Sup,ChildSpec).
start([Value])-> #child's start_link equivalent (non-genserver)
spawn_link(?MODULE,initworker,[self(),Value]).
initworker(From,Value)-> #child's init
receive
MSG->From ! {got_msg,Value,MSG}
end.
As you using simple_one_for_one, You should define ChildSpec in init and all children use the same ChildSpec .
If you need different, then use 'one_for_one' strategy instead.
For simple_one_for_one:
The second argument of supervisor:start_child/2 must be a list which will be combined with default arguments to child start function arguments defined in ChildSpec.
Here I quickly modified the code to make it work for you.
-module(sup).
-behaviour(supervisor).
-compile([export_all]).
start_link() ->
{ok, Pid} = supervisor:start_link({local, ?MODULE}, ?MODULE, []),
Pid.
init(_Args) ->
RestartStrategy = {simple_one_for_one, 10, 60},
ChildSpec = {
ch1,
{sup, start, []},
permanent,
brutal_kill,
worker,
[sup]
},
{ok, {RestartStrategy,[ChildSpec]}}.
add(Sup,Value)->
supervisor:start_child(Sup,[Value]).
start(Value)->
P = spawn(?MODULE, initworker,[]),
P!{self(),Value},
{ok,P}.
initworker()->
receive
{From, MSG} -> io:format(" Value is ~p~n", [MSG])
end.

erlang otp application is not restarted when it crashes

I've been reading the book Erlang and OTP In Action and trying the source code in chapter 4 which builds an OTP application.
There is a gen_server that has these call back methods (full source):
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([Port]) ->
{ok, LSock} = gen_tcp:listen(Port, [{active, true}]),
{ok, #state{port = Port, lsock = LSock}, 0}.
handle_call(get_count, _From, State) ->
{reply, {ok, State#state.request_count}, State}.
handle_cast(stop, State) ->
{stop, normal, State}.
handle_info({tcp, Socket, RawData}, State) ->
do_rpc(Socket, RawData),
RequestCount = State#state.request_count,
{noreply, State#state{request_count = RequestCount + 1}};
handle_info(timeout, #state{lsock = LSock} = State) ->
{ok, _Sock} = gen_tcp:accept(LSock),
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
The superviser init([]) method looks like this (full source):
init([]) ->
Server = {tr_server, {tr_server, start_link, []},
permanent, 2000, worker, [tr_server]},
Children = [Server],
RestartStrategy = {one_for_one, 0, 1},
{ok, {RestartStrategy, Children}}.
I've started the application with application:start(tcp_rpc) and telneted into the application. When I exit the telnet session, the following erlang error was thrown:
=ERROR REPORT==== 11-Mar-2015::08:12:58 ===
** Generic server tr_server terminating
** Last message in was {tcp_closed,#Port<0.733>}
** When Server state == {state,1055,#Port<0.725>,5}
** Reason for termination ==
** {function_clause,[{tr_server,handle_info,
[{tcp_closed,#Port<0.733>},
{state,1055,#Port<0.725>,5}],
[{file,"src/tr_server.erl"},{line,87}]},
{gen_server,try_dispatch,4,
[{file,"gen_server.erl"},{line,593}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,659}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,237}]}]}
=INFO REPORT==== 11-Mar-2015::08:12:58 ===
application: tcp_rpc
exited: shutdown
type: temporary
I can see that the application is crashing because it doesn't have a handler for tcp_closed, however I was expecting the application to be restarted by the supervisor, but it wasn't.
Should the supervisor have restarted the application?
You have to change restart strategy to something like
RestartStrategy = {one_for_one, 10, 1},
because 0, 1 means that app can't be restarted at all.

My supervisor crashes when I try to start it from eshell?

I'm very new to OTP, I'm trying to create simple example to understand supervisor behaviour:
Here is simple increment server
-module( inc_serv ).
-behaviour( gen_server ).
-export( [ start/0, inc/1, stop/0 ] ).
-export( [ init/1, handle_call/3, terminate/2 ] ).
start() ->
gen_server:start_link( { local, ?MODULE }, ?MODULE, no_args, [] ).
stop() ->
gen_server:call( ?MODULE, stop ).
inc( Num ) ->
gen_server:call( ?MODULE, { num, Num } ).
init( no_args ) ->
io:format( "~p~n", [ "Increment server started :)" ] ),
{ ok, no_state }.
handle_call( { num, Num }, _From, no_state ) ->
{ reply, Num + 1, no_state };
handle_call( stop, _From, no_state ) ->
{ stop, normal, ok, no_state }.
terminate( Reason, no_state ) ->
io:format( "~p~n", [ "Increment server stopped" ] ).
And I'd like to make it supervised by this module:
-module( supervisor_inc ).
-behaviour( supervisor ).
-export( [ start/0 ] ).
-export( [ init/1 ] ).
start() ->
supervisor:start_link( { local, ?MODULE }, ?MODULE, no_args ).
init( no_args ) ->
process_flag( trap_exit, true ),
Supervisor_Spec = { one_for_one, 1, 1 },
IncServ_Spec = {
inc_serv,
{ inc_serv, start, [] },
permanent, 2000, worker, [ inc_serv ] },
{ ok, { Supervisor_Spec, [ IncServ_Spec ] } }.
After that I've performed in erlang shell next steps:
1>
1> c(inc_serv).
{ok,inc_serv}
2>
2> c(supervisor_inc).
{ok,supervisor_inc}
3>
3> supervisor_inc:start().
"Increment server started :)"
{ok,<0.43.0>}
4>
4> inc_serv:inc( 7 ).
8
5> inc_serv:inc( 8 ).
9
After this I've tried next (as I expected I've got error):
6> inc_serv:inc( bad_arg ).
"Increment server stopped"
"Increment server started :)"
=ERROR REPORT==== 23-Aug-2012::19:32:06 ===
** Generic server inc_serv terminating
** Last message in was {num,bad_arg}
** When Server state == no_state
** Reason for termination ==
** {badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]}
=ERROR REPORT==== 23-Aug-2012::19:32:06 ===
** Generic server supervisor_inc terminating
** Last message in was {'EXIT',<0.31.0>,
{{{badarith,
[{inc_serv,handle_call,3,
[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}},
[{gen_server,call,2,
[{file,"gen_server.erl"},{line,180}]},
{erl_eval,do_apply,6,
[{file,"erl_eval.erl"},{line,576}]},
{shell,exprs,7,[{file,"shell.erl"},{line,668}]},
{shell,eval_exprs,7,
[{file,"shell.erl"},{line,623}]},
{shell,eval_loop,3,
[{file,"shell.erl"},{line,608}]}]}}
** When Server state == {state,
{local,supervisor_inc},
one_for_one,
[{child,<0.48.0>,inc_serv,
{inc_serv,start,[]},
permanent,2000,worker,
[inc_serv]}],
undefined,1,1,
[{1345,739526,107495}],
supervisor_inc,no_args}
** Reason for termination ==
** {{{badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}},
[{gen_server,call,2,[{file,"gen_server.erl"},{line,180}]},
{erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,576}]},
{shell,exprs,7,[{file,"shell.erl"},{line,668}]},
{shell,eval_exprs,7,[{file,"shell.erl"},{line,623}]},
{shell,eval_loop,3,[{file,"shell.erl"},{line,608}]}]}
** exception exit: {{badarith,[{inc_serv,handle_call,3,
[{file,"inc_serv.erl"},{line,22}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}}
in function gen_server:call/2 (gen_server.erl, line 180)
After this I've expected - my supervisor to restart inc_serv. But it didn't:
7> inc_serv:inc( 8 ).
** exception exit: {noproc,{gen_server,call,[inc_serv,{num,8}]}}
in function gen_server:call/2 (gen_server.erl, line 180)
Could you help me to understand what happened? And how should I rewrite my supervisor, to make it able to restart inc_serv
Thanks
This is actually a kind of race condition.
As you might know, the Erlang shell itself is a normal Erlang process. When you start your supervisor from the shell, the supervisor is linked to the shell (because you use supervisor:start_link/3).
When you call your gen_server process, that process crashes (and is correctly restarted by the supervisor, as you can see by the subsequent "Increment server started :)" output).
However, at the same time, your call to gen_server:call/2 will result in the same crash (a gen_server crashing during the call will emit the same crash through the gen_server:call/2 function). This then crashes the shell process, which is linked to your supervisor, which in turn crashes with the same reason (badarith).
Basically, your supervisor is backstabbed by your shell process, after it loyally restarted your gen_server. Like so:
+---------(6)exit----------+ +---------(5)restart---------+
| | | |
| v | v
Shell ---(1)start_link---> supervisor ---(2)start_link---> gen_server
| ^ ^ | ^ | ^
| | | | | | |
| | | +---------(7)exit---------+ | |
| | | | |
| +-------------------------+--------------(4)exit------------+ |
| |
+---------------------------(3)call--------------------------------+
You can avoid this by calling catch inc_serv:inc(bad_arg). in your shell:
90> inc_serv:inc(7).
8
91> catch inc_serv:inc(bad_arg).
"Increment server stopped"
=ERROR REPORT==== 23-Aug-2012::22:10:02 ===
** Generic server inc_serv terminating
** Last message in was {num,bad_arg}
** When Server state == no_state
** Reason for termination ==
** {badarith,[{inc_serv,handle_call,3,[{file,"inc_serv.erl"},{line,20}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]}
"Increment server started :)"
{'EXIT',{{badarith,[{inc_serv,handle_call,3,
[{file,"inc_serv.erl"},{line,20}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,588}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,227}]}]},
{gen_server,call,[inc_serv,{num,bad_arg}]}}}
92> inc_serv:inc(7).
8

simple_one_for_one child can not start

I write some code to test simple_one_for_one supervisor, but it can not work, the code is:
-module(test_simple_one_for_one).
-behaviour(supervisor).
%% API
-export([start_link/0, start_fun_test/0]).
%% Supervisor callbacks
-export([init/1]).
-define(SERVER, ?MODULE).
%%--------------------------------------------------------------------
start_link() ->
{ok, Pid} = supervisor:start_link({local, ?SERVER}, ?MODULE, []).
start_fun_test() ->
supervisor:start_child(test_simple_one_for_one, []).
init([]) ->
RestartStrategy = simple_one_for_one,
MaxRestarts = 1000,
MaxSecondsBetweenRestarts = 3600,
SupFlags = {RestartStrategy, MaxRestarts, MaxSecondsBetweenRestarts},
Restart = permanent,
Shutdown = 2000,
Type = worker,
AChild = {fun_test_sup, {fun_test, run, []},
Restart, Shutdown, Type, [fun_test]},
io:format("start supervisor------ ~n"),
{ok, {SupFlags, [AChild]}}.
When I run
test_simple_one_for_one:start_link().
and
test_simple_one_for_one:start_fun_test().
in erl shell, it gives me error:
test_simple_one_for_one:start_fun_test().
** exception exit: {noproc,{gen_server,call,
[test_simple_one_for_one,{start_child,[]},infinity]}}
in function gen_server:call/3 (gen_server.erl, line 188)
If it's all the code you've written for the test, beware that when you register a supervisor child you suplly a {M, F, A} tuple which represent the function called when you start a child.
In you're case, I think it cannot simply find the fun_test:run/1 function.

Resources