I am running Erlang R16B03-1 (erts-5.10.4) at OS X 10.9.2. Erlang was installed by using brew.
And I am trying to run a gen_server module.
-module(logger).
-author("evangelosp").
-behaviour(gen_server).
%% API
-export([start/0, stop/0, log/2]).
%% gen_server callbacks
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
-define(SERVER, ?MODULE).
%%%===================================================================
%%% API
%%%===================================================================
start() -> gen_server:start_link({global, ?SERVER}, ?MODULE, [], []).
stop() -> gen_server:call(?MODULE, stop).
log(_Level, _MSG) -> gen_server:call(?MODULE, {add, {_Level, _MSG}}).
%%%===================================================================
%%% gen_server callbacks
%%%===================================================================
init([]) -> {ok, ets:new(?MODULE, [])}.
handle_call(_Request, _From, Table) -> {reply, {ok, ["Mplah!", _Request, _From, Table]}, Table}.
handle_cast(_Request, State) -> {noreply, State}.
handle_info(_Info, State) -> {noreply, State}.
terminate(_Reason, _State) -> ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
In the erlang shell I am running:
Eshell V5.10.4 (abort with ^G)
1> c(logger).
{ok,logger}
2> logger:start().
{ok,<0.40.0>}
3> logger:log(info, "Hello World").
** exception exit: {noproc,{gen_server,call,
[logger,{add,{info,"Hello World"}}]}}
in function gen_server:call/2 (gen_server.erl, line 180)
And I can't get rid of that exception. I haven't actually found any useful resource by looking up the exception message, but this which didn't help much.
Cheers.
In you code start() -> gen_server:start_link({global, ?SERVER}, ?MODULE, [], [])., you use {global, ?SERVER} which means that:
If ServerName={global,GlobalName} the gen_server is registered
globally as GlobalName using global:register_name/2.
So when you send message to the server, you should write log(_Level, _MSG) -> gen_server:call({global, ?MODULE}, {add, {_Level, _MSG}}).. Please see the erlang doc:
call(ServerRef, Request, Timeout) -> Reply
Types:
ServerRef = Name | {Name,Node} | {global,GlobalName} |
ServerRef can be:
Name, if the gen_server is locally registered,
{global,GlobalName}, if the gen_server is globally registered.
Your server is not registered, so it is accesible only by its pid. But the interface functions use the implicit macro ?MODULE (which is replaced by logger at compilation) to access it.
You need either to change your interface functions, or, to register the server in the start function:
start() ->
{ok,Pid} = gen_server:start_link({global, ?SERVER}, ?MODULE, [], []),
register(?MODULE,Pid).
[edit] Thanks Evalon, I made the correction in the answer :o)
Related
Start the server using:
erlc server.erl ; erl -eval 'server:start()'
In another terminal:
telnet localhost 3547
Which could establish the connection successfully, but in a few seconds, the connection is closed by the server due to reasons beyond me. Reading the doc for handle_call/3, {noreply, NewState} is allowed as well.
Could someone explain it? Feels super confusing to me.
SOURCE CODE
-module(server).
-mode(compile).
-behavior(gen_server).
-compile(export_all).
-export([ main/1
, start/0
, stop/0
, stop_and_wait/0
]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-define(DEFAULT_PORT, 3547).
-record(state, {port, lsock}).
start_link(Port) ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [Port], []).
start() ->
start_link(?DEFAULT_PORT).
stop() ->
gen_server:cast(?SERVER, stop).
stop_and_wait() ->
gen_server:call(?SERVER, stop, infinity).
init([Port]) ->
{ok, LSock} = gen_tcp:listen(Port, [{active, false}, {reuseaddr, true}]),
{ok, #state{port = Port, lsock = LSock}, 0}.
handle_call({do_stuff, Arg}, _From, State) ->
io:format("do_stuff is called with ~p~n", [Arg]),
% {reply, ok, State};
{noreply, State};
handle_call(stop, _From, State) ->
{stop, normal, ok, State}.
handle_cast(stop, State) ->
{stop, normal, State}.
handle_info(timeout, #state{lsock = LSock} = State) ->
Server = self(),
Listener = spawn(fun() -> listen(Server, LSock) end),
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_Oldvsn, State, _Extra) ->
{ok, State}.
listen(Server, LSock) ->
{ok, Socket} = gen_tcp:accept(LSock),
gen_server:call(?SERVER, {do_stuff, 1}),
listen(Server, LSock).
main(_) ->
io:format("~p~n", [ok]),
ok.
Returning {noreply, NewState} from a gen_server:handle_call/3 implementation is allowed, but it does not mean the gen_server does not have to reply to the call. Rather, in that case it's assumed that the gen_server will reply at some later point using the gen_server:reply/2 call.
The default timeout for gen_server:call/2,3 is 5 seconds. What's happening in your code is your listen/2 function is running in a process that accepts a socket, and is thus the owner of that socket, after which it calls gen_server:call(?SERVER, {do_stuff, 1}). Since your gen_server does not reply to that call, the gen_server:call times out after 5 seconds, killing the process and thus closing the socket.
I have a simple server (below). I can create the server with cell_tracer:start_link() and this works; I know this because when I repeat the same call, I get an already_started error. The problem is that after the server is created, using cell_tracker:get_cells() (or doing any gen_server:call(...)) on it causes the server to exit with an exception like this:
** exception exit: {noproc,{gen_server,call,[cell_tracker,get_cells]}}
in function gen_server:call/2 (gen_server.erl, line 180)
I can call the handle_call(get_cells...) directly, and I get the expected result.
I'm not sure what's happening here. It doesn't give me much information to work with, and I don't see a problem. How can I dig into this further?
-module(cell_tracker).
-behavior(gen_server).
-export([start_link/0, stop/0]).
-export([add_cell/1, del_cell/1, get_cells/0]).
-export([init/1,
handle_cast/2,
handle_call/3,
terminate/2,
handle_info/2,
code_change/3]).
%% operational api
start_link() ->
gen_server:start_link({global, ?MODULE}, ?MODULE, [], []).
stop() ->
gen_server:cast(?MODULE, stop).
%% traking api
add_cell(Cell) ->
gen_server:call(?MODULE, { add_cell, Cell }).
del_cell(Cell) ->
gen_server:call(?MODULE, { del_cell, Cell }).
get_cells() ->
gen_server:call(?MODULE, get_cells).
%% gen_server callbacks
init(Coords) ->
{ok, Coords}.
handle_cast(stop, Coords) ->
{stop, normal, Coords}.
handle_call({add_cell, Cell}, _From, Cells) ->
{reply, ok, [Cell|Cells]};
handle_call({del_cell, Cell}, _From, Cells) ->
{reply, ok, Cells -- Cell};
handle_call(get_cells, _From, Cells) ->
{reply, Cells, Cells}.
terminate(unnormal, _State) -> ok.
handle_info(_,_) -> ok.
code_change(_,State,_) -> {ok, State}.
register the server locally
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
or if you register {global, ?MODULE} cast, call to {global, ?MODULE}
see gen_server
I have got the same problem working with python ( 2.7 ) and couchdb ( 2.1 ).
The point was I did save document without id. Here is an example
import couchdb
server = couchdb.Server('http://admin:PASSWORD#127.0.0.1:5984/')
db = server.create('numbers')
idd, revv = db.save({"taras":"vaskiv"})
after this I got this exception
431 # Store cachable responses
ServerError: (500, (u'noproc', u'{gen_server,call,[couch_uuids,create]}'))
But when I did save document with id, everything seems to work just fine.
idd, revv = db.save({"taras":"vaskiv", "_id": "55"})
The supervisor is an OTP behavior.
init([]) ->
RoomSpec = {mod_zytm_room, {mod_zytm_room, start_link, []},
transient, brutal_kill, worker, [mod_zytm_room]},
{ok, {{simple_one_for_one, 10, 10000}, [RoomSpec]}}.
Above code will invoke child's terminate method.
But if I change the brutal_kill to an integer timeout (e.g. 6000), the terminate method was never invoked.
I see an explanation in the Erlang document:
The dynamically created child processes of a simple-one-for-one
supervisor are not explicitly killed, regardless of shutdown strategy,
but are expected to terminate when the supervisor does (that is, when
an exit signal from the parent process is received).
But I cannot fully understand. Is it said that exit(Pid, kill) can terminate a simple_one_for_one child spec while exit(Pid, shutdown) can't ?
===================================update====================================
mod_zytm_room_sup.erl
-module(mod_zytm_room_sup).
-behaviour(supervisor).
-export([start_link/0, init/1, open_room/1, close_room/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
RoomSpec = {mod_zytm_room, {mod_zytm_room, start_link, []},
transient, brutal_kill, worker, [mod_zytm_room]},
{ok, {{simple_one_for_one, 10, 10000}, [RoomSpec]}}.
open_room(RoomId) ->
supervisor:start_child(?MODULE, [RoomId]).
close_room(RoomPid) ->
supervisor:terminate_child(?MODULE, RoomPid).
mod_zytm_room.erl
-module(mod_zytm_room).
-behaviour(gen_server).
-export([start_link/1]).
-export([init/1, handle_cast/2, handle_info/2, handle_call/3, code_change/3, terminate/2]).
start_link(RoomId) ->
gen_server:start_link(?MODULE, [RoomId], []).
init([RoomId]) ->
{ok, []}.
terminate(_, _) ->
error_logger:info_msg("~p terminated:~p", [?MODULE, self()]),
ok.
...other methods ommited.
mod_zytm_sup.erl
-module(mod_zytm_sup).
-behaviour(gen_server).
-export([start_link/0]).
-export([init/1, handle_cast/2, handle_info/2, handle_call/3, code_change/3, terminate/2]).
start_link() ->
gen_server:start_link(?MODULE, [], []).
init([]) ->
{ok, []}.
%% invoked by an erlang:send_after event.
handle_info({'CLOSE_ROOM', RoomPid}, State) ->
mod_zytm_room_sup:close_room(RoomPid),
{noreply, State}.
...other methods ommited.
Both mod_zytm_sup and mod_zytm_room_sup are a part of a system supervision tree, mod_zytm_sup invoke mod_zytm_room_sup to create or close mod_zytm_room process.
Sorry I've got wrong result.
To make it clear:
brutal_kill strategy kill child process immediately.
The terminate method will be invoked if the simple_one_for_one's shutdown strategy is an integer timeout. The child must declare process_flag(trap_exit, true) in its init callback.
FYI, Manual on Erlang doc:
If the gen_server is part of a supervision tree and is ordered by its
supervisor to terminate, this function will be called with
Reason=shutdown if the following conditions apply:
the gen_server has been set to trap exit signals, and the shutdown
strategy as defined in the supervisor's child specification is an
integer timeout value, not brutal_kill.
The dynamically created child processes of a simple-one-for-one
supervisor are not explicitly killed, regardless of shutdown strategy,
but are expected to terminate when the supervisor does (that is, when
an exit signal from the parent process is received).
Note that this is no longer true. Since Erlang/OTP R15A, dynamic children are explicitly terminated as per the shutdown strategy.
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
I need to keep a gen_mod process running as it loops every minute and does some cleanup. However once every few days it will crash and I'll have to manually start it back up again.
I could use a basic example of implementing a supervisor into ejabberd_sup so it can keep going. I am struggling to understand the examples that use gen_server.
Thanks for the help.
Here's an example module combining ejabberd's gen_mod and OTP's gen_server. Explanation is inlined in the code.
-module(cleaner).
-behaviour(gen_server).
-behaviour(gen_mod).
%% gen_mod requires these exports
-export([start/2, stop/1]).
%% these are exports for gen_server
-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-define(INTERVAL, timer:minutes(1)).
-record(state, {}).
%% ejabberd calls this function when this module is loaded
%% basically it adds gen_server defined by this module to
%% ejabberd main supervisor
start(Host, Opts) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
ChildSpec = {Proc,
{?MODULE, start_link, [Host, Opts]},
permanent,
1000,
worker,
[?MODULE]},
supervisor:start_child(ejabberd_sup, ChildSpec).
%% this is called by ejabberd when module is unloaded, so it
%% does the opposite of start/2 :)
stop(Host) ->
Proc = gen_mod:get_module_proc(Host, ?MODULE),
supervisor:terminate_child(ejabberd_sup, Proc),
supervisor:delete_child(ejabberd_sup, Proc).
%% it will be called by supervisor when it is time to start
%% this gen_server under control of supervisor
start_link(_Host, _Opts) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
%% it is an initialization function for gen_server
%% it starts a timer, which sends 'tick' message periodically to itself
init(_) ->
timer:send_interval(?INTERVAL, self(), tick),
{ok, #state{}}.
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
%% this function is called whenever gen_server receives a 'tick' message
handle_info(tick, State) ->
State2 = do_cleanup(State),
{noreply, State2};
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% this function is called by handle_info/2 when tick message is received
%% so put all cleanup code here
do_cleanup(State) ->
%% do all cleanup work here
State.
This blog post gives a good explanation how gen_servers work. Of course make sure to re-read OTP design principles on gen_server and on supervisor.
Ejabberd's module developement is described here