読者です 読者をやめる 読者になる 読者になる

supervisor:start_childが動かない

タイトル通りの件でハマったのでメモ。

こちらで基本的なことを学びつつ簡単なechoサーバを書いてみた。
Introduction | Learn You Some Erlang for Great Good!

以下のコードを書いて実行してみるもstart_childが動かない。
supervisor

-module(tcp_server_sup).                                                                                         
-behaviour(supervisor).                                                                                          
-export([start_server/1]).                                                                                       
-export([init/1]).                                                                                               
                                                                                                                 
start_server(Port) ->                                                                                            
    supervisor:start_link(?MODULE, [Port]).
                                  
init([Port]) ->                  
    {ok, ListenSocket} = gen_tcp:listen(Port, [binary, {active, false}, {packet, line}]),                        
    spawn_link(fun() -> start_acceptors() end),                                                                  
    Spec = {tcp_server                                                                                           
            ,{tcp_server, start_link, [ListenSocket]}                                                            
            ,temporary                                                                                           
            ,3600                                                                                                
            ,worker                                                                                              
            ,[tcp_servers]},                                                                                     
    {ok, {{simple_one_for_one, 60, 3600}, [Spec]}}.                                                              
                                                                                                                 
start_acceptor() ->                                                                                              
    io:format("start_acceptor~n", []),
    supervisor:start_child(?MODULE, []),

start_acceptors() ->
    io:format("start_acceptors~n", []),
    [start_acceptor() || _ <- lists:seq(1,20)],
    ok.

worker

 -module(tcp_server).
 -export([start_link/1]).
 
 start_link(ListenSocket) ->
     io:format("tcp_server:start_link~n", []),
     spawn_link(fun() -> accept(ListenSocket) end),
     {ok, self()}.
 
 accept(ListenSocket) ->
     {ok, AcceptSocket} = gen_tcp:accept(ListenSocket),
     spawn_link(fun() -> accept(ListenSocket) end),
     handle(AcceptSocket).
 
 handle(AcceptSocket) ->
     inet:setopts(AcceptSocket, [{active, once}]),
     receive
         {tcp, AcceptSocket, <<"quit", _/binary>>} ->
             gen_tcp:close(AcceptSocket);
         {tcp, AcceptSocket, Msg} ->
             gen_tcp:send(AcceptSocket, Msg),
            handle(AcceptSocket)
     end.

各関数にio:formatで出力を入れたが、"tcp_server:start_link"が出力されない。特にエラーも出ない。
同期的に処理する関数にブロックするような処理を入れてしまったことが以前あったが今回は問題なし。
supervisor:start_childが同期的であるというようなことはreferenceを見ても書いていない
Erlang -- supervisor

手がかりがなくなってしまったのでしばらくググッていたらこんなページを発見
Erlang Supervisor fail to start_child with no errors simple_one_for_one - Stack Overflow
まさにこの状況だった。

supervisorのプロセスに名前を付けて呼ぶ必要があるということだった。supervisor:start_lilnk/2の第一引数はcallback関数を定義したモジュール名を表すだけで、名前を付けるにはsupervisor:start_link/3を呼ぶ必要がある。

修正としては以下のようにする必要があった

start_server(Port) ->  
    supervisor:start_link({local, ?MODULE}, ?MODULE, [Port]).