Difficulty adopting gen_server architecture in erlang - 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.

Related

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.

What OTP behaviors should I use for such module?

I have simple erlang module and I want to rewrite it based on OTP principles. But I can not determine what opt template I should use.
Module's code:
-module(main).
-export([start/0, loop/0]).
start() ->
Mypid = spawn(main, loop, []),
register( main, Mypid).
loop() ->
receive
[Pid, getinfo] -> Pid! [self(), welcome],
io:fwrite( "Got ~p.~n", [Pid] ),
// spawn new process here
loop();
quit -> ok;
X ->
io:fwrite( "Got ~p.~n", [ X ] ),
// spawn new process here
loop()
end.
gen_server would be fine.
Couple things:
it is a bad practice to send message to yourself
messages are usually tuples not lists because they are not dynamic
despite your comment, you do not spawn the new process.
Call to loop/0 enters the same loop.
Gen_server init would hold your start/0 body. API calls sequence and proxy your calls via gen_server to handle_calls. To spawn new process on function call, add spawn function to the body of desired handle_call. Do not use handle_info to handle incoming messages -- instead of sending them call the gen_server API and 'translate' your call into gen_server:call or cast. e.g.
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init(_) ->
{ok, #state{}}
welcome(Arg) ->
gen_server:cast(?MODULE, {welcome, Arg}).
handle_cast({welcome, Arg},_,State) ->
io:format("gen_server PID: ~p~n", [self()]),
spawn(?MODULE, some_fun, [Arg]),
{noreply, State}
some_fun(Arg) ->
io:format("Incoming arguments ~p to me: ~p~n",[Arg, self()]).
I have never compiled above, but it should give you the idea.

'function not exported' in 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..

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