Erlang, process_flag trap_exit убивает мой gen_server из CLI - PullRequest
1 голос
/ 12 октября 2011

У меня есть этот gen_server, с которым я работаю:

-module(user_info_provider).
-export([start_link/0, stop/0]).
-export([init/1, terminate/2, handle_info/2, handle_call/3, handle_cast/2,
 code_change/3]).
-export([request_user_info/2]).

-behaviour(gen_server).

start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

stop() ->
    gen_server:cast(?MODULE, stop).

request_user_info(From,UserId) ->
    gen_server:cast(?MODULE, {request_user_info, From, UserId}).
%% Callback Functions
init(_) ->
    process_flag(trap_exit, true),
    io:format("I am ~w ~n", [self()]),
    {ok, null}.
%% @doc terminate
terminate(Reason, _LoopData) ->
    io:format("Terminating by ~w~n",[Reason]),
    {ok, null}.
handle_cast({request_user_info,From,UserId}, LoopData) ->
    {noreply, LoopData};
handle_cast(stop, LoopData) ->
    {stop, normal, LoopData}.
handle_info(_Info, State) ->
    {ok, State}.
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.
handle_call(_,_From,LoopData) ->
    {ok,ok,LoopData}.

Проблема в том, как я покажу далее, если я выполню его из cli, как erl -pa ebin/ -s user_info_provider start_link, он сразу же умрет, но потом яможет вызвать его из консоли, и он работает.

erl -pa ebin -s user_info_provider start_link
Erlang R14B02 (erts-5.8.3) [source] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false]

I am <0.32.0> 
Terminating by normal
Eshell V5.8.3  (abort with ^G)
1> user_info_provider:start_link().
I am <0.35.0> 
{ok,<0.35.0>}

Этого не произойдет, если я не установлю process_flag(trap_exit, true) или не запусту его непосредственно из консоли с помощью -s module function.
Я запускаю его так, потому что настоящий gen_server намного сложнее, и я тестирую его отдельно от вызова Makefile.
Есть идеи?

Ответы [ 2 ]

5 голосов
/ 13 октября 2011

Это решение, предложенное W55tKQbuRu28Q4xv или без start_link, только start.Вот что происходит:

Параметр -s обрабатывается init.Грубо говоря, init будет spawn новым процессом, который он затем использует для инициализации и запуска всех параметров -s. После этого этот порожденный процесс завершится.

Поскольку процесс init завершается, и вы перехватываете его,ваш процесс получает сообщение {'EXIT', P, Reason}, где P - это pid () процесса, инициируемого init.Это сообщение обрабатывается частью gen_server вашего процесса.Обычно такое сообщение будет переадресовано на ваш handle_info/2 обратный вызов (который, к слову, имеет неправильное возвращаемое значение в вашем коде, должен быть неоправданным).Но в этом случае он будет не перенаправлен.Причина в том, что gen_server содержит концепцию своего родительского процесса.Он отмечает, какой процесс породил его, с помощью словаря процессов и значения '$ancestors', помещенного туда proc_lib.Теперь, если сообщение о выходе поступает от parent , тогда вызывается обратный вызов прерывания, и процесс завершается.Такое сообщение не будет отправлено вам.Это то, что вы видите.

Решение, красивое решение, состоит в том, чтобы создать небольшое приложение, супервизор, и поместить ваш процесс под этим супервизором.Затем позвоните application:start(your_app) из -s.Это работает потому, что контроллер приложения работает отдельно.Еще лучшим решением является создание релиза, который автоматически запустит ваше приложение.Релиз - это ваше приложение + его зависимости, связанные вместе со средой выполнения ERTS.Такой выпуск живет полностью сам по себе и может быть скопирован на целевой хост и запущен.Таким образом, в целевой системе не требуется Erlang, поскольку релиз самодостаточен.

1 голос
/ 12 октября 2011

Сделайте минимальное приложение с супервизором (арматура может создать скелет такого приложения) и запустите его из cli с -s.

...