Erlang: Why can't I link two gen_servers? - erlang

I have two gen_server modules.
First serv.erl
-module(serv).
-behaviour(gen_server).
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
code_change/3,
terminate/2,
start_link/0
]).
start_link() ->
gen_server:start_link(?MODULE, [], []).
init([]) ->
process_flag(trap_exit, true),
spawn_link(user, start_link,[]),
{ok, []}.
handle_call(_E, _From, State) ->
{noreply, State}.
handle_cast(_Message, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
handle_info(Message, State) ->
{noreply, State}.
code_change(_OldVersion, State, _Extra) ->
{ok, State}.
And user.erl (which is completely the same except init/1):
init([]) ->
{ok, []}.
I thought that the servers would last forever. And if the first server dies another one gets {'EXIT', Pid, Reason} message.
But if you start the modules by serv:start_link() , the user module will exit immediately after the start with a message {'EXIT',Pid,normal} .
Why does user die?

spawn and spawn_link are the two basic Erlang functions for starting a new process. Both will create a process which then calls the function with arguments as specified in the arguments to spawn/spawn_link. When that function ends the process automatically terminates with the exit reason normal. The difference between the functions is that spawn_link also creates a link between the two processes.
The gen_server:start_link function does much more than just creating the process by initiating the behaviour and then running a behaviour top-loop which provides all the behaviour functionality. Amongst other things the callback function init is called to initialise the behaviour and then return {ok,State} to tell the behaviour that everything has been initialised and went well and here is the local state it is to pass into all the callbacks. The callback functions of a gen_server are not meant to be called directly but by the behaviour.
So when you explicitly spawn a process just running the init function it will terminate as soon as the init function ends. This is what happening here.
The {'EXIT',Pid,Reason} messages comes from the processes being linked and that the process is trapping exits. When a process dies an exit signal is sent from the dying process to all the processes to which it is linked. When this signal arrives at a process trapping exits then it is converted to normal message and put in that processes message queue. This is what you are seeing here. Note that all this is done automatically because of the link and trapping exits.
I hope that helps. Sorry for being a bit over-didactic here.

When you use the spawn link function, you are starting a new process which calls user:start_link. That process starts and links to a user gen_server process, and then exits, since the call to user:start_link returned. The user process is linked to that process, so it gets an exit signal. Since the user process isn't trapping exits, it also exits.
You should just run user:start_link in your serv:init function, as suggested in the comments.

Related

Can a function be called in a gen_server process by its Pid

If I have a group of gen servers called lock, can I call a function say
hello() ->
io:format("Hello, world!~n").
from the Pid of individual processes of that gen_server instead of generic lock:hello().
I tried Pid=<0.91.0> (so the Pid is return when i start a chld in my supervisor), and Pid:hello(). gives a bad argument is it impossible to do this??
Is it a better idea to send a message instead to call that function??
You can call gen_server:call(Pid, TuplePatternThatMatchesOnCallback)
-behaviour(gen_server).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
...
hello() ->
gen_server:call(Pid, hello).
handle_call(hello, _From, _State) ->
Reply = io:format("Hello, world!~n")
{reply, Reply, State}.
There is no Pid:Function API in Erlang.
In both cases calling gen server will serialize the call, providing you are using gen_server API. But going with function call you can choose synchronous reply.
If the hello is just placed in gen_server module (without gen_server:call) it will be executed in context of calling process, not gen_server one's.
When you call a function in a gen_server module, the function is not executed in the gen_server process. It is executed in the caller process.
If you want the gen_server process to do something, you should use the gen_server:call or the gen_server:cast functions:
For example, the gen_server:call/2 function will take a pid and a message that will be sent along with the call. The gen_server will then run it's handle_call function in the gen_server process.
Normally, you would have functions in the same module that defines the gen_server that will do the gen_server:call so that the caller will not have to care. That creates a clean API for others and hides the gen_server specific stuff.
It may be a little bit tricky to get all the pieces together but it's simple once you have. Check out this chapter of LYSE:
http://learnyousomeerlang.com/clients-and-servers
You can call the function hello from anywhere you want. If you call it 1000 times from 1000 processes, each process will be executing the function in parallel not interfering with each other. You just call it like that lock:hello(). from each one of those processes, so you call a specific function hello that is defined in a specific module lock and which takes zero arguments.
Probably there is something you didn't mention in your question?

erlang otp child workers

I'm trying to get an OTP supervisor to start child workers which will (eventually) connect to remote servers. I used Rebar to create a template test application and I'm trying to get the supervisor to fire off function 'hi' in module 'foo'. it compiles OK and runs:
Eshell V5.8.5 (abort with ^G)
1> test_app:start(1,1).
{ok,<0.34.0>}
but when I try to start the worker it goes pear shaped with this error:
2> test_sup:start_foo().
{error,{badarg,{foo,{foo,start_link,[]},
permanent,5000,worker,
[foo]}}}
The problem seems similar, but not the same, to this question: Erlang - Starting a child from the supervisor module
Any ideas?
test_app.erl
-module(test_app).
-behaviour(application).net
-export([start/2, stop/1]).
start(_StartType, _StartArgs) ->
test_sup:start_link().
stop(_State) ->
ok.
Test_sup.erl:
-module(test_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1, start_foo/0]).
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
{ok, { {one_for_one, 5, 10}, []} }.
start_foo()->
supervisor:check_childspecs(?CHILD(foo, worker)),
supervisor:start_child(?MODULE, ?CHILD(foo, permanent)).
foo.erl:
-module(foo).
-export([hi/0]).
hi()->
io:format("worker ~n").
You check the childspec using the macro call ?CHILD(foo, worker) while you try to start the child with the macro using the macro call ?CHILD(foo, permanent). The second argument of the CHILD macro is the process type which should be either worker or supervisor. So the first macro call is correct. The value permanent is a value for the restart type, which you have already set to permanent, so the second call is wrong and you get a badarg error.
Note: It is quite common that library functions generate badarg errors as well, not just from built-in functions. It is not always obvious why it is a badarg.
I think that Robert answer is incomplete, after replacing permanent by worker you still have an error returned by supervisor:check_childspecs(?CHILD(foo, worker)),, I don't know why.
[edit]
The problem of bard arg comes from ... badarg :o)
check_childspecs extepect a list of child_specs, the correct syntax is supervisor:check_childspecs([?CHILD(foo, worker)]), and then it works fine. the following code is updated.
[end of edit]
But you will get also an error because the supervisor will try to launch the function foo:start_link that does not exist in the foo module.
the following code print an error, but seems to work properly.
-module(foo).
-export([hi/0,start_link/0,loop/0]).
start_link() ->
{ok,spawn_link(?MODULE,loop,[])}.
hi()->
io:format("worker ~n").
loop() ->
receive
_ -> ok
end.
-module(test_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1, start_foo/0]).
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
{ok, { {one_for_one, 5, 10}, []} }.
start_foo()->
io:format("~p~n",[supervisor:check_childspecs([?CHILD(foo, worker)])]),
supervisor:start_child(?MODULE, ?CHILD(foo, worker)).
[edit]
answering to David comment
in my code the loop/0 does not loop at all, on the receive block, the process waits for any message, and as soon as it receives one, the process dies returning the value ok. So as long as the worker process does not receive any message, it keeps living, which is nice when you make some test with supervisors :o).
On the opposite, the hi/0 function simply prints 'worker' on the console and finishes. As the restart strategy of the supervisor is one_for_one, the max restart is 5 and the child process is permanent, the supervisor will try to start the hi process 5 times, printing five time 'worker' on the console, and then it will give up and terminate itself with an error message ** exception error: shutdown
Generally you should choose permanent for never ending processes (main server of an application for example). For process that normally die as soon as they have done their job, you should use temporary. I never used transient but I read that it should be used for process that must complete a task before dying.

How to determine if a worker in a supervision tree is starting for the very first time or has been restarted

I have a simple supervisor configuration:
-module(my_supervisor).
-behaviour(supervisor).
-export([start_link/0, init/1]).
init(_Args) ->
{ok, { {one_for_one, 5, 10},
[
{my_worker, {my_worker, start_link, []}, permanent, 5000, worker, [my_worker]}
]
}
}.
And even simple worker:
-module(my_worker).
-export([start_link/0]).
start_link() ->
%??? is this the first time the supervisor is starting me or have I crashed and been restarted???
So is it even possible to determine whether this is the first time the start_link function is called by the supervisor or the worker process has crashed sometime in the past and is now being restarted?
For determining whether this is the first time the start_link function is called by the supervisor.
You can use childId parameter and pass the childId from outside as follows, for details, please doc about erlang supervisor.
start_child(ChildId, Mod, Args) ->
{ok, _} = supervisor:start_child(?SERVER,
{ChildId, {Mod, start_link, Args},
transient, ?MAX_WAIT, worker, [Mod]}),
ok.
For determining worker has been restarted. Please read document of monitor and link. When crash happens,your process will receive messages. If you read the supervisor's source code, you will find the supervisor actually use link and monitor to solve the crash monitor task.
3.
init([]) ->
process_flag(trap_exit, true),
...
terminate(_Reason, _State) ->
% may be crash here by check reason above.
ok.
handle_info({'EXIT',Self,Reason},State#state{self=Self)->
error_logger:info_report([crash_now]),
{stop,Reason,State};
[1]: http://www.erlang.org/doc/man/supervisor.html

Added supervisor(s) for a gen_server, shutdown immediately?

EDIT: Below.
Why is my supervised gen_server shutting down so quickly?
I'll give these organizational names to make it more clear the chain of command that I want in my application: First I'm starting with the "assembly_line_worker" then later I'll add the "marketing_specialist" to my supervision tree...
ceo_supervisor.erl
-module(ceo_supervisor).
-behaviour(supervisor).
-export([start_link/1]).
-export([init/1]).
start_link(State) ->
supervisor:start_link({local,?MODULE}, ?MODULE, [State]).
init([Args]) ->
RestartStrategy = {one_for_one, 10, 60},
ChildSpec= {assembly_line_worker_supervisor,
{assembly_line_worker_supervisor, start_link, [Args]},
permanent, infinity, supervisor, [assembly_line_worker_supervisor]},
{ok, {RestartStrategy, [ChildSpec]}}.
assembly_line_worker_supervisor.erl
-module(assembly_line_worker_supervisor).
-behaviour(supervisor).
-export([start_link/1]).
-export([init/1]). %% Internal
start_link(State) ->
supervisor:start_link({local, ?MODULE}, ?MODULE, [State]).
init([Args]) ->
RestartStrategy = {one_for_one, 10, 60},
ChildSpec = {assembly_line_worker, {assembly_line_worker, start_link, [Args]}, permanent,
infinity, worker, [assembly_line_worker]},
{ok, {RestartStrategy, [ChildSpec]}}.
assembly_line_worker.erl
-module(assembly_line_worker).
...
init([State]) ->
process_flag(trap_exit, true),
{ok, State}.
start_link(State) ->
gen_server:start_link({global, ?MODULE}, ?MODULE, [State], []).
handle_cast(...,State} ->
io:format("We're getting this message.~n",[]),
{noreply, State};
...
What's happening is that the assembly line worker does a few bits of work, like receiving a couple of messages that are sent just after the ceo_supervisor:start_link(#innovative_ideas{}) command is called, then it shuts down. Have any idea why? I know that the gen_server is receiving a few messages because it io:format's them to the console.
Thanks!
EDIT: I'm hosting this on Windows via erlsrv.exe and I found that when I start up my program via a function like so:
start() ->
ceo_supervisor:start_link(#innovative_ideas{}),
assembly_line_worker:ask_for_more_pay(), %% Prints out "I want more $$$" as expected,
ok.
...this function exiting immediately causes my supervisors / gen_servers to shut down. I would expect this because all of this is linked via supervision to the original calling process, so when that exits so should the children.
So I guess a better question would be, how can I allow my supervisors to keep running after going through all of the start up configuration? Is there an option other than wrapping all of this in an application? (Which doesn't sound too bad...)
Thanks for the probing questions! I learned more about supervisors that way.
batman
To get more information about what is happening start sasl before you start your supervisor: application:start(sasl).
Another way to debug this would be to start the worker from your erlang shell send the sequence of message that crashed the server. Btw: are you sure that you need 2 levels of supervisors?
Some immediate comments:
In ceo_supervisor:init/1 your supervisor child spec should declare transient instead of permanent.
Run erl -boot start_sasl so you have the error log when something goes bad and you can get the crash report of a crasher.
If you run this in the shell and you make any mistake, then your tree will be forcibly killed. This is because you linked to the shell and the shell crashes upon errors. So you are dragging down your tree. Try something like:
Pid = spawn(fun() -> my_app:start() end).
so you have it split off. You can kill the app by sending an exit message to Pid.

Erlang. Correct way to stop process

Good day, i have following setup for my little service:
-module(mrtask_net).
-export([start/0, stop/0, listen/1]).
-define(SERVER, mrtask_net).
start() ->
Pid = spawn_link(fun() -> ?MODULE:listen(4488) end),
register(?SERVER, Pid),
Pid.
stop() ->
exit(?SERVER, ok).
....
And here is the repl excerpt:
(emacs#rover)83> mrtask_net:start().
<0.445.0>
(emacs#rover)84> mrtask_net:stop().
** exception error: bad argument
in function exit/2
called as exit(mrtask_net,ok)
in call from mrtask_net:stop/0
(emacs#rover)85>
As you see, stopping process produces error, process is stopping though.
What does this error mean and how to make thing clean ?
Not being an Erlang programmer and just from the documentation of exit (here), I'd say, that exit requires a process id as first argument whereas you are passing an atom (?SERVER) to it.
Try
exit(whereis(?SERVER), ok).
instead (whereis returns the process id associated with a name, see here)
You need to change the call to exit/2 as #MartinStettner has pointed out. The reason the process stops anyway is that you have started it with spawn_link. Your process is then linked to the shell process. When you called mrtask_net:stop() the error caused the shell process to crash which then caused your process to crash as they were linked. A new shell process is then automatically started so you can keep working with the shell. You generally do want to start your servers with spawn_link but it can cause confusion when your are testing them from the shell and they just "happen" to die.
I would suggest you to stick with OTP. It really gives you tons of advantages (I hardly can immagine the case where OTP doesn't benefit).
So, if you want to stop process in OTP you should do something like this for gen_server:
% process1.erl
% In case you get cast message {stopme, Message}
handle_cast({stopme, Message}, State) ->
% you will stop
{stop, normal, State}
handle_cast(Msg, State) ->
% do your stuff here with msg
{noreply, State}.
% process2.erl
% Here the code to stop process1
gen_server:cast(Pid, {stopme, "It's time to stop!"}),
More about it you can find here: http://www.erlang.org/doc/man/gen_server.html

Resources