Erlang,process_flag trap_exit 从 CLI 杀死我的 gen_server

发布于 2024-12-10 02:24:02 字数 1666 浏览 1 评论 0原文

我正在使用这个 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 模块函数直接从控制台启动它,则不会发生这种情况。
我这样启动它是因为真正的 gen_server 更加复杂,并且我正在通过 Makefile 调用独立测试它。
有什么想法吗?

I have this gen_server that I am working with:

-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}.

The problem is, as I show next, if I execute it from the cli like erl -pa ebin/ -s user_info_provider start_link it dies straight away, but then I can spawn it from the console and it works.

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>}

This doesn't happen if I don't set process_flag(trap_exit, true) or I don't launch it directly from the console with -s module function.
I launch it like that because the real gen_server is way more complex and I am testing it standalone from a Makefile call.
Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

自我难过 2024-12-17 02:24:02

解决方案是 W55tKQbuRu28Q4xv 建议的解决方案,或者不使用 start_link,仅使用 start。发生的情况如下:

-s 参数由 init 处理。粗略地说,init 将生成一个新进程,然后用它来初始化并运行所有-s 参数。之后,该生成的进程将退出。

由于 init 进程退出,并且您陷阱退出,因此您的进程会收到一条消息 {'EXIT', P, Reason} ,其中 P 是生成的 pid() -by-init 进程。此消息由进程的 gen_server 部分处理。通常这样的消息会被转发到您的 handle_info/2 回调(顺便说一句,它在您的代码中有错误的返回值,应该是 noreply)。但在这种情况下,它将不会被转发。原因是 gen_server 包含其进程的概念。它通过进程字典和 proc_lib 放置的值 '$ancestors' 来记录哪个进程生成了它。现在,如果退出消息从父级到达,则立即调用终止回调并终止进程。此类消息不会转发给您。这就是你所看到的。

解决方案,一个漂亮的解决方案,是创建一个小型应用程序,一个主管,并将您的流程置于该主管之下。然后从 -s 调用 application:start(your_app)。这样做的原因是应用程序控制器单独运行。更好的解决方案是构建一个将自动启动您的应用程序的版本。发布版是您的应用程序及其与 ERTS 运行时捆绑在一起的依赖项。这样的版本完全独立存在,可以复制到目标主机并运行。因此,目标系统上不需要 Erlang,因为该版本是独立的。

The solution is the one suggested by W55tKQbuRu28Q4xv or by not using start_link, only start. Here is what happens:

The -s parameter is handled by init. Roughly, init will spawn a new process which it then uses to initialize and run all -s parameters in. Afterwards this spawned process will exit.

Since the init-process exits, and you trap exits, your process receives a message {'EXIT', P, Reason} where P is the pid() of the spawned-by-init process. This message is handled by the gen_server portion of your process. Normally such a message would be forwarded to your handle_info/2 callback (which has the wrong return value in your code btw, should be noreply). But in this case, it will not be forwarded. The reason is that a gen_server contains a concept of its parent process. It notes which process spawned it, by means of the process dictionary and a value '$ancestors' put there by proc_lib. Now if the exit message arrives from the parent, then the terminate callback is immediately called and the process is terminated. Such a message will not be forwarded to you. This is what you see.

The solution, the pretty solution, is to create a small application, a supervisor, and put your process under that supervisor. Then call application:start(your_app) from -s. The reason this works is because the application controller runs separately. The even better solution is to build a release which will automatically start your application. A release is your application + its dependencies bundled together with an ERTS runtime. Such a release lives completely by its own and can be copied to the target host and run. No Erlang is thus needed on the target system as the release is self-containing.

离旧人 2024-12-17 02:24:02

使用 Supervisor 执行最小应用程序(rebar 可以生成此类应用程序的框架)并使用 -s 从 cli 运行它。

Do minimal application with supervisor (rebar can generate skeleton of such application) and run it from cli with -s.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文