[erlang-questions] gen_server etc: blocking init, blocking cast
Chandru
chandrashekhar.mullaparthi@REDACTED
Sat Apr 16 07:25:19 CEST 2016
Hi Oliver,
On 14 April 2016 at 23:23, Oliver Korpilla <Oliver.Korpilla@REDACTED> wrote:
> Hello.
>
> I was trying to write a gen_server for handling a TCP connection. So,
> there would be an instance of gen_server for each open connection. (Each
> instance would have a copy of the socket returned by gen_tcp:listen to
> start accepting a connection from.)
>
> One thing I noticed is that gen_tcp does not seem to play very well with
> OTP's assumptions since accept is a blocking call, at least I understood it
> (and the principles) that way.
>
> So, on the net I saw examples dealing with accept in init/1. So, this will
> delay init/1 from finishing until there is either a timeout or there is a
> connection. I read somewhere else that it is a good practice to keep init
> from blocking...
>
Yes, that is correct, because until your callback_module:init returns, your
supervisor:init will not return which means your application startup hasn't
finished, which in turn means it will block any applications after yours
from starting.
>
> I saw an example (I think it was in "Learn You Some Erlang Good") that
> delayed the accept call by calling gen_server:cast and sending your own
> mailbox a trigger that was immediately followed by the blocking accept
> call. While this certainly was keeping init short, I wonder what was
> exactly gained here?
>
> Both init and handle_cast get executed in the new process started by
> gen_server:start_link. If either of them blocks, the process cannot react
> to anything else, right? So, how would a cast blocking the server be better
> than init? The process is still not responsive to events.
>
init is executing in the context of the spawned gen_server process. It
sends itself a message (which is non-blocking) and the init function
returns, which means the supervisor:init function finishes executing, and
your application startup is complete. Now your gen_server callback
handle_cast is immediately called which then calls the gen_tcp:accept
blocking call. It is okay for this to be blocked, because that is the main
purpose of this process - block until a connection is available. The
blocking nature is a problem only in two situations.
1. Code upgrade tends to be a problem. If you update the module invoking
the blocking gen_tcp:accept twice, with no intervening socket connections
being accepted, the server process will be killed, because your process
hasn't handled the code upgrade message.
2. The gen_server process handles any other application specific messages -
which as you've surmised will be blocked.
I typically use the following pattern for accepting TCP connections.
https://gist.github.com/cmullaparthi/18ba2219befbf7a3c44c28cab004456f
This frees up your process which owns the listen socket (tcp_server.erl) to
handle other messages as well. The process which is blocking
(tcp_socket.erl) is not doing anything useful until it gets a TCP
connection.
I hope this helps.
cheers,
Chandru
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/erlang-questions/attachments/20160416/7be1f0a2/attachment.htm>
More information about the erlang-questions
mailing list