'function not exported' in erlang - erlang

This is my code, when i run: gen_server_test:alloc, it gives me the error, please help me out
-module(gen_server_test).
-behaviour(gen_server).
-export([start_link/0]).
-export([alloc/0, free/1]).
-export([init/1, handle_call/3, handle_cast/2]).
start_link() ->
gen_server:start_link({local, gen_server_test}, gen_server_test, [], []).
alloc() ->
io:format("allocing"),
gen_server:call(?MODULE, {alloc}).
free(Ch) ->
gen_server:cast(gen_server_test, {free, Ch}).
init(_Args) ->
{ok, channels()}.
handle_call({alloc}, _From, LoopData) ->
io:format("handle alloc").
handle_cast({free, Ch}, LoopData) ->
io:format(Ch).
free() ->
io:format("free").
channels() ->
io:format("channels").
the error:
23> gen_server_test:alloc().
allocinghandle alloc
=ERROR REPORT==== 6-May-2011::18:05:48 ===
** Generic server gen_server_test terminating
** Last message in was {alloc}
** When Server state == ok
** Reason for termination ==
** {'function not exported',
[{gen_server_test,terminate,[{bad_return_value,ok},ok]},
{gen_server,terminate,6},
{proc_lib,init_p_do_apply,3}]}
** exception exit: undef
in function gen_server_test:terminate/2
called as gen_server_test:terminate({bad_return_value,ok},ok)
in call from gen_server:terminate/6
in call from proc_lib:init_p_do_apply/3

Here is the important part of the error report: {bad_return_value,ok}
Valid return values for handle_call/3 are:
{reply,Reply,NewState}
| {reply,Reply,NewState,Timeout}
| {reply,Reply,NewState,hibernate}
| {noreply,NewState}
| {noreply,NewState,Timeout}
| {noreply,NewState,hibernate}
| {stop,Reason,Reply,NewState}
| {stop,Reason,NewState}
Rewriting your handle_call into:
handle_call({alloc}, _From, LoopData) ->
Reply = io:format("handle alloc"),
{reply, Reply, LoopData}
would "solve" the problem now. But I recommend going through the documentation of gen_server in general cause you have some other strange stuff going on. Your state for instance is the result of ìo:format("channels"). This doesn't make any sense at all.
I guess you're learning about gen_servers and that's great and I promise the documentation will help you solve a lot of minor problems along your path towards knowledge.

your handle_call and handle_cast methods need to return a tuple of the type {reply, Response, NewState} or {noreply, NewState}. Take a look at the gen_server documentation for more information of the allowed return values..
The io:format(..) function returns only ok.. which is the reason why u r seeing this error..

Related

Erlang gen_server: How to catch Errors?

I'm new in Erlang development and I'm interested in processes relationship.
If we link two processes P1 and P2 with process_flag(trap_exit, true) and use construction like Pid ! msg and receive .. after .. end - it's possible to catch P1 errors like badarith by the second process P2.
But if we use gen_server process P1 , which is linked with P2, - P1 terminates after P2 fails.
So, How to catch exit() errors using gen_server?
Thanks in advance!
P.S. Testing code.
P1:
-module(test1).
-compile(export_all).
-behaviour(gen_server).
start() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init([]) -> Link = self(),
spawn(fun() ->
process_flag(trap_exit, true),
link(Link),
test2:start(Link)
end),
{ok, "lol"}.
handle_call(stop, From, State) ->
io:fwrite("Stop from ~p. State = ~p~n",[From, State]),
{stop, normal, "stopped", State};
handle_call(MSG, From, State) ->
io:fwrite("MSG ~p from ~p. State = ~p~n",[MSG, From, State]),
{reply, "okay", State}.
handle_info(Info, State) -> io:fwrite("Info message ~p. State = ~p~n",[Info, State]), {noreply, State}.
terminate(Reason, State) -> io:fwrite("Reason ~p. State ~p~n",[Reason, State]), ok.
P2:
-module(test2).
-compile(export_all).
start(Mod) ->
io:fwrite("test2: Im starting with Pid=~p~n",[self()]),
receiver(Mod).
receiver(Mod)->
receive
stop ->
Mod ! {goodbye},
io:fwrite("Pid: I exit~n"),
exit(badarith);
X ->
io:fwrite("Pid: I received ~p~n",[X])
end.
Result: test1 process fails after test2 exits with badarith.
38>
38> c(test1).
test1.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,test1}
39> c(test2).
test2.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,test2}
40> test1:start().
test2: Im starting with Pid=<0.200.0>
{ok,<0.199.0>}
41> <0.200.0> ! stop.
Pid: I exit
Info message {goodbye}. State = "lol"
stop
** exception exit: badarith
42> gen_server:call(test1, stop).
** exception exit: {noproc,{gen_server,call,[test1,stop]}}
in function gen_server:call/2 (gen_server.erl, line 215)
43>
Linking is bidirectional, but trapping exits isn't; you need to call process_flag(trap_exit, true) in P1 (your current code does it in P2), and then handle messages of the form {'EXIT', FromPid, Reason} in handle_info (put P2's pid into State to help).
In this case it makes sense for P2 to stop if P1 does, otherwise I'd suggest using a monitor instead of a link.
Side notes:
Use spawn_link instead of spawn + link.
It's better style to move spawn into test2:start.

Difficulty adopting gen_server architecture in erlang

I am relatively new to erlang and wrote following modules:
-module(gserver).
-export([start1/0]).
-define(SERVER, gserver).
start1() ->
serv_util:start(?SERVER,
{ gserver, game_loop,
[dict:new(), dict:new()]}).
serv_util:
-module(serv_util).
-export([start/2]).
start(ServerName, {Module, Function, Args}) ->
global:trans({ServerName, ServerName},
fun() ->
case global:whereis_name(ServerName) of
undefined ->
Pid = spawn(Module, Function, Args),
global:register_name(ServerName, Pid);
_ ->
ok
end
end).
Then I tried to bring it under gen_server architecture of erlang as below:
-module(s_child).
-behaviour(gen_server).
-export([start/0,stop/0]).
-export([init/1, handle_call/3,handle_cast/2, terminate/2,
handle_info/2,code_change/3]).
-import(gserver,[start1/0]).
-import(gclient,[login/1]).
start()->
gserver:start1(),
gen_server:start_link({global,?MODULE}, ?MODULE, [], []).
stop()->
gen_server:cast(?MODULE, {stop}).
log_in(Name)->
gen_server:call({global,?MODULE}, {login,Name,self()}).
init(_Args) ->
io:format("Hello started ~n"),
{ok,_Args}.
handle_call({login,Name},_From,State)->
State,
Reply=login(Name),
{reply, Reply,State}.
But when i call this in the following order
1)s_sup:start_link().
Hello,started
{ok,<0.344.0>}
2)s_child:log_in("Abhishek").
** exception exit: {{function_clause,
[{s_child,handle_call,
[{login,"Abhishek",<0.335.0>},
{<0.335.0>,#Ref<0.0.4.457>},
[]],
[{file,"s_child.erl"},{line,61}]},
{gen_server,try_handle_call,4,
[{file,"gen_server.erl"},{line,615}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,647}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,247}]}]},
{gen_server,call,
[{global,s_child},{login,"Abhishek",<0.335.0>}]}}
in function gen_server:call/2 (gen_server.erl, line 204)
And I am not able to understand that what exactly is wrong at line 61 in my code.In the complete code at line 61 Following code is present:
handle_call(stop, _From, State) ->
{stop, normal, ok, State};
i.e the first handle_call is present at line 61
Can anybody help me out here please.
In the log_in function, you pass {login,Name,self()} as the gen_server call, but the handle_call function only expects {login,Name}. Thus, since there is no matching clause, the call to handle_call fails.
You don't need to pass self(), as you use the reply feature to ensure that the response makes it back to the caller, so just modify log_in to pass {login,Name} to gen_server:call.
Legoscia's answer is right.
I recommend to see Understanding gen_server.
This article gives useful information for working with gen_server.

Error in gen_server also terminates the calling process?

My gen_server contains a method like this:
handle_call(error, From, State) ->
io:format("Inside the handle_call error~n"),
1/0.
It provides a start (not start_link) function:
start() ->
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
normal_call() ->
gen_server:call(?MODULE, {normal}).
error_call() ->
gen_server:call(?MODULE, error).
I call the start function from the shell:
c(some_module).
some_module:start().
I then call the error call that terminates the server process because of the divide-by-zero error, but it also terminates the shell (the calling process). I don't understand why? They are not linked but still the shell restarts with a new pid. Is that an expected behavior of gen_server, or am I doing something wrong?
Updated :
It is still not working,, to help i m posting complete code.
-module(some_test).
-behaviour(gen_server).
-compile(export_all).
%% api functions, can be directly used by the user
start() ->
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
normal_call() ->
gen_server:call(?MODULE, normal, infinity).
error_call() ->
gen_server:call(?MODULE, error, infinity).
%% service specific function shoule not be call directly
init(_) ->
io:format("Inside the init ( ~p )~n", [self()]),
io:format("Inside the init...~n"),
{ok, nil}.
handle_call(normal, _, _) ->
io:format("Inside the handle_call normal~n"),
{reply, ok, nil};
handle_call(error, _, nil) ->
io:format("Inside the handle_call error~n"),
1/0.
terminate(Reason, _) ->
io:format("Inside the terminate~p~n", [Reason]).
%% just to complete
handle_info(_, _) ->
{noreply, nil}.
handle_cast(_, _) ->
{noreply, nil}.
code_change(_, _, _) ->
{ok, nil}.
%% a single test function that prove that called process was removed immeditely, did not wait for 5 seconds
test_function() ->
io:format("Id is : ~p~n", [self()]),
?MODULE:start(),
?MODULE:normal_call(),
?MODULE:error_call(),
io:format("Id is : ~p~n", [self()]). %% never reached :(
and to start i used this :-
c(some_test).
some_test:test_function().
and got the output :-
20> some_test:test_function().
Id is : <0.84.0>
Inside the init ( <0.92.0> )
Inside the init...
Inside the handle_call normal
Inside the handle_call error
Inside the terminate{badarith,
[{some_test,handle_call,3,
[{file,"some_test.erl"},{line,29}]},
{gen_server,try_handle_call,4,
[{file,"gen_server.erl"},{line,629}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,661}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,240}]}]}
=ERROR REPORT==== 3-Dec-2015::18:36:22 ===
** Generic server some_test terminating
** Last message in was error
** When Server state == nil
** Reason for termination ==
** {badarith,[{some_test,handle_call,3,[{file,"some_test.erl"},{line,29
{gen_server,try_handle_call,4,
[{file,"gen_server.erl"},{line,629}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,6
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,240}]}]}
** exception exit: {{badarith,
[{some_test,handle_call,3,
[{file,"some_test.erl"},{line,29}]},
{gen_server,try_handle_call,4,
[{file,"gen_server.erl"},{line,629}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,661}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,240}]}]},
{gen_server,call,[some_test,error,infinity]}}
in function gen_server:call/3 (gen_server.erl, line 212)
in call from some_test:test_function/0 (some_test.erl, line 51)
21>
so as we can see that last line after the some_test:error_call() is never called ?? because calling process also terminated ??
gen_server:call/2,3 is a synchronous messaging function. That means it does not return until the gen_server sends a reply to the sender. In this case the gen_server is crashing, and so never sends a response. The result of this is that the calling function times out, and the result of that timeout is a crash.
You will notice that the callee crashes instantly, but the caller crashes 5 seconds later. That is because the default timeout is 5000 milliseconds (check the docs, linked above). Try setting it to infinity -- your calling process will just hang, blocking forever waiting for a response that is never coming.
[UPDATE: The call coming directly from the shell is crashing immediately because an exception is being raised in the middle of its execution. This is different from the timeout expiring -- both cases result in crashes, but an exception is instant.]
The way around this is to send asynchronous messages, using gen_server:cast/2. Try defining this:
handle_cast(error, State) ->
io:format("Inside the handle_cast error~n"),
1/0.
This will only cause the callee to crash; the caller will continue on the instant the message is away. Its like throwing a baseball and walking away instead of throwing a boomerang and waiting.
As you gain experience with Erlang you will tend to write code with as many casts as possible and only resort to calls when the situation really demands a synchronous message (some state value actually depends on the sequence of events). This makes a system much more loosely coupled in many ways, and makes your calling functions resistant to failures in the processes they are sending data to.
EDIT
The shell crashing is because the call it was making crashed during execution. This does not happen with an asynch message, though. I added another module and broadened yours to illustrate the point. Now there is a new module called some_tester that relays everything we send, so it crashes, not the shell. Here are the relevant bits:
-module(some_tester).
-compile(export_all).
start() ->
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
relay(Nature, Message) ->
gen_server:cast(?MODULE, {Nature, Message}).
init(State) ->
ok = z("Starting..."),
{ok, State}.
handle_call(Message, From, S) ->
ok = z("Received ~tp from ~tp", [Message, From]),
{reply, ok, S}.
handle_cast({cast, Message}, S) ->
ok = some_test:cast(Message),
{noreply, S};
handle_cast({call, Message}, S) ->
ok = z("Sending call with ~tp", [Message]),
Reply = some_test:normal_call(Message),
ok = z("Received ~tp as reply", [Reply]),
{noreply, S};
handle_cast({infinite, Message}, S) ->
ok = z("Sending infinite with ~tp", [Message]),
Reply = some_test:infinite_call(Message),
ok = z("Received ~tp as reply", [Reply]),
{noreply, S};
handle_cast(Message, S) ->
ok = z("Unexpected ~tp", [Message]),
{noreply, S}.
Here are the relevant bits of some_test:
start() ->
gen_server:start({local, ?MODULE}, ?MODULE, [], []).
normal_call(Message) ->
gen_server:call(?MODULE, Message).
infinite_call(Message) ->
gen_server:call(?MODULE, Message, infinity).
cast(Message) ->
gen_server:cast(?MODULE, Message).
% ...
handle_call(normal, _, S) ->
io:format("Inside the handle_call normal~n"),
{reply, ok, S};
handle_call(error, _, S) ->
io:format("Inside the handle_call error~n"),
{reply, 1/0, S};
handle_call(bad_reply, _, S) ->
io:format("Inside the handle_call error~n"),
foo;
handle_call(Message, From, S) ->
io:format("Received ~tp from ~tp~n", [Message, From]),
{reply, ok, S}.
handle_cast(error, S) ->
io:format("Bad arith: ~tp~n", [1/0]),
{noreply, S};
handle_cast(Message, S) ->
io:format("Received ~tp~n", [Message]),
{noreply, S}.
Here is a run playing around with it. Note the output of the shell's own call to self() and flush():
1> c(some_test).
some_test.erl:31: Warning: this expression will fail with a 'badarith' exception
some_test.erl:32: Warning: variable 'S' is unused
some_test.erl:40: Warning: this expression will fail with a 'badarith' exception
{ok,some_test}
2> c(some_tester).
{ok,some_tester}
3> {ok, Test} = some_test:start().
Inside the init ( <0.45.0> )
Inside the init...
{ok,<0.45.0>}
4> {ok, Tester} = some_tester:start().
<0.47.0> some_tester: Starting...
{ok,<0.47.0>}
5> monitor(process, Test).
#Ref<0.0.2.178>
6> monitor(process, Tester).
#Ref<0.0.2.183>
7> self().
<0.33.0>
8> some_tester:relay(call, foo).
<0.47.0> some_tester: Sending call with foo
ok
Received foo from {<0.47.0>,#Ref<0.0.2.196>}
<0.47.0> some_tester: Received ok as reply
9> some_tester:relay(cast, bar).
Received bar
ok
10> some_tester:relay(call, error).
<0.47.0> some_tester: Sending call with error
ok
Inside the handle_call error
Inside the terminate{badarith,
[{some_test,handle_call,3,
[{file,"some_test.erl"},{line,31}]},
{gen_server,try_handle_call,4,
[{file,"gen_server.erl"},{line,629}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,661}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,240}]}]}
Inside the terminate{{badarith,
[{some_test,handle_call,3,
[{file,"some_test.erl"},{line,31}]},
{gen_server,try_handle_call,4,
[{file,"gen_server.erl"},{line,629}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,661}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,240}]}]},
{gen_server,call,[some_test,error]}}
11>
=ERROR REPORT==== 3-Dec-2015::22:52:17 ===
** Generic server some_test terminating
** Last message in was error
** When Server state == nil
** Reason for termination ==
** {badarith,[{some_test,handle_call,3,[{file,"some_test.erl"},{line,31}]},
{gen_server,try_handle_call,4,
[{file,"gen_server.erl"},{line,629}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,661}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,240}]}]}
=ERROR REPORT==== 3-Dec-2015::22:52:17 ===
** Generic server some_tester terminating
** Last message in was {'$gen_cast',{call,error}}
** When Server state == []
** Reason for termination ==
** {{{badarith,[{some_test,handle_call,3,[{file,"some_test.erl"},{line,31}]},
{gen_server,try_handle_call,4,
[{file,"gen_server.erl"},{line,629}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,661}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,240}]}]},
{gen_server,call,[some_test,error]}},
[{gen_server,call,2,[{file,"gen_server.erl"},{line,204}]},
{some_tester,handle_cast,2,[{file,"some_tester.erl"},{line,24}]},
{gen_server,try_dispatch,4,[{file,"gen_server.erl"},{line,615}]},
{gen_server,handle_msg,5,[{file,"gen_server.erl"},{line,681}]},
{proc_lib,init_p_do_apply,3,[{file,"proc_lib.erl"},{line,240}]}]}
11> self().
<0.33.0>
12> flush().
Shell got {'DOWN',#Ref<0.0.2.178>,process,<0.45.0>,
{badarith,
[{some_test,handle_call,3,
[{file,"some_test.erl"},{line,31}]},
{gen_server,try_handle_call,4,
[{file,"gen_server.erl"},{line,629}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,661}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,240}]}]}}
Shell got {'DOWN',#Ref<0.0.2.183>,process,<0.47.0>,
{{badarith,
[{some_test,handle_call,3,
[{file,"some_test.erl"},{line,31}]},
{gen_server,try_handle_call,4,
[{file,"gen_server.erl"},{line,629}]},
{gen_server,handle_msg,5,
[{file,"gen_server.erl"},{line,661}]},
{proc_lib,init_p_do_apply,3,
[{file,"proc_lib.erl"},{line,240}]}]},
{gen_server,call,[some_test,error]}}}
ok
Read through that carefully.

Erlang basic general server debugger output interpretation

I'm having some trouble with an Erlang module. Here is the one that I wrote:
-module(basic_gen_server).
-export([start/1, call/2, cast/2]).
start(Module) ->
register(server, spawn(basic_gen_server,gen_server_loop,[Module, Module:init()])), server.
call(Pid,Request) ->
Pid ! {call, self(), Request},
receive
Reply -> Reply
end.
cast(Pid,Request) ->
Pid ! {cast, self(), Request},
receive
_ -> ok
end.
gen_server_loop(Module, CurrentState) ->
io:fwrite("gen_server_loop~n", []),
receive
{call, CallPid, Request} ->
{reply, Reply, NewState} = Module:handle_call(Request,self(),CurrentState),
CallPid ! Reply,
gen_server_loop(Module, NewState);
{cast, CastPid, Request} ->
{noReply, NewState} = Module:handle_cast(Request, CurrentState),
CastPid ! noReply,
gen_server_loop(Module, NewState)
end.
And here is the callback module that was defined:
% Written by Caleb Helbling
% Last updated Oct 10, 2014
-module(name_server).
-export([init/0, add/3, whereis/2, handle_cast/2,
handle_call/3, handle_swap_code/1]).
%% client routines
add(ServerPid, Person, Place) ->
basic_gen_server:cast(ServerPid, {add, Person, Place}).
whereis(ServerPid, Person) ->
basic_gen_server:call(ServerPid, {whereis, Person}).
%% callback routines
init() ->
maps:new().
handle_cast({add, Person, Place}, State) ->
NewState = maps:put(Person, Place, State),
{noreply, NewState}.
handle_call({whereis, Person}, _From, State) ->
Reply = case maps:find(Person, State) of
{ok, Place} -> Place;
error -> error
end,
NewState = State,
{reply, Reply, NewState}.
handle_swap_code(State) ->
{ok, State}.
Upon trying to initialize the server with the following command:
MyServer = basic_gen_server:start(name_server).
I get the following debug output:
=ERROR REPORT==== 29-Oct-2014::12:41:42 ===
Error in process <0.70.0> with exit value: {undef,[{basic_gen_server,gen_server_loop,[name_server,#{}],[]}]}
Conceptually, I understand the notion of making serial code into a basic server system, but I believe that I have a syntax error that I haven't been able to find using either syntax highlighting or Google. Thanks in advance for the help!
Function gen_server_loop is not exported. So you can not call basic_gen_server:gen_server_loop(Module, Module:init()), which is what is happening inside spawn(basic_gen_server,gen_server_loop,[Module, Module:init()]).
If you read your error message it tells you that function you are trying to call in undefined (trougn undef atom). Function being {basic_gen_server,gen_server_loop,[name_server,#{}],[]}, or where you have {Module, Function, ListOfArgs, ...}. You always should check that
there are no types module or function name
function arity match number of arguments in call (List in error message)
function is exported
All local calls (like loop(SomeArgs), without module specified) will not compile if function is not defined. And you can do local call dynamically (FuntionName(SomeArgs) again without module name).
EDIT after comment about need of local calls.
You actually could use lambda for this. There is spawn/1 funciton, which takes lambda (or fun if you like), so you can call spawn( fun local_functino/0).. Only issue with that is fact that your fun can not take any arguments, but there is a way around it, with use of closures.
spawn(fun () ->
gen_server_loop(Module, Module:init())
end).
And gen_serve_loop stays local call.

Can't get a gen_server to crash from a spawn_linked process crash

From what I've read in the docs, gen_servers don't trap exits. Moreover, my understanding is that if a process starts another process with spawn_link, and the child process crashes, the parent crashes too.
However, this is not what I'm seeing. I've got a gen_server that spawn_links a process. I set up a function in the child process like so:
test(JobIsHalfDone) ->
case JobIsHalfDone of
true -> exit(test);
false -> ok
end.
When this function sends the exit signal, I get a message:
** exception exit: test
Yet its parent gen_server keeps right on ticking. Why?
Lets compare experience, I have the following behaviour that says that it does die when a linked process dies.
1> {ok, Pid} = gen_server:start(server, [], []).
{ok,<0.33.0>}
2> gen_server:call(Pid, nice_to_see_you).
thanks
3> gen_server:call(Pid, nice_to_see_you).
thanks
4> gen_server:call(Pid, twap).
oh_noes
5> gen_server:call(Pid, nice_to_see_you).
** exception exit: {noproc,{gen_server,call,[<0.33.0>,nice_to_see_you]}}
in function gen_server:call/2
That is, I made it crash by spawn_link:ing a process that does not much but dying, bringing the server down with it.
-module(server).
-compile(export_all).
init(_) ->
{ok, []}.
handle_call(twap, _From, State) ->
spawn_link(fun suicidal/0),
{reply, oh_noes, State};
handle_call(_, _From, State) ->
{reply, thanks, State}.
suicidal() ->
exit(kaboom).
The fact that you see the "*** exception exit: test" seems to indicate that you used gen_server:start_link/3 from the shell, so that you gen server is linked to the shell process. That might introduce additional confusion, but nothing explaining why you would think the server didnt die.
I think the context of your spawn_linked process is the process that made the call, not the gen_server. Either spawn the loop in the init() callback or do a gen_server:call() and spawn the loop in a handle_call. Then the loop will be linked to the process that is running the gen_server.
I was wrong, which I guess leaves me a step closer to understanding why it's not killing my real server.
-module(crash).
-behaviour(gen_server).
-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-export([crash/0]).
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init([]) ->
{ok, []}.
crash() ->
gen_server:cast(?MODULE, crash).
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(crash, State) ->
spawn_link(fun() ->
crash_loop(0)
end),
{noreply, State};
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
crash_loop(Counter) ->
case Counter =:= 10 of
true ->
exit(crash_loop);
false ->
ok
end,
timer:sleep(100),
crash_loop(Counter + 1).
...and testing in shell:
11> c(crash).
{ok,crash}
12> crash:start_link().
{ok,<0.67.0>}
13> crash:crash().
ok
14> ** exception error: crash_loop
14> whereis(crash).
undefined

Resources