Max, thanks for the complements on the book I am glad you liked it. If
you get a chance write us a review with your thoughts on the book over
on Amazon. Ok, so to the gen_web_server behaviour and race conditions.
What you describe will not actually be able to happen. The gen server
is single threaded, i.e its behaviour container is a single process.
It is not possible for a gen server to call handle info while it is
still in handle info. When data comes in off the socket it is sent to
the behaviour container via erlang messages in order. There is no
possibility to get the messages in an order other than the order that
they hit the machine in. Does that make sense to you or am I
misunderstanding your question?

Cheers,
Martin

-----------------------

Hello Erlware team and the "Erlang and OTP in Action" authors.  First
let me say that I have thoroughly enjoyed the book, which I read cover
to cover (twice).  Finally, us amateurs have a guide for OTP which is
concise and extremely clear.  I was particularly interested because,
as a hobby, I attempted to create an erlang implementation of
memcached, which you guys pretty much show from start to finish
(interface not withstanding).

I wanted to ask specifically about your web server design pattern.
After studying the code for a few days, I was curious if you would
agree that there's a potential risk for a race condition in the
message handling routines.  Specifically this snippet:

handle_info({http, _Sock, {http_request, _, _, _}=Request}, State) ->
    inet:setopts(State#state.socket, [{active,once}]),
    {noreply, State#state{request_line = Request}};
handle_info({http, _Sock, {http_header, _, Name, _, Value}}, State) ->
    inet:setopts(State#state.socket, [{active,once}]),
    {noreply, header(Name, Value, State)};

If I understand correctly, inet:setopts() with {active,once}, would
immediately allow the low level tcp driver to receive more data and
send another message to the web server process.  Is it possible that a
fast client could potentially send data before the server returns from
handle_info/2 with a new state such that gen_server will call a new
handle_info/2 with the old state?

I solved this by using the same timeout trick you guys showed for
delayed initialization.  I enabled the timeout in the message receiver
and the initializer functions with a conditional in the server state:

-record(state, {socket, clientdata = new, ready = false}).

init([Socket]) ->
    {ok, #state{socket = Socket}, 0}.

handle_info({tcp, Socket, Packet}, State) ->
    ClientData = handle_data(Socket, Packet, State#state.clientdata),
    {noreply, State#state{clientdata = ClientData}, 0};

handle_info(timeout, #state{socket = Socket, ready = true} = State) ->
    inet:setopts(Socket, [{active, once}]),
    {noreply, State};

handle_info(timeout, #state{ready = false} = State) ->
    {ok, Socket} = gen_tcp:accept(State#state.socket),
    tcp_sup:start_child(),
    {noreply, State#state{socket = Socket, ready = true}, 0}.


I'm wondering if I'm just overthinking the problem and this is just
overkill or is there really a potential to get out of order confusion
with this?

Thanks again for the great resource and your time reading this.

--mk23

-- 
You received this message because you are subscribed to the Google Groups 
"erlware-dev" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/erlware-dev?hl=en.

Reply via email to