it's continue of previos question
I have gen_server:
start(UserName) ->
case gen_server:start({global, UserName}, player, [], []) of
{ok, _} ->
io:format("Player: " ++ UserName ++ " started");
{error, Error} ->
Error
end
...
How correctly send message to this gen_server. For example: in another file i make:
gen_server:cast(test, message).
In my gen_server file i have:
handle_cast(message, State) ->
io:format("Message receiving \r\n"),
{noreply, State};
I start my gen_server with test name:
server:start(test).
test started
when i call gen_server:cast(test, message). it is nothing output in shell. How can i check handle_cast calling or not?
Thank you.
Instead of
gen_server:cast(test, message).
write
gen_server:cast({global, test}, message).
If you register name as {global, name} you must call it as {global, name}
If your handler is called it will print "Message receiving \r\n" in shell. You made that with io:format call.
Related
I am new to erlang, I having following code:
-module (test_srv).
-behaviour (gen_server).
-export ([start_link/0, init/1, handle_info/2]).
start_link() ->
gen_server:start_link(?MODULE, [], []).
init([]) ->
self() ! asdasd,
{ok, new_state}.
handle_info(Msg, State) ->
io:format("server got ~p , now state is ~p~n", [Msg, State]),
{noreply, State}.
I test in erl shell:
1> {_, P} = test_srv:start_link().
server got asdasd , now state is new_state
The problem is, When send a message to server when the server is not initialised and not readly, how dose gen_server handle the message? I have following guesses:
gen_server handle the message immediately, and send the message to handle_info callback, but will lose initialised state in init callback
gen_server store the message if not server initialised, and send message after server initialised.
I wanna to know how erlang or gen_server handle this problem? what is the principle of handle message?
I'm guessing by server is not initialised you mean the rest of the init function being executed. In that case your second guess is correct. It's guaranteed that the handle_info will be executed after init has returned. Since the gen_server is a single process, and you're already executing init, the messages sent to itself from init will only be processed by gen_server after init has finished executing.
gen_server documentation on Module:terminate callback says:
Even if the gen_server process is not part of a supervision tree, this
function is called if it receives an 'EXIT' message from its parent.
Reason is the same as in the 'EXIT' message.
Here is my handle_info and terminate function:
handle_info(UnknownMessage, State) ->
io:format("Got unknown message: ~p~n", [UnknownMessage]),
{noreply, State}.
terminate(Reason, State) ->
io:format("Terminating with reason: ~p~n", [Reason]).
I start this server using gen_server:start. I assume when I call erlang:exit(Pid, fuckoff), it should call terminate callback function. But it shows:
Got unknown message: {'EXIT',<0.33.0>,fuckoff}
Which means it is calling handle_info. But when I call gen_server:stop, everything works as mentioned in documentation. I'm calling my gen_server from shell. Would you please clarify this?
[UPDATE]
Here is source code of decode_msg function inside gen_server. If it receives any 'EXIT' message it should call terminate function:
decode_msg(Msg, Parent, Name, State, Mod, Time, Debug, Hib) ->
case Msg of
{system, From, Req} ->
sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,
[Name, State, Mod, Time], Hib);
{'EXIT', Parent, Reason} ->
terminate(Reason, Name, Msg, Mod, State, Debug);
_Msg when Debug =:= [] ->
handle_msg(Msg, Parent, Name, State, Mod);
_Msg ->
Debug1 = sys:handle_debug(Debug, fun print_event/3,
Name, {in, Msg}),
handle_msg(Msg, Parent, Name, State, Mod, Debug1)
end.
In my case it doesn't call terminate function.
[UPDATE]
When I start gen_server using gen_server:start_link(), sending an exit signal using erlang:exit(Pid, Reason) will result in calling terminate call back function which is an expected behaviour. It seems there is a difference in interpreting an exit signal whether a process is linked to its parent or not.
Short answer:
If you call the exit/2 function from inside the gen_server actor, it behaves as expected based on the documentation and the terminate/2 callback will be called.
Long answer:
When you send the exit message from the shell, the Parent value of exit tuple is set to the shell process id, on the other hand when you start the gen_server process form shell its Parent value is set to its own process id, not the shell process id, therefore when it gets the exit message it doesn't match the second clause of the receive block in decode_msg/8 function so the terminate/6 function is not called and finally the next clause is matched which is calling handle_msg/5 function.
Recommendation:
For getting the terminate/3 callback called even by sending an exit message to the gen_server process, you can trap the exit message in handle_info/2 and then return with stop tuple as follows:
init([]) ->
process_flag(trap_exit, true),
{ok, #state{}}.
handle_info({'EXIT', _From, Reason}, State) ->
io:format("Got exit message: ~p~n", []),
{stop, Reason, State}.
terminate(Reason, State) ->
io:format("Terminating with reason: ~p~n", [Reason]),
%% do cleanup ...
ok.
When you start your gen_server, its a simple process, so, erlang:exit/1 or erlang:exit/2 work as expected.
If Pid is not trapping exits, Pid itself exits with exit reason Reason.
If Pid is trapping exits, the exit signal is transformed into a message {'EXIT', From, Reason} and delivered to the message queue of Pid.
So, currently your code trap 'EXIT' signal because this one is send like any other message into the mailbox and match handle_info/2 wildcard pattern.
If you want more information about that, you can read gen_server source code and see how it works. You can also find your problem described in this code.
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.
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.
I have gen_server:
start(UserName) ->
case gen_server:start({global, UserName}, player, [], []) of
{ok, _} ->
io:format("Player: " ++ UserName ++ " started");
{error, Error} ->
Error
end
...
Now i want to write function to stop this gen server. I have:
stop(UserName) ->
gen_server:cast(UserName, stop).
handle_cast(stop, State) ->
{stop, normal, State};
handle_cast(_Msg, State) ->
{noreply, State}.
I run it:
start("shk").
Player: shk startedok
stop(shk).
ok
start("shk").
{already_started,<0.268.0>}
But:
stop(player).
ok
is work.
How can i run gen_server by name and stop by name?
Thank you.
First: You must always use the same name to address a process, "foo" and foo are different, so start by having a strict naming convention.
Second: When using globally registered processes, you also need to use {global, Name} for addressing processes.
In my opinion you should also convert the stop function to use gen_server:call, which will block and let you return a value from the gen_server. An example:
stop(Name) ->
gen_server:call({global, Name}, stop).
handle_call(stop, _From, State) ->
{stop, normal, shutdown_ok, State}
This would return shutdown_ok to the caller.
With this said, the global module is rather limited and alternatives like gproc provides much better distribution.
I don't have the docs infront of me, but my guess would be that you need to wrap the username in a global tuple within the gen_server cast.