[erlang-questions] Learning OTP - Application, Supervisor, FSM, Server

Ulf Wiger (TN/EAB) <>
Mon Sep 1 12:37:22 CEST 2008


Jeff Crane skrev:

 > My chat_server.erl is a gen_server, and I think that if
 > someone can explain this last bit of the child
 > specification, I might understand why I'm getting
 > this at runtime (startup):
 > =INFO REPORT==== 1-Sep-2008::00:39:12 === application: tcp_server
 > exited: {shutdown,{tcp_server_app,start,[normal,[]]}} type: temporary
 >  {error,{shutdown,{tcp_server_app,start,[normal,[]]}}}
 >
 > Apologies for the inability to read specs, but I just don't
 > understand.>

That particular error message is one of the most annoying
messages produced by Erlang/OTP IMHO. It really doesn't
tell you anything except which application failed to start.

I believe it's due to an error caught in
supervisor:do_start_child(). The error reason isn't propagated,
but if you want to see it, you should make sure that the 'sasl'
application is started. That should give you an error report
from the supervisor, telling you what went wrong.

So instead of just typing application:start(tcp_server), try:

application:start(sasl).
application:start(tcp_server).

(or start erlang with erl -boot start_sasl)



 > % No idea what this bad boy is for.
 > [tcp_listener]

It's for in-service upgrade. It tells the release handler that
the process in question needs to be suspended before new
versions of the modules in the list are loaded into the system.
The release handler gets this info by querying the supervisors
during execution of an upgrade script. It has no bearing on
your current problem.

BR,
Ulf W


> This code is modified from:
> http://www.trapexit.org/Building_a_Non-blocking_TCP_server_using_OTP_principles
> 
> 
> -module(tcp_server_app).
> -author('').
> 
> -behaviour(application).
> 
> %% Internal API
> -export([start_client/0]).
> 
> %% Application and Supervisor callbacks
> -export([start/2, stop/1, init/1]).
> 
> -define(MAX_RESTART,	5).
> -define(MAX_TIME,	60).
> -define(DEF_PORT,	6123).
> 
> %% A startup function for spawning new client connection handling FSM.
> %% To be called by the TCP listener process.
> start_client() ->
> 	supervisor:start_child(tcp_client_sup, []).
> 
> %%----------------------------------------------------------------------
> %% Application behaviour callbacks
> %%----------------------------------------------------------------------
> start(_Type, _Args) ->
> 	ListenPort = get_app_env(listen_port, ?DEF_PORT),
> 	supervisor:start_link({local, ?MODULE}, ?MODULE, [ListenPort, prechat_fsm]).
> 	
> stop(_S) ->
> 	ok.
> 
> %%----------------------------------------------------------------------
> %% Supervisor behaviour callbacks
> %%----------------------------------------------------------------------
> init([Port, Module]) ->
> 	{ok,
> 		{
> 			_SupFlags = {one_for_one, ?MAX_RESTART, ?MAX_TIME},
> 			[
> 				% TCP Listener
> 				{
> 					tcp_server_sup,										% Id = internal id
> 					{tcp_listener,start_link,[Port,Module]},						% StartFun = {M, F, A}
> 					permanent,											% Restart = permanent | transient | temporary
> 					2000,												% Shutdown = brutal_kill | int() >= 0 | infinity
> 					worker,											% Type = worker | supervisor
> 					[tcp_listener]										% Modules  = [Module] | dynamic
> 				},
> 				% Client instance supervisor
> 				{
> 					tcp_client_sup,										% Id  = internal id
> 					{supervisor,start_link,[{local, tcp_client_sup}, ?MODULE, [Module]]},	% StartFun = {M, F, A}
> 					permanent,											% Restart = permanent | transient | temporary
> 					infinity,											% Shutdown = brutal_kill | int() >= 0 | infinity
> 					supervisor,											% Type = worker | supervisor
> 					[]												% Modules  = [Module] | dynamic
> 				},
> 				% Chat Server instance
> 				{
> 					chat_server_sup,										% Id  = internal id
> 					{chat_server,start_link, []},								% StartFun = {M, F, A}
> 					permanent,											% Restart = permanent | transient | temporary
> 					2000,												% Shutdown = brutal_kill | int() >= 0 | infinity
> 					worker,											% Type = worker | supervisor
> 					[chat_server]										% Modules  = [Module] | dynamic
> 				}
> 			]
> 		}
> 	};
> 
> init([Module]) ->
> 	{ok,
> 		{
> 			_SupFlags = {simple_one_for_one, ?MAX_RESTART, ?MAX_TIME},
> 			[
> 				% TCP Client
> 				{
> 					undefined,											% Id = internal id
> 					{Module,start_link,[]},									% StartFun = {M, F, A}
> 					temporary,											% Restart = permanent | transient | temporary
> 					2000,												% Shutdown = brutal_kill | int() >= 0 | infinity
> 					worker,											% Type = worker | supervisor
> 					[]												% Modules = [Module] | dynamic
> 				}
> 			]
> 		}
> 	}.
> 
> %%----------------------------------------------------------------------
> %% Internal functions
> %%----------------------------------------------------------------------
> get_app_env(Opt, Default) ->
> 	case application:get_env(application:get_application(), Opt) of
> 		{ok, Val} -> 
> 			Val;
> 		_ ->
> 			case init:get_argument(Opt) of
> 				[[Val | _]] -> 
> 					Val;
> 				error -> 
> 					Default
> 			end
> 	end.
> 
> 
> This doesn't run, but what's worse is that I don't understand why. Let me walk through what I think I know.
> 
> In shell, I type:
> application:start(tcp_server).
> 
> The OTP framework looks for and at the .app file for the argument specified.
> It sees tcp_server.app and finds the line:
> {mod, {tcp_server_app, []}},
> 
> This tells what args (happen to be [] in this case) to send the module tcp_server_app and the Type defaults to the atom "normal" somehow. start is always the default initial function according to the OTP framework.
> 
> Putting this altogether, we get the initial function call of:
> tcp_server_app:start(normal,[])
> 
> Next it pulls the ListenPort out of the define directives.
> 
> According to what I've observed, behaviours are equivalent to java interfaces. Unlike java interfaces, you don't need to declare that you implement the interface for another module to call them, another module will simply do a "blind" call, assuming it's there. tcp_server_app implements both application and supervisor behaviours, even though it only declares 1 of them (application). The supervisor behaviour allows the supervisor module to start up a supervisor node via an init/1 fun. The following line:
> supervisor:start_link({local, ?MODULE}, ?MODULE, [ListenPort, prechat_fsm]),
> 
> ...says, spawn module tcp_server_app, function init/1, register the process locally as tcp_server_app, passing the args [ListenPort, prechat_fsm] to the init/1. This results in a spawned process:
> tcp_server_app:init([ListenPort, prechat_fsm])
> 
> The first init([Port,Module]) matches so it is used.
> Next, I had no idea what was going on:
> {ok,{_SupFlags,[Arg1,Arg2]}};
> until I ran into:
> http://www.erlang.org/doc/design_principles/part_frame.html (look for child specifications)
> 
> % Child specification consists of...
> Arg = 
> {	
> % unique c.spec identifier
> tcp_server_sup,  
> 
> % spawn a process of tcp_listener:init([Port,Module]),
> {tcp_listener,start_link,[Port,Module]}, 
> 
> % Restart always
> permanent,
> 
> % How long to die after getting a "die" message from supervisor	
> 2000,	
> 
> % Is this child a sub-supervisor, otherwise: worker
> worker,						
> 
> % No idea what this bad boy is for.
> [tcp_listener]
> 
> }
> 
> My chat_server.erl is a gen_server, and I think that if someone can
explain this last bit of the child specification, I might understand why
I'm getting this at runtime (startup):
> =INFO REPORT==== 1-Sep-2008::00:39:12 === application: tcp_server 
> exited: {shutdown,{tcp_server_app,start,[normal,[]]}} type: temporary
>  {error,{shutdown,{tcp_server_app,start,[normal,[]]}}}
> 
> Apologies for the inability to read specs, but I just don't
> understand. >
> 
>       
> _______________________________________________
> erlang-questions mailing list
> 
> http://www.erlang.org/mailman/listinfo/erlang-questions



More information about the erlang-questions mailing list