[erlang-questions] Writing gen_servers with TCP listeners
Eric Maland
eric@REDACTED
Mon May 12 02:15:50 CEST 2008
Thanks, this and the oserl code was good reading and extremely
helpful. Serge's tutorial is a bit terrifying given the use of
prim_inet, though.
With your help I did find the solution and it had nothing to do
(directly) with the relationship between the listening and accepting
processes.
Here is what my start() looked like (in module 'sissie'):
start() ->
sissie_db:start(),
sissie_supervisor:start_link(1042, fun session_manager/1),
This worked fine from the erl console but when run with:
erl -boot start_sasl -s sissie start
The socket closed immediately, and all the accept()ors failed.
I had made the assumption that since the supervisor starts my
gen_server and the gen_server creates the listener, as well as spawns
the accept()ors, that my gen_server's lifetime was what was important
to the listening socket's longevity.
It turns out my parent module, 'sissie' was the important process due
to its linkage to the supervisor. When starting the server from the
command line rather than the shell, 'sissie' would exit and that exit
would chain down to the supervisor, its gen_servers, etc.
Any of these changes 'fixes' the problem:
start() ->
sissie_db:start(),
sissie_supervisor:start_link(1042, fun session_manager/1),
receive after infinity -> true end. %%
start() ->
sissie_db:start(),
spawn(fun() ->
sissie_supervisor:start_link(1042, fun
session_manager/1),
receive after infinity -> true end
end).
start() ->
sissie_db:start(),
{ok, Pid} = sissie_supervisor:start_link(1042, fun
session_manager/1),
unlink(Pid).
The latter seems the most explicit, but I have to ask myself if
there's not a better/prescribed method of starting a system like this
other than what I'm doing. I went through Joe Armstrong's book a
little and found at least one example similar to the above, where the
'starter' is unlinked from the supervisor at startup, so perhaps this
is just normal and something that I must get used to.
Clearly I am new to Erlang and OTP. Thanks again for your help.
Cheers
Eric
On May 11, 2008, at 3:48 PM, Dave Peticolas wrote:
> Eric Maland wrote:
>> Hello,
>> I am having some trouble writing a gen_server that is a tcp
>> listener that works outside the shell.
>> My goal has been to write a gen_server that starts listening on a
>> socket and manages a connection pool of accept()-ors. This seems
>> like a pretty standard behaviour and is very easy to implement,
>> but every implementation I've produced runs perfectly in the shell
>> but not in an erl instance without a shell.
>> This sort of problem seems to be referenced in a previous thread (http://www.erlang.org/pipermail/erlang-questions/2008-January/032400.html
>> ), but I have found 0 examples of how to apply the information
>> there to a gen_server - clearly the server needs to live forever,
>> but how do you get a gen_server to hang around? Is it acceptable
>> to implement a handle_call() that just does a receive after
>> infinity -> foo end ??? Who would call that? I have tried that,
>> and it seemed like after 10 or so requests the server died
>> (presumably on some timeout from the supervisor insantiating it??).
>> I found a great example of a gen_server tcp listener at http://www.duomark.com/erlang/tutorials/proxy.html
>> but this also exhibits the same problem outside the shell - it
>> serves one request and then the socket is closed.
>> Does anyone have or know of a great tcp listener example that
>> works outside the shell (eg started with: 'erl -boot start_sasl -s
>> mymodule start'), or another interesting way to resolve this,
>> specifically when writing gen_server?
>> Here are some models I have gone through, all of which work in the
>> shell but not outside the shell:
>> gen_server:init listen()'s ->
>> spawn()'s a connection pool ->
>> each pool process accept()'s and spawns another pool process
>> In the shell: works fine
>> Outside the shell: 2nd and subsequent accept() fails
>> gen_server:init listen()'s ->
>> gen_server:init calls gen_server:cast(accept_loop) ->
>> accept_loop loops forever, spawning handlers on each request
>> (and gen_tcp:controlling_process the associated connections over
>> to the processes)
>> In the shell: works fine
>> Outside the shell: 2nd and subsequent accept() fails
>> These all work great in the shell but serve 1-2 requests (depending
>> on your platform) outside the shell before the socket closes.
>> Any advice on how to properly get the owner of the listening socket
>> to stick around in a gen_server would be appreciated.
>
> A gen_server will 'hang around' indefinitely by design, unless you
> tell
> it to stop or one of its callbacks fail. I don't know if this is the
> problem, but you need to make sure that the listening socket itself
> always has an owner too, not just the sockets created by the listening
> socket.
>
> There's an example, by Serge Aleynikov, of how to do a generic tcp
> server here:
>
> http://trapexit.org/Building_a_Non-blocking_TCP_server_using_OTP_principles
>
> I packaged it up for erlware (http://erlware.org) and you can
> install it
> with their 'faxien' tool, or just grab the code here:
>
> http://git.krondo.com/?p=erlware/gen_socket.git;a=summary
>
> dave
>
>> Thanks!
>> Eric
>> References:
>> 1. http://www.erlang.org/pipermail/erlang-questions/2008-January/032400.html
>> 2. http://www.duomark.com/erlang/tutorials/proxy.html
>> _______________________________________________
>> erlang-questions mailing list
>> erlang-questions@REDACTED
>> http://www.erlang.org/mailman/listinfo/erlang-questions
>
More information about the erlang-questions
mailing list