Joe's "deep trickery"

martin j logan <>
Fri Feb 28 17:17:36 CET 2003


On Fri, 2003-02-28 at 02:03, Chris Pressey wrote:
> Good day, eh?
> 
> OK, in Joe's latest tutorial
> (http://www.sics.se/~joe/tutorials/web_server/web_server.html) he states:
> 
> "We also need some deep trickery to arrange that one
> instance of this process is started for each incoming request"
> 
> And by the looks of tcp_server.erl, the trickery is quite complex.
> 
> But I've been using a very much simpler method for starting generic TCP/IP
> servers, roughly:
> 
>   server(Module, Function, Args, Port, Options) ->
>     case gen_tcp:listen(Port, Options) of
>       {ok, LSock} ->
>         spawn_link(?MODULE, server_loop,
>           [Module, Function, Args, LSock, Port, Options])
>     end.
> 
>   server_loop(Module, Function, Args, LSock, Port, Options) ->
>     case gen_tcp:accept(LSock) of
>       {ok, Socket} ->
>          Pid = spawn_link(Module, Function, [Socket] ++ Args),
>          gen_tcp:controlling_process(Socket, Pid),
>          server_loop(Module, Function, Args, LSock, Port, Options);
>       {error, closed} ->
>          server(Module, Function, Args, Port, Options);
>       N ->
>          server_loop(Module, Function, Args, LSock, Port, Options)
>     end.
> 
> I realize my version doesn't limit the number of connections like Joe's
> does, but that seems straightforward to implement.  Also, if you want
> {active, true}, you have to pass the option {active, false} initially,
> then use inet:setopts to set it to {active, true} while in
> Module:Function, or risk a race condition - a minor inconvenience.
> 
There is actually no _need_ to do that because the race condition is
already handled in inet.erl by the functions tcp_controlling_process/2
and tcp_sync_input/3. This is of course assuming that no one would
change this in a further release and break code that does not check for
the race condition existing in the gap between handing over control from
one process to another. I think that this should be stated in the docs. 

In short the code in tcp_controlling_process switches the port to active
false, if it was true to begin with. The function is coded to then
message the new process with all of the tcp messages in the original
controlling processes mailbox. The final step restore the connection to
the port. Basically, set active to true(or whatever it was on the port
originally, true, in this case) for the new controlling process and then
return. 

Cheers,
Martin


> This version seems to work ok for me, and even with those improvements, it
> could hardly be called deep trickery; so in deference to Joe's wisdom, and
> knowing how much of a fan he is of simplicity, I must assume either:
> 
> a) mine has a fatal flaw or flaws that I can't presently perceive, or
> b) Joe forgot the word "efficiently" before "arrange" in his sentence
> 
> My concern is mainly that an intimidating module like tcp_server.erl could
> scare off new Erlang programmers by giving them the impression that
> working with TCP/IP in Erlang is a bit nasty.  It's not nasty at all, at
> least I don't think it is unless you want to push the envelope.
> 
> This is otherwise a fine tutorial.  I like it.
> 
> Middlemen gets me thinking: are there generic consumer/producer patterns
> that can be packaged?  I find I'm writing a lot of processes along the
> lines of: accept data in arbitrary-sized chunks from one process and
> dispense it in chunks of some calculated size to another process,
> sometimes reformatting it on the way.  Is there something like a
> gen_stream that I've overlooked in OTP?
> 
> But then I start thinking: why the hell do I want more gen_* modules when
> I rarely ever use the existing ones?  For better or worse, I usually build
> my own with receive and !, which I find easier to read (at least while
> coding,) with the assumption that some day, if it becomes really
> important, I'll rewrite them to be gen_*'s.  So I sat myself down the
> other day and forced myself to write one each gen_server, gen_event and
> gen_fsm, to practice.
> 
> I learned more about them, but unfortunately I learned more about why I
> never end up using them.  I totally understand Suresh's confusion over
> gen_event:call.  It's unintuitive until you think about how Erlang grew up
> - in an ideal world, if you wanted an event handler you could treat like a
> server, you'd say -behaviour([gen_event, gen_server]).  Clearly, you can't
> do that, and gen_event:call smells like the workaround for it.
> 
> Also, it would be really nice if the event handler could actually *handle*
> events and not just react to them after they've happened - i.e. if
> gen_event:notify took, and returned, a term to represent state, perhaps
> modified by the event handlers.  (That way you wouldn't need
> gen_event:call either; you could configure an event handler using events.)
> 
> Anyway, sorry that got off on sort of a tangent.
> 
> -Chris





More information about the erlang-questions mailing list