[erlang-questions] blocking vs non-blocking gen_tcp:accept()

Serge Aleynikov saleyn@REDACTED
Sat Feb 16 03:25:59 CET 2008


The objective of the article was to show how to build a TCP server using 
only non-blocking processes and relying on the network driver to deliver 
events of interest to the application layer.  Blocking processes for N 
seconds as in the code below is not always an acceptable design.

I also wish that the non-blocking prim_inet:accept/2 call was exposed to 
the gen_tcp module.  Perhaps this was an oversight rather than a 
conscious decision?

Serge

Samuel Tesla wrote:
> Robin: Sorry if you got this twice, I realized just after I hit send that I
> had only replied to you.
> 
> It only blocks the Erlang process, I believe.
> 
> I've been meaning to post some code examples about this.
> 
> What's always bugged me about that article is that it relies on prim_inet
> which is not guaranteed at all to remain the same across releases, whereas
> gen_tcp is (or at least guaranteed to be squawked about).  I've written
> several TCP servers for personal projects, and I have found that there's no
> need to use anything but gen_tcp:accept (with a timeout).
> 
> Here's an example of using gen_tcp:accept/2 in a gen_server that operates
> similarly to the one in that walkthrough.
> 
> %% BEGIN CODE %%
> -module(tcp_lister).
> 
> -behaviour(gen_server).
> 
> -export([start_link/3]).
> -export([init/1, handle_call/3, handle_cast/2, handle_info/2,
>          terminate/2, code_change/3]).
> 
> -define(TCP_ACCEPT_TIMEOUT, 10000).
> 
> -record(state, {
>           listen, % listening socket
>           sup     % supervisor for client proxies
>          }).
> 
> start_link(Port, Opts, Sup) when is_integer(Port),
>                                  is_list(Opts),
>                                  is_atom(Sup) ->
>     gen_server:start_link(?MODULE, [Port, Opts, Sup], []).
> 
> init([Port, Opts, Sup]) ->
>     {ok, Listen} = gen_tcp:listen(Port, Opts),
>     State = #state{listen=Listen, sup=Sup},
>     gen_server:cast(self(), accept),
>     ?MODULE:accept(),
>     {ok, State}.
> 
> handle_call(Request, From, State) ->
>     {stop, {unknown_call, {Request, From}}, State}.
> 
> handle_cast(accept, State) ->
>     #state{listen=Listen, sup=Sup} = State,
>     case gen_tcp:accept(Listen, ?TCP_ACCEPT_TIMEOUT) of
>         {ok, Socket} -> Sup:start_client(Socket);
>         {error, timeout} -> ok
>     end,
>     gen_server:cast(self(), accept),
>     {noreply, State};
> handle_cast(Msg, State) ->
>     {stop, {unknown_cast, Msg}, State}.
> 
> handle_info(Info, State) ->
>     {stop, {unknown_info, Info}, State}.
> 
> terminate(_Reason, _State) ->
>     ok.
> 
> code_change(_OldVsn, State, _Extra) ->
>     {ok, State}.
> 
> %% END CODE %%
> 
> Then Sup:start_link/1 looks like this:
> 
> %% BEGIN CODE %%
> start_client(Socket) ->
>     {ok, Pid} = supervisor:start_child(?MODULE, []),
>     gen_tcp:controlling_process(Socket, Pid),
>     ok = client_module:set_socket(Pid, Socket),
>     ok.
> %% END CODE %%
> 
> And client_module:set_socket just lets the newly spawned process know what
> it's socket is just like in the trapexit article.
> 
> There's several things this code doesn't handle. It could get other errors
> such as EMFILE or ENFILE from gen_tcp:accept/2. But in those cases the
> gen_server will blow up and get logged and the supervisor will do something
> appropriate. There is a brief moment during which sockets will sit idle in
> the buffer, but seriously, it's brief.
> 
> The key thing here is that you're using gen_tcp:accept/2, so you won't be
> blocking the process forever. That means that the process will eventually
> loop through and check it's mailbox again and can handle all of the fun
> supervision-tree stuff gen_server does behind the scenes.
> 
> Hope this helps!
> 
> -- Samuel
> 
> On Fri, Feb 15, 2008 at 2:16 PM, Robin <robi123@REDACTED> wrote:
> 
>> Does gen_tcp:accept() block the entire OS thread or just the calling
>> erlang process?
>>
>> "One of the shortcomings of the gen_tcp module is that it only exports
>> interface to a blocking accept call." ->
>>
>> http://www.trapexit.org/Building_a_Non-blocking_TCP_server_using_OTP_principles
>>
>> Does the performance gain of non-blocking accept justify the added
>> complexity (finite state machine etc)?
>>
>> thanks,
>>
>> Robin
>> _______________________________________________
>> erlang-questions mailing list
>> erlang-questions@REDACTED
>> http://www.erlang.org/mailman/listinfo/erlang-questions
>>
> 
> 
> ------------------------------------------------------------------------
> 
> _______________________________________________
> erlang-questions mailing list
> erlang-questions@REDACTED
> http://www.erlang.org/mailman/listinfo/erlang-questions




More information about the erlang-questions mailing list