Considering Ruby For a Networking Application

Works fine. It's not a ruby-specific issue. You have to remember to set the
accepting socket non-blocking, and then be aware that it may return EAGAIN
(or whatever your platform-specific equivalent is) even if it selected
readable.

(This was two days ago and now I can't remember the context this came up
in. Were we talking about apps that prefer to use blocking i/o?)

···

On 5/11/06, Sam Roberts <sroberts@uniserve.com> wrote:

Quoting garbagecat10@gmail.com, on Tue, May 09, 2006 at 04:09:20AM +0900:
> has relatively light requirements. You may find this odd but I worry
more
> about accepts blocking than writes. An accepting socket can select
readable
> (meaning a new connection is available), but be empty by the time you
get
> around to reading it (like if there was a network error that caused a
> pending connection to reset). This can be a real nasty.

Stevens describes this problem, but I thought he also described the fix,
set the socket to non-blocking so the accept() won't block.

doesn't this work? is this a ruby-specific problem?

Sam

So my callbacks need to return reasonably quickly right? So EventMachine can get back to processing input from other sockets. If I need to do a long-running calculation, I should spawn a Ruby Thread for that. Sound right?

I'm very excited about working with you're library. Thanks so much for answering my questions. I'm sure I will have more at some point...

James Edward Gray II

···

On May 8, 2006, at 2:32 PM, Francis Cianfrocca wrote:

I'm glad you asked. Eventmachine is single-threaded. That's an essential
part of its design.

When you run nonblock_test.rb in Windows, does it ever catch EAGAIN on the
read thread? (I'm too lazy to track down a Windows box to run it on.)

···

On 5/8/06, Bill Kelly <billk@cts.com> wrote:

From: "Francis Cianfrocca" <garbagecat10@gmail.com>
>
> From what you said, this code doesn't do what you think it should do:
>
> # hmm, io/nonblock refuses to work on windows because
> # of missing GETFL :frowning: Just fake it...
> if RUBY_PLATFORM =~ /mswin32/
> class IO
> def nonblock=(nb)
> fcntl(Fcntl::F_SETFL, nb ? Fcntl::O_NONBLOCK : 0)
> end
> end
> end
>
> That's because on Windows, this isn't the way to set sockets
nonblocking.
> Microsoft reinvented a lot of wheels in Windows, and this is one of 'em.
Try
> this C code instead (I'll leave it to you to turn it into Ruby):
>
> unsigned long one = 1;
> ioctlsocket (the_socket_descriptor, FIONBIO, &one);

Right, except that in win32/win32.c, ruby corrects microsoft's
misfeatures for us, by making fcntl() on win32-ruby shunt to
ioctlsocket behind the scenes:

int
fcntl(int fd, int cmd, ...)
{
//[...]

    if (arg & O_NONBLOCK) {
        ioctlArg = 1;
    }
    else {
        ioctlArg = 0;
    }
    RUBY_CRITICAL({
        ret = ioctlsocket(sock, FIONBIO, &ioctlArg);
        if (ret == -1) {
            errno = map_errno(WSAGetLastError());
        }
    });

Regards,

Bill

One more thing, what happens if you run this program on Windows on a
network? You have an intra-process localhost connection and the kernel may
be behaving differently in this case, particularly as regards the buffer
usage that would interact with nonblocking i/o.

Thanks for correcting me on the wrapper in win32.c. I have to say, it's a
strange design choice. To me it would have made more sense to wrap both
behind a "setnonblocking" method. Also, that source file is interesting, it
looks like the Ruby folks borrowed it.

···

On 5/8/06, Bill Kelly <billk@cts.com> wrote:

From: "Francis Cianfrocca" <garbagecat10@gmail.com>
>
> From what you said, this code doesn't do what you think it should do:
>
> # hmm, io/nonblock refuses to work on windows because
> # of missing GETFL :frowning: Just fake it...
> if RUBY_PLATFORM =~ /mswin32/
> class IO
> def nonblock=(nb)
> fcntl(Fcntl::F_SETFL, nb ? Fcntl::O_NONBLOCK : 0)
> end
> end
> end
>
> That's because on Windows, this isn't the way to set sockets
nonblocking.
> Microsoft reinvented a lot of wheels in Windows, and this is one of 'em.
Try
> this C code instead (I'll leave it to you to turn it into Ruby):
>
> unsigned long one = 1;
> ioctlsocket (the_socket_descriptor, FIONBIO, &one);

Right, except that in win32/win32.c, ruby corrects microsoft's
misfeatures for us, by making fcntl() on win32-ruby shunt to
ioctlsocket behind the scenes:

int
fcntl(int fd, int cmd, ...)
{
//[...]

    if (arg & O_NONBLOCK) {
        ioctlArg = 1;
    }
    else {
        ioctlArg = 0;
    }
    RUBY_CRITICAL({
        ret = ioctlsocket(sock, FIONBIO, &ioctlArg);
        if (ret == -1) {
            errno = map_errno(WSAGetLastError());
        }
    });

Regards,

Bill

Quoting garbagecat10@gmail.com, on Thu, May 11, 2006 at 01:35:58PM +0900:

Works fine. It's not a ruby-specific issue. You have to remember to set the
accepting socket non-blocking, and then be aware that it may return EAGAIN
(or whatever your platform-specific equivalent is) even if it selected
readable.

(This was two days ago and now I can't remember the context this came up
in. Were we talking about apps that prefer to use blocking i/o?)

I think it was in the contex of ruby's socket apis not behaving as
expected, maybe a few things got mixed up there.

Cheers,
Sam

Yes, you want your callbacks to return quickly. Spin a Ruby thread
otherwise. If it turns out that many users of Eventmachine face that issue
(such as doing a database call while processing a callback) then we'll look
at incorporating a thread-pool into Eventmachine. That would fit with the
goals of Eventmachine, which are performance/scalability, ease-of-use, and
feature-completeness for server implementations.

···

On 5/8/06, James Edward Gray II <james@grayproductions.net> wrote:

On May 8, 2006, at 2:32 PM, Francis Cianfrocca wrote:

> I'm glad you asked. Eventmachine is single-threaded. That's an
> essential
> part of its design.

So my callbacks need to return reasonably quickly right? So
EventMachine can get back to processing input from other sockets. If
I need to do a long-running calculation, I should spawn a Ruby Thread
for that. Sound right?

I'm very excited about working with you're library. Thanks so much
for answering my questions. I'm sure I will have more at some point...

James Edward Gray II

When you run nonblock_test.rb in Windows, does it ever catch EAGAIN on the
read thread? (I'm too lazy to track down a Windows box to run it on.)

No; the only exception I ever saw raised was EOFError from the
readpartial.

I included the EAGAIN handling because the code in
lib/openssl/buffering.rb is written that way. But, I was never
sure it should be necessary, from what I'd seen in the io.c,
win32/win32.c, and ext/socket/socket.c source.

From casually perusing the source, it appears to me as though
EAGAIN/EWOULDBLOCK are being handled internally; so I'm unclear
on why/when one would expect to need to handle them from ruby.

(And, as yet, I've not seen an EAGAIN from readpartial or
syswrite in my tests on either Linux or Windows... not that my
tests have been by any means exhaustive... :slight_smile:

Continuing to your next post:

One more thing, what happens if you run this program on Windows on a
network? You have an intra-process localhost connection and the kernel may
be behaving differently in this case, particularly as regards the buffer
usage that would interact with nonblocking i/o.

Good point; I'll try that...

[...] the wrapper in win32.c. I have to say, it's a strange
design choice. To me it would have made more sense to wrap both
behind a "setnonblocking" method.

I'd tend to agree, but I think it's partly historical. The
nonblocking handling (including the ftctl -> ioctlsocket shunt)
magically appeared in ruby sometime between 1.8.2 and 1.8.4.
Previously, there had been discussions on the mailing lists about
how to address the blocking issues, and suggestions for adding
a set of nonblock_read(), etc. methods, among other suggestions.
As I recall, Matz was supportive of seeing a solution to the
blocking issues, but didn't like any of the as-yet proposed
method names. My guess is, some enterprising soul(s) between
1.8.2 and 1.8.4 found an acceptable way to make the solution
happen.... without changing the existing APIs. :slight_smile:

Regards,

Bill

···

From: "Francis Cianfrocca" <garbagecat10@gmail.com>

I personally am fine with the current implementation. Seems like a perfect fit with Ruby's threads.

James Edward Gray II

···

On May 8, 2006, at 7:36 PM, Francis Cianfrocca wrote:

Yes, you want your callbacks to return quickly. Spin a Ruby thread
otherwise. If it turns out that many users of Eventmachine face that issue
(such as doing a database call while processing a callback) then we'll look
at incorporating a thread-pool into Eventmachine. That would fit with the
goals of Eventmachine, which are performance/scalability, ease-of-use, and
feature-completeness for server implementations.

Your guess about how that Intergraph library happened to get into the distro
makes a lot of sense. They can also roll it back out it they ever decide to,
because the core APIs never got changed :-). At the end of the day, however,
this convinces me all the more that it was right for us to offer a
higher-level library that wraps up network i/o as much as possible. We wrote
a SIP stack in pure Ruby last year, and it was very painful to get it right
because Ruby's implementation of i/o kept violating expectations.

···

On 5/8/06, Bill Kelly <billk@cts.com> wrote:

From: "Francis Cianfrocca" <garbagecat10@gmail.com>
>
> When you run nonblock_test.rb in Windows, does it ever catch EAGAIN on
the
> read thread? (I'm too lazy to track down a Windows box to run it on.)

No; the only exception I ever saw raised was EOFError from the
readpartial.

I included the EAGAIN handling because the code in
lib/openssl/buffering.rb is written that way. But, I was never
sure it should be necessary, from what I'd seen in the io.c,
win32/win32.c, and ext/socket/socket.c source.

From casually perusing the source, it appears to me as though
EAGAIN/EWOULDBLOCK are being handled internally; so I'm unclear
on why/when one would expect to need to handle them from ruby.

(And, as yet, I've not seen an EAGAIN from readpartial or
syswrite in my tests on either Linux or Windows... not that my
tests have been by any means exhaustive... :slight_smile:

Continuing to your next post:

> One more thing, what happens if you run this program on Windows on a
> network? You have an intra-process localhost connection and the kernel
may
> be behaving differently in this case, particularly as regards the buffer
> usage that would interact with nonblocking i/o.

Good point; I'll try that...

> [...] the wrapper in win32.c. I have to say, it's a strange
> design choice. To me it would have made more sense to wrap both
> behind a "setnonblocking" method.

I'd tend to agree, but I think it's partly historical. The
nonblocking handling (including the ftctl -> ioctlsocket shunt)
magically appeared in ruby sometime between 1.8.2 and 1.8.4.
Previously, there had been discussions on the mailing lists about
how to address the blocking issues, and suggestions for adding
a set of nonblock_read(), etc. methods, among other suggestions.
As I recall, Matz was supportive of seeing a solution to the
blocking issues, but didn't like any of the as-yet proposed
method names. My guess is, some enterprising soul(s) between
1.8.2 and 1.8.4 found an acceptable way to make the solution
happen.... without changing the existing APIs. :slight_smile:

Regards,

Bill

In article <01ed01c67309$01656540$6442a8c0@musicbox>,
  "Bill Kelly" <billk@cts.com> writes:

No; the only exception I ever saw raised was EOFError from the
readpartial.

I included the EAGAIN handling because the code in
lib/openssl/buffering.rb is written that way. But, I was never
sure it should be necessary, from what I'd seen in the io.c,
win32/win32.c, and ext/socket/socket.c source.

I modified the document of readpartial.
I hope it makes the behaviour clear.

Index: io.c

···

===================================================================
RCS file: /src/ruby/io.c,v
retrieving revision 1.401
diff -u -p -r1.401 io.c
--- io.c 28 Mar 2006 01:50:11 -0000 1.401
+++ io.c 9 May 2006 02:10:04 -0000
@@ -1337,7 +1337,7 @@ io_getpartial(int argc, VALUE *argv, VAL
  * r.readpartial(4096) #=> "ghi\n" "" ""
  *
  * Note that readpartial is nonblocking-flag insensitive.
- * It blocks even if the nonblocking-flag is set.
+ * It blocks on the situation IO#sysread causes Errno::EAGAIN.
  *
  * Also note that readpartial behaves similar to sysread in blocking mode.
  * The behavior is identical when the buffer is empty.

From casually perusing the source, it appears to me as though
EAGAIN/EWOULDBLOCK are being handled internally; so I'm unclear
on why/when one would expect to need to handle them from ruby.

(And, as yet, I've not seen an EAGAIN from readpartial or
syswrite in my tests on either Linux or Windows... not that my
tests have been by any means exhaustive... :slight_smile:

IO#syswrite causes EAGAIN.

% ./ruby -rfcntl -ve '
r, w = IO.pipe
w.fcntl(Fcntl::F_SETFL, File::NONBLOCK|w.fcntl(Fcntl::F_GETFL))
p w.syswrite("a" * 70000)
p w.syswrite("a" * 70000)
'
ruby 1.9.0 (2006-05-01) [i686-linux]
65536
-e:5:in `IO#syswrite': Resource temporarily unavailable (Errno::EAGAIN)
        from -e:5

It is also possible on sockets.

% ./ruby -rsocket -rfcntl -ve '
a = TCPServer.new 20000
r = TCPSocket.new "localhost", 20000
w = a.accept
w.fcntl(Fcntl::F_SETFL, File::NONBLOCK|w.fcntl(Fcntl::F_GETFL))
p w.syswrite("a" * 70000)
p w.syswrite("a" * 70000)
p w.syswrite("a" * 70000)
p w.syswrite("a" * 70000)
p w.syswrite("a" * 70000)
'
ruby 1.9.0 (2006-05-01) [i686-linux]
65536
16384
-e:8:in `IO#syswrite': Resource temporarily unavailable (Errno::EAGAIN)
        from -e:8

The condition is
1. IO#syswrite
2. pipe/socket (kernel) buffer is full
3. Ruby has only one thread (No I/O multiplexing using select)
--
Tanaka Akira

If you try it out, I'll be very interested in hearing how it goes. We're
taking feature requests, too :-).
(We're currently re-implementing the Windows version to use async i/o, but
that probably doesn't matter to you.)

···

On 5/8/06, James Edward Gray II <james@grayproductions.net> wrote:

On May 8, 2006, at 7:36 PM, Francis Cianfrocca wrote:

> Yes, you want your callbacks to return quickly. Spin a Ruby thread
> otherwise. If it turns out that many users of Eventmachine face
> that issue
> (such as doing a database call while processing a callback) then
> we'll look
> at incorporating a thread-pool into Eventmachine. That would fit
> with the
> goals of Eventmachine, which are performance/scalability, ease-of-
> use, and
> feature-completeness for server implementations.

I personally am fine with the current implementation. Seems like a
perfect fit with Ruby's threads.

James Edward Gray II

I modified the document of readpartial.
I hope it makes the behaviour clear.

[...]

- * It blocks even if the nonblocking-flag is set.
+ * It blocks on the situation IO#sysread causes Errno::EAGAIN.

Ah - OK, thanks!

IO#syswrite causes EAGAIN.

[...]

It is also possible on sockets.

[...]

The condition is
1. IO#syswrite
2. pipe/socket (kernel) buffer is full
3. Ruby has only one thread (No I/O multiplexing using select)

Wow. Thanks for the details! I'm going to have to eat my
words on a couple recent posts where I thought ruby behaved
identically regardless of whether just one thread or multiple
threads existed.

Out of curiosity, is there a technical reason why it's desirable for #sysread to raise EAGAIN in this one situation?

It seems strange to have to clutter the ruby code with EAGAIN
checks, if just saying Thread.new{loop{sleep(1000000)}} means
I'll never have to check for EAGAIN in ruby.

If that makes sense. I'm just wondering if there's a technical
reason why we wouldn't want the C code to hide EAGAIN from ruby code in all cases, not just most cases.

Thanks,

Regards,

Bill

···

"Tanaka Akira" <akr@m17n.org> wrote:

In article <021f01c67317$905a5900$6442a8c0@musicbox>,
  "Bill Kelly" <billk@cts.com> writes:

Wow. Thanks for the details! I'm going to have to eat my
words on a couple recent posts where I thought ruby behaved
identically regardless of whether just one thread or multiple
threads existed.

As far as I know, matz found the inconsistency in
[ruby-talk:66196]. It is not intended.

Out of curiosity, is there a technical reason why it's
desirable for #sysread to raise EAGAIN in this one situation?

1. In general, matz respect programmer's knowledge on POSIX.
   For example, "read(2) system call cause EAGAIN on
   non-blocking mode" in this case. I don't think many
   programmers understand the non-blocking issue well,
   though.

2. Some application, maybe event driven system, needs
   EAGAIN. (If no application needs EAGAIN, why POSIX has
   EAGAIN?)

However checking O_NONBLOCK before read(2) have race
condition if the fd is shared with another process. So I
recommend other solution if possible.

···

--
Tanaka Akira

Francis -- just out of curiousity -- is the Reactor model in
Eventmachine similar to what happens inside the Twisted networking
framework in Python?

···

--
Giles Bowkett
http://www.gilesgoatboy.org

Well, I don't actually know anything about the innards of Twisted, but I'll
try to find out. The "reactor" pattern has been around for a while. As I
recall it started getting more currency a few years ago when the whole
"C10K" issue was being discussed. To me, it's one of the ways of recognizing
that specific issues of blocking vs. nonblocking i/o, threaded vs.
nonthreaded and all the rest of the controversial stuff are really low-level
issues. And they shouldn't pollute the application-level domain where most
programmers work, because they add no value there. What the reactor pattern
says is: "if you're willing to think of a network protocol as a state
machine rather than a conversation, then I can enable to you process far
more users in much less time."

And for what it's worth, the reactor model may not be the best way to do
Windows servers, because unlike any mainstream Unix flavor, Windows actually
has excellent async i/o. We're currently in the middle of rewriting the
Windows side of EventMachine to use async instead of nonblocking i/o.

···

On 5/9/06, Giles Bowkett <gilesb@gmail.com> wrote:

Francis -- just out of curiousity -- is the Reactor model in
Eventmachine similar to what happens inside the Twisted networking
framework in Python?

--
Giles Bowkett
http://www.gilesgoatboy.org

Well, Giles, now I'm really glad you asked the question about Twisted. I'm
going to open another thread about this because I'm curious to know what
others think.

···

On 5/9/06, Giles Bowkett <gilesb@gmail.com> wrote:

Francis -- just out of curiousity -- is the Reactor model in
Eventmachine similar to what happens inside the Twisted networking
framework in Python?

--
Giles Bowkett
http://www.gilesgoatboy.org