gen_server的enter_loop分析

在看ranch user guide的过程中,发现实现protocol handler需要使用特殊的gen_server形式,也就是enter_loop函数调用,事例代码如下:

-module(echo_protocol).
-behaviour(ranch_protocol).
 
-export([start_link/4]).
-export([init/4]).
 
start_link(ListenerPid, Socket, Transport, Opts) ->
    Pid = spawn_link(?MODULE, init, [ListenerPid, Socket, Transport, Opts]),
    {ok, Pid}.
 
init(ListenerPid, Socket, Transport, _Opts = []) ->
    ok = ranch:accept_ack(ListenerPid),
    loop(Socket, Transport).
 
loop(Socket, Transport) ->
    case Transport:recv(Socket, 0, 5000) of
        {ok, Data} ->
            Transport:send(Socket, Data),
            loop(Socket, Transport);
        _ ->
            ok = Transport:close(Socket)
    end.
实现ranch的protocol handler只需要实现start_link函数即可,函数中需要启动一个新的进程,新的进程需要调用accept_ack函数来绑定socket的owner进程。

如果回调要实现gen_server行为模式的话,Listener进程调用模块的start_link方法,内部同步的启动gen_server进程,并且等待gen_server进程调用init函数返回,如果这个时候在init的方法中调用accept_ack方法,就会造成循环调用,造成死锁。

ranch提供的方法如下,使用gen_server的enter_loop方法。

-module(my_protocol).
-behaviour(gen_server).
-behaviour(ranch_protocol).
 
-export([start_link/4]).
-export([init/1]).
%% Exports of other gen_server callbacks here.
 
start_link(ListenerPid, Socket, Transport, Opts) ->
    proc_lib:start_link(?MODULE, [[ListenerPid, Socket, Transport, Opts]]).

init(ListenerPid, Socket, Transport, _Opts = []) ->
    ok = proc_lib:init_ack({ok, self()}),
    %% Perform any required state initialization here.
    ok = ranch:accept_ack(ListenerPid),
    ok = Transport:setopts(Socket, [{active, once}]),
    gen_server:enter_loop(?MODULE, [], {state, Socket, Transport}).

%% Other gen_server callbacks here.
通常情况下启动gen_server,需要调用gen_server:start_link(),参数中设置回调模块,这样的话像上面的分析,是会有死锁的问题的。

规避这个问题就是使用enter_loop方法。这个方法可以让已经存在的独立进程成为gen_server进程,在进入普通的gen_server循环之前执行设定的逻辑。

Listener进程调用proc_lib:start_link方法,创建新进程A,A执行init方法,调用proc_lib:init_ack()方法,告诉listener进程A进程已经启动,此时Listener进程就可以返回了。然后A进程再执行accept_ack()方法,最后调用enter_loop方法,让自己进入gen_server的执行循环。




转载于:https://my.oschina.net/astute/blog/119250

File "D:\Anaconda3\Lib\site‑packages\jupyter_core\utils\__init__.py", line 165, in wrapped return loop.run_until_complete(inner) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\Anaconda3\Lib\asyncio\base_events.py", line 687, in run_until_complete return future.result() ^^^^^^^^^^^^^^^ File "D:\Anaconda3\Lib\site‑packages\jupyter_client\manager.py", line 96, in wrapper raise e File "D:\Anaconda3\Lib\site‑packages\jupyter_client\manager.py", line 87, in wrapper out = await method(self, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\Anaconda3\Lib\site‑packages\jupyter_client\manager.py", line 435, in _async_start_kernel kernel_cmd, kw = await self._async_pre_start_kernel(**kw) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\Anaconda3\Lib\site‑packages\jupyter_client\manager.py", line 400, in _async_pre_start_kernel kw = await self.provisioner.pre_launch(**kw) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "D:\Anaconda3\Lib\site‑packages\jupyter_client\provisioning\local_provisioner.py", line 193, in pre_launch km.write_connection_file(jupyter_session=jupyter_session) File "D:\Anaconda3\Lib\site‑packages\jupyter_client\connect.py", line 500, in write_connection_file self.connection_file, cfg = write_connection_file( ^^^^^^^^^^^^^^^^^^^^^^ File "D:\Anaconda3\Lib\site‑packages\jupyter_client\connect.py", line 156, in write_connection_file with secure_write(fname) as f: File "D:\Anaconda3\Lib\contextlib.py", line 137, in __enter__ return next(self.gen) ^^^^^^^^^^^^^^ File "D:\Anaconda3\Lib\site‑packages\jupyter_core\paths.py", line 990, in secure_write fd = os.open(fname, open_flag, 0o0600) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ PermissionError: [Errno 13] Permission denied: 'C:\\Users\\23786\\AppData\\Roaming\\jupyter\\runtime\\kernel‑d32add565210.json'
03-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值