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

Serge Aleynikov saleyn@REDACTED
Sat Feb 16 16:53:49 CET 2008


In addition to Sean's good example, I'd like to say that while blocking 
calls such as gen_tcp:accept/2 generally don't have anything to do with 
performance issues, in a large distributed system they may introduce 
undesired properties leading to a loss of predictive behavior.  Say, if 
you need to synchronize a restart of several nodes, upgrade mnesia 
schema and transition process state to a new version of code in a hot 
system then blocking calls surely complicate these tasks by quite a bit 
as they may cause timeouts in other processes.

Using blocking calls in stand-alone components such as ejabberds and 
YAWS may not be an issue at all up until you start integrating them in 
larger systems with a central point of control.

Serge

Sean Hinde wrote:
> Telecom systems typically have to allow re-configuration on the fly of 
> everything (e.g. by sending a reconf message to a process). They might 
> also perform hearbeat monitoring of key processes. Both of these things 
> are much easier if a process is not sitting in a long term blocking call.
> 
> Sean
> 
> On 16 Feb 2008, at 03:13, Samuel Tesla wrote:
> 
>> When is it not an acceptable design?
>>
>> I can't imagine that it's performance.  Both of the large examples of 
>> TCP services that I've seen in Erlang (YAWS and ejabberd) use this 
>> model, and they're both documented to handle very heavy loads and 
>> perform very well.
>>
>> -- Samuel
>>
>> On Fri, Feb 15, 2008 at 8:25 PM, Serge Aleynikov <saleyn@REDACTED> 
>> wrote:
>> 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
>>
>>
>> _______________________________________________
>> erlang-questions mailing list
>> erlang-questions@REDACTED
>> http://www.erlang.org/mailman/listinfo/erlang-questions
> 
> 



More information about the erlang-questions mailing list