[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

inheriting socket in child process on Windows

knutaf

6/10/2009 7:43:00 AM

Hello, list

In my Ruby application, I accept TCP connections from several clients.
When
any client sends a particular command, I want to spawn a child process
in
place of the parent process that will continue to service the already-
open
TCP connections without the clients needing to reconnect.

I originally wrote this code on Linux, and it works in the most
obvious way:

When the command is received, make note of all of the client's file
descriptors (IO#fileno). Spawn the child process and communicate the
list of
file descriptors to it. In the child process, call TCPSocket.for_fd
for each
file descriptor, and it is ready to read and write data.

I suppose it's not too surprising that this doesn't "just work" on
Windows,
mainly because Ruby does a lot of special handling around the fact
that on
Windows, you can have both file descriptors and handles, and can only
pass
one or the other into various functions. I've done enough debugging to
know
specifically why the above method doesn't work on Windows, but I don't
think
it's necessary to explain it unless someone is curious.

After a good amount of debugging and examining the Ruby 1.9 source, I
have
this working to the point where my child process can write to the
client
sockets, but cannot read from them. Here's what I did, and what
doesn't
work.

In the parent process, I now wrap _get_osfhandle and _open_osfhandle.
Instead of just calling IO#fileno, I call _get_osfhandle(IO#fileno).
This
fetches the real SOCKET (i.e. HANDLE) value. I pass these to the
child
process.

In the child process, I now call TCPSocket.for_fd(_open_osfhandle
(handle)).
The other flags to _open_osfhandle don't actually matter. This step is
only
important to set up a mapping between the fd and the HANDLE value in
the
child process, so that later when I call TCPSocket#send or recv,
which
internally calls _get_osfhandle on its fd, it will get the right
SOCKET
value to pass into winsock functions.

By doing this, as I mentioned above, TCPSocket#send works, but recv
doesn't.
As far as I can tell, this is entirely due to some extra checks that
Ruby
does before actually calling ws2_32!select.

In rb_w32_select:

rb_fd_init(&else_rd);
nonsock += extract_fd(&else_rd, rd, is_not_socket);

This removes entries from the "rd" fd set that is passed in that match
the
criterion "is not a socket." I assumed that since I had passed in a
socket,
my fd would remain on this list. However...

From is_socket:

if (st_lookup(socklist, (st_data_t)sock, NULL)) // then true - it is
a
socket

Only sockets produced by winsock function wrappers in win32.c here
are
admitted to the special socklist. Outsiders aren't allowed in, even if
they
actually are sockets. I also verified using a bit of debugger hacking
that
if I invert the return value of is_not_socket for the fd I care about,
then
ws2_32!select does get called, which in turn does lead to recv
getting
called successfully.

Is there something special I can call to get my socket added to this
list? I
didn't see anything with a cursory glance of win32.c, which appears to
be
the only place socklist is used. Is there an entirely different
approach I
should be taking?

Thanks
-hargo
5 Answers

Robert Klemme

6/10/2009 12:07:00 PM

0

2009/6/10 <knutaf@gmail.com>:
> In my Ruby application, I accept TCP connections from several clients.
> When
> any client sends a particular command, I want to spawn a child process
> in
> place of the parent process that will continue to service the already-
> open
> TCP connections without the clients needing to reconnect.
>
> I originally wrote this code on Linux, and it works in the most
> obvious way:
>
> When the command is received, make note of all of the client's file
> descriptors (IO#fileno). Spawn the child process and communicate the
> list of
> file descriptors to it. In the child process, call TCPSocket.for_fd
> for each
> file descriptor, and it is ready to read and write data.

I would find this more obvious:

server = TCPServer.new port

while ( client = server.accept )
printf "Got client %p\n", client.peeraddr

fork do
client.each do |line|
client.printf "ECHO: %p\n", line
client.flush
end

client.close_write
end

client.close
end

> Is there an entirely different
> approach I should be taking?

I do not know whether you _should_ take this approach but using cygwin
might ease your pain considerably. The approach noted above does work
on cygwin.

Kind regards

robert

--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestprac...

knutaf

6/10/2009 2:30:00 PM

0

On Jun 10, 5:06 am, Robert Klemme <shortcut...@googlemail.com> wrote:
> 2009/6/10  <knu...@gmail.com>:
>
>
>
>
>
> > In my Ruby application, I accept TCP connections from several clients.
> > When
> > any client sends a particular command, I want to spawn a child process
> > in
> > place of the parent process that will continue to service the already-
> > open
> > TCP connections without the clients needing to reconnect.
>
> > I originally wrote this code on Linux, and it works in the most
> > obvious way:
>
> > When the command is received, make note of all of the client's file
> > descriptors (IO#fileno). Spawn the child process and communicate the
> > list of
> > file descriptors to it. In the child process, call TCPSocket.for_fd
> > for each
> > file descriptor, and it is ready to read and write data.
>
> I would find this more obvious:
>
> server = TCPServer.new port
>
> while ( client = server.accept )
>   printf "Got client %p\n", client.peeraddr
>
>   fork do
>     client.each do |line|
>       client.printf "ECHO: %p\n", line
>       client.flush
>     end
>
>     client.close_write
>   end
>
>   client.close
> end
>
> > Is there an entirely different
> > approach I should be taking?
>
> I do not know whether you _should_ take this approach but using cygwin
> might ease your pain considerably.  The approach noted above does work
> on cygwin.
>
> Kind regards
>
> robert
>
> --
> remember.guy do |as, often| as.you_can - without endhttp://blog.rubybestpract... Hide quoted text -
>
> - Show quoted text -

True, I did realize after I posted that perhaps my way isn't the "most
obvious." In any case, I'd very much like to avoid requiring cygwin
for this to work. Plus, with cygwin, I would guess my original Linux
approach would probably work without modification.

Finally, after testing it out briefly, I don't think fork will work in
my situation. I didn't give enough context originally, but I'm
spawning the new child process in order for it to be the same process
as the parent, but having picked up any code changes that have been
made. A "live reboot", if you will. Fork doesn't seem to re-interpret
the source.

Robert Klemme

6/10/2009 2:42:00 PM

0

2009/6/10 <knutaf@gmail.com>:

> True, I did realize after I posted that perhaps my way isn't the "most
> obvious." In any case, I'd very much like to avoid requiring cygwin
> for this to work. Plus, with cygwin, I would guess my original Linux
> approach would probably work without modification.

I don't understand the "plus". Why is that an additional reason to
not want to work with cygwin?

> Finally, after testing it out briefly, I don't think fork will work in
> my situation. I didn't give enough context originally,

Aha!

> but I'm
> spawning the new child process in order for it to be the same process
> as the parent, but having picked up any code changes that have been
> made. A "live reboot", if you will. Fork doesn't seem to re-interpret
> the source.

fork() just copies the process - with all open file descriptors etc.
My understanding of Windows internals is limited but I do believe that
there is no direct equivalent function. It may be that the pattern
does not work on "plain" Windows.

Kind regards

robert


--
remember.guy do |as, often| as.you_can - without end
http://blog.rubybestprac...

knutaf

6/10/2009 2:49:00 PM

0

On Jun 10, 7:41 am, Robert Klemme <shortcut...@googlemail.com> wrote:
> 2009/6/10  <knu...@gmail.com>:
>
> > True, I did realize after I posted that perhaps my way isn't the "most
> > obvious." In any case, I'd very much like to avoid requiring cygwin
> > for this to work. Plus, with cygwin, I would guess my original Linux
> > approach would probably work without modification.
>
> I don't understand the "plus".  Why is that an additional reason to
> not want to work with cygwin?

You caught me. That was bad wording on my part. s/Plus//.

> > Finally, after testing it out briefly, I don't think fork will work in
> > my situation. I didn't give enough context originally,
>
> Aha!
>
> > but I'm
> > spawning the new child process in order for it to be the same process
> > as the parent, but having picked up any code changes that have been
> > made. A "live reboot", if you will. Fork doesn't seem to re-interpret
> > the source.
>
> fork() just copies the process - with all open file descriptors etc.
> My understanding of Windows internals is limited but I do believe that
> there is no direct equivalent function. It may be that the pattern
> does not work on "plain" Windows.
>
> Kind regards
>
> robert
>
> --
> remember.guy do |as, often| as.you_can - without endhttp://blog.rubybestprac...

You're right. In fact, calling fork() on plain Windows throws a "not
supported by this platform" exception.

Reid Thompson

6/10/2009 2:55:00 PM

0

On Wed, 2009-06-10 at 23:50 +0900, knutaf@gmail.com wrote:
> On Jun 10, 7:41 am, Robert Klemme <shortcut...@googlemail.com> wrote:
> > 2009/6/10 <knu...@gmail.com>:
> >
> > > True, I did realize after I posted that perhaps my way isn't the "most
> > > obvious." In any case, I'd very much like to avoid requiring cygwin
> > > for this to work. Plus, with cygwin, I would guess my original Linux
> > > approach would probably work without modification.
> >
> > I don't understand the "plus". Why is that an additional reason to
> > not want to work with cygwin?
>
> You caught me. That was bad wording on my part. s/Plus//.
>
> > > Finally, after testing it out briefly, I don't think fork will work in
> > > my situation. I didn't give enough context originally,
> >
> > Aha!
> >
> > > but I'm
> > > spawning the new child process in order for it to be the same process
> > > as the parent, but having picked up any code changes that have been
> > > made. A "live reboot", if you will. Fork doesn't seem to re-interpret
> > > the source.
> >
> > fork() just copies the process - with all open file descriptors etc.
> > My understanding of Windows internals is limited but I do believe that
> > there is no direct equivalent function. It may be that the pattern
> > does not work on "plain" Windows.
> >
> > Kind regards
> >
> > robert
> >
> > --
> > remember.guy do |as, often| as.you_can - without endhttp://blog.rubybestprac...
>
> You're right. In fact, calling fork() on plain Windows throws a "not
> supported by this platform" exception.
>

see CreateProcess(...) for the closest win equivalent

http://msdn.microsoft.com/en-us/librar...(VS.85).aspx