1
votes

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").
2

2 Answers

1
votes

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.

0
votes

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.