Forum: Ruby Considering Ruby For a Networking Application

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
James G. (Guest)
on 2006-05-06 07:25
(Received via mailing list)
I'm going to be working on a fairly basic networking application
shortly.  This is low level socket work.  (I cannot use a nicety like
DRB sadly.)

I'm very familiar with this kind of stuff in Perl, but have done very
little socket work in Ruby.  I need something that can reliably work
with around 50 users sending light activity.  There is the
possibility of a large write to them here and there and that cannot
block the process.

I know a little from past questions here:

http://rubyurl.com/miq

However, I think I remember someone saying Ruby 1.8.4 is better with
nonblocking sockets.  So, can some networking guru take a stab at
these questions:

1.  I need to use threads *and* nonblocking sockets to be safe, right?
2.  Can I use gets() and puts() now in Ruby 1.8.4 or do I still need
to do low level reads and writes?
3.  Do I need to rescue Errno::EWOULDBLOCK?
4.  This isn't going to work on Windows, right?  (That's okay in this
case.  Just making sure.)

Thanks for the help.

James Edward G. II
Austin Z. (Guest)
on 2006-05-06 07:31
(Received via mailing list)
On 5/5/06, James Edward G. II <removed_email_address@domain.invalid> wrote:
> I'm going to be working on a fairly basic networking application
> shortly.  This is low level socket work.  (I cannot use a nicety like
> DRB sadly.)

James, you might want to look for "EventMachine".

It seems interesting.

-austin
Jon A. Lambert (Guest)
on 2006-05-06 08:13
(Received via mailing list)
James Edward G. II wrote:
>
> However, I think I remember someone saying Ruby 1.8.4 is better with
> nonblocking sockets.  So, can some networking guru take a stab at
> these questions:

I haven't noticed any difference between 1.8.3 and 1.8.4.

> 1.  I need to use threads *and* nonblocking sockets to be safe, right?

No.  The whole point of select() and non-blocking sockets is to
de-multiplex
I/O.

> 2.  Can I use gets() and puts() now in Ruby 1.8.4 or do I still need
> to do low level reads and writes?

I would guess these would be "unsafe" as would any network I/O function
that
depends on specific separators.   Would gets() effectively block on a
non-blocking socket if the EOL separator never arrives?
*shrug* I don't know.

> 3.  Do I need to rescue Errno::EWOULDBLOCK?

Yes.

> 4.  This isn't going to work on Windows, right?  (That's okay in this
> case.  Just making sure.)

Sure it work on Windows.

Here's a pretty good example.  No threads.  Single select client/server
code
that uses non-blocking sockets.

http://sourcery.dyndns.org/svn/teensymud/release/t...
Bill K. (Guest)
on 2006-05-06 08:43
(Received via mailing list)
From: "James Edward G. II" <removed_email_address@domain.invalid>
>
> to do low level reads and writes?
> 3.  Do I need to rescue Errno::EWOULDBLOCK?
> 4.  This isn't going to work on Windows, right?  (That's okay in this
> case.  Just making sure.)

I'm sorry I don't have definitive answers.  I'm going to
need to find out for sure pretty soon, myself.  But . . . .

From looking at the changes in the ruby sources between 1.8.2
and 1.8.4, it _looks_ as though nonblocking sockets are pretty
well supported now.  (Even on Windows!)

It would appear nonblocking is handled properly internally
whether you use ruby threads or not.  (I.e. from the perspective
of the underlying mechanisms, the main ruby thread is just as
much a thread as any other ruby thread.)

In 1.8.4, you can:

  require 'io/nonblock'

This adds methods to the IO class, allowing:

  some_socket.nonblock = true

In the past, I've done all my ruby socket programming using the
lower level send() and recv() methods.  However, again from
reading the source (io.c and ext/openssl/lib/openssl/buffering.rb)
it looks like IO#readpartial or IO#sysread (but *not* IO#read)
may be the new favored approach to dealing with nonblocking
descriptors in general.

(In Windows, I'm pretty sure only sockets have any chance of
working with nonblocking semantics.  Whereas in Unix, I'd
presume support for any file descriptor.  So using IO#sysread
and IO#syswrite, now that these methods apparently are
nonblocking-savvy, should be more general than using send()
and recv().)

If you look at ext/openssl/lib/openssl/buffering.rb, you'll
see what looks like a fairly generic module for building
a buffered read and readpartial, as well as write, on top of
sysread and syswrite.  I note that the lowest level read and
write methods here both rescue Errno::EAGAIN, and retry.  I'm
a little surprised by that - because it appears that EAGAIN
is already being handled along with EWOULDBLOCK in io.c
anyway.  But I haven't really studied the code in io.c, just
perused it.

Hmm..........

My sense of it--at least, this is what I plan to try in my
first test--is:

1. require 'io/nonblock' and set 'nonblock = true' on all
   my sockets.

2. use a separate ruby thread to handle I/O for each socket.

3. use IO#readpartial to read... (note that the comments in
   io.c say that IO#readpartial can block regardless of
   the nonblocking flag... but I'm pretty sure this means
   block the ruby thread, NOT the native thread...!)

4. use IO#syswrite to write... (I think, with 1.8.4 and a
   nonblocking socket, ruby _appears_ to be handling
   EWOULDBLOCK internally, so that we can do a large write
   but not block the whole process.)
   - But maybe we need to rescue Errno::EAGAIN for some
     reason, as seen in openssl/buffering.rb

With luck, this will work on Unix and Windows (sockets only
on windows, any file descriptor on unix), and never block
the whole native thread ruby runs in.

If so, we've got a reasonable solution for TCP.  UDP had its
own unique blocking issues (related to linux kernels breaking
POSIX semantics for select/recv for packets with bad UDP
checksums.... or something like that.... I dunno if this has
changed since 1.8.2 or not...)


Or, yeah, maybe that EventMachine library... <grin>


Regards,

Bill
Bill K. (Guest)
on 2006-05-06 08:46
(Received via mailing list)
From: "Jon A. Lambert" <removed_email_address@domain.invalid>
> James Edward G. II wrote:
>
>> 4.  This isn't going to work on Windows, right?  (That's okay in this
>> case.  Just making sure.)
>
> Sure it work on Windows.

If nonblocking works on Windows, it's a post-1.8.2 development...
(But I'm stoked if it does, now.)


Regards,

Bill
James G. (Guest)
on 2006-05-06 22:02
(Received via mailing list)
On May 5, 2006, at 10:28 PM, Austin Z. wrote:

> On 5/5/06, James Edward G. II <removed_email_address@domain.invalid> wrote:
>> I'm going to be working on a fairly basic networking application
>> shortly.  This is low level socket work.  (I cannot use a nicety like
>> DRB sadly.)
>
> James, you might want to look for "EventMachine".

This is a terrific find.  Thank you so much!

James Edward G. II
James G. (Guest)
on 2006-05-06 22:05
(Received via mailing list)
On May 5, 2006, at 11:42 PM, Bill K. wrote:

> I'm sorry I don't have definitive answers.  I'm going to
> need to find out for sure pretty soon, myself.  But . . . .

Thank you for the very detailed breakdown.  It was quite helpful.

James Edward G. II
James G. (Guest)
on 2006-05-06 22:05
(Received via mailing list)
On May 5, 2006, at 11:13 PM, Jon A. Lambert wrote:

> Here's a pretty good example.  No threads.  Single select client/
> server code that uses non-blocking sockets.
>
> http://sourcery.dyndns.org/svn/teensymud/release/t...
> network/

Yeah, this is pretty much how I did it in Perl.  Thank you from the
example though.

James Edward G. II

P.S.  I need to play with your MUD sometime.  Looks like a blast!
Francis C. (Guest)
on 2006-05-08 21:16
(Received via mailing list)
A great deal depends on what you really need to accomplish. Are you
doing a
plain-vanilla protocol handler for some standard server protocol? If
your
performance and scalability requirements are low, then the easiest thing
is
probably to use blocking i/o with a thread per socket. If you use
blocking
i/o, you're better off multiplexing the i/o with a select loop. (My
preference in these cases is ALMOST ALWAYS to avoid a thread pool for a
range of reasons, but most people disagree with me ;-).) If you use
nonblocking i/o, then you really want to avoid multithreading because
you'll
need to poll in each one. Ruby can handle it because its implementation
of
select is integrated with its internal thread scheduler. (If you try to
run
select on a native thread, you'll have major problems with Ruby
threads.)

If your process has to do work on other threads, meaning this isn't a
pure
server application, then
the calculations change. UDP is really quite a lot easier than TCP, but
it
always seems to take some getting used to. So the major questions are:
tell
us more about the nature of the application, and tell us what kind of
performance and scalability you need. And additionally, do you need
encryption?


If you decide to look at EventMachine, I can support you if you like.
Just
email me or ask questions here. It runs on Windows, works correctly with
Ruby threads, and supports encryption.
James G. (Guest)
on 2006-05-08 22:43
(Received via mailing list)
On May 8, 2006, at 12:12 PM, Francis C. wrote:

> If your performance and scalability requirements are low, then the
> easiest thing is
> probably to use blocking i/o with a thread per socket. If you use
> blocking
> i/o, you're better off multiplexing the i/o with a select loop.

But then a large write could still block the entire process, since
Ruby's threads aren't native.  Right?

> If you decide to look at EventMachine, I can support you if you like.

I believe this is going to be my first choice.  I haven't had time to
play with it yet, but it looks very nice.

Are there API docs available anywhere for EventMachine?

James Edward G. II
Francis C. (Guest)
on 2006-05-08 23:11
(Received via mailing list)
Writes can block your process whether they are large or small- it all
depends on what is happening in your kernel at that particular point in
time. However, if a socket has just selected writable, then it generally
will be able to take a write that is the size of an ethernet packet
(1400
bytes or so).  (Of course the sharper readers will point out that there
are
also good reasons to avoid such small writes, especially if you don't
turn
off the Nagle algorithm.) But you're asking a good question. This is one
of
the reasons why blocking i/o on threads can make a lot of sense if your
app
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.

If you download the eventmachine gem and install the rdocs, the API docs
will all be there, and there is sample code too.
James G. (Guest)
on 2006-05-08 23:23
(Received via mailing list)
On May 8, 2006, at 2:09 PM, Francis C. wrote:

> If you download the eventmachine gem and install the rdocs, the API
> docs
> will all be there, and there is sample code too.

Forgive me if this is answered in the documentation, I have had time
to look:  Is EventMachine multi-threaded?  I'm mainly wondering if I
need to synchronize the methods that socket data arriving.

James Edward G. II
Francis C. (Guest)
on 2006-05-08 23:24
(Received via mailing list)
James, your application needs to support about 50 users with the
occasional
large write. Is the application a client app or a server app. It's funny
to
re-read your message, because I just started designing a
presence-dissemination system, which will have very similar requirements
to
yours! (The network protocol will work a lot like BGP4, except that the
"routing tables" are actually the IM-presence status of users.) And this
will be both a client and a server.
James G. (Guest)
on 2006-05-08 23:27
(Received via mailing list)
On May 8, 2006, at 2:21 PM, Francis C. wrote:

> James, your application needs to support about 50 users with the
> occasional
> large write. Is the application a client app or a server app.

I'm writing a server.

James Edward G. II
Francis C. (Guest)
on 2006-05-08 23:33
(Received via mailing list)
I'm glad you asked. Eventmachine is single-threaded. That's an essential
part of its design. (More for performance and scalability than to remove
any
need for synchronization, which is very difficult between native threads
and
Ruby threads. (Well, it's actually impossible. Eventmachine does it
through
a mechanism that is unrelated to threads.) There's an EventMachine wiki
that
has a page on the "Reactor model" which explains this in more detail.
http://eventmachine.rubyforge.org/wiki/wiki.pl

However, if you want to implement a thread-pool in Ruby threads to
handle
your workload, that's perfectly compatible with Eventmachine, and your
threads will run concurrently with Eventmachine. This might make sense
if
the processing you have to do on behalf of your clients involves
something
like writing to a database, where there is a system latency that you
can't
hand off to Eventmachine (because DBMS client libraries are usually not
asynchronous).
James G. (Guest)
on 2006-05-08 23:43
(Received via mailing list)
On May 8, 2006, at 2:32 PM, Francis C. 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 G. II
Bill K. (Guest)
on 2006-05-09 01:27
(Received via mailing list)
From: "James Edward G. II" <removed_email_address@domain.invalid>
> Ruby's threads aren't native.  Right?
Judging from the results of my test program on Linux and
Windows, it appears nonblocking writes are supported in
1.8.4.

ruby 1.8.4 (2005-12-24) [i686-linux]
ruby 1.8.4 (2005-12-24) [i386-mswin32]

Interestingly, while the Linux version behaved as expected,
displaying blocking characteristics until I explicitly set
nonblock=true, the windows version appears to sport some
sort of always-on nonblocking buffered implementation behind
the scenes now.

On Windows, even if I never set nonblock=true, I could syswrite
40 megs to the socket and #syswrite would return immediately!
(Subsequent syswrites would then appear to block until the
data from the previous huge syswrite was consumed; but just the
ruby thread performing the syswrite was blocked, never the whole
process.  So anyway there's some kind of dynamic buffering going
on behind the scenes in the windows implementation--but in any
case, it never blocked the whole process.  Excellent.)

In my test program (attached) I'm catching just about every
exception under the sun, including EAGAIN, which I saw being
done in lib/openssl/buffering.rb.  However, the only
exception I actually saw raised was EOFError.  (I never tried
to force an EPIPE condition on the write.)

Anyway, for what it's worth, it appears ruby 1.8.4 now
definitely supports nonblocking socket I/O on both Linux
and Windows!!  Yay!

I'll still be looking at EventMachine though.  :)


Regards,

Bill
James G. (Guest)
on 2006-05-09 02:04
(Received via mailing list)
On May 8, 2006, at 4:24 PM, Bill K. wrote:

> Judging from the results of my test program on Linux and
> Windows, it appears nonblocking writes are supported in
> 1.8.4.

This is some awesome information.  Thanks Bill!

I believe I am going to try EventMachine first though as it looks
quite nice to work with.

James Edward G. II
Francis C. (Guest)
on 2006-05-09 04:39
(Received via mailing list)
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.
Francis C. (Guest)
on 2006-05-09 04:45
(Received via mailing list)
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 :(  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);

This will return non-zero in case of error (I've never seen it return an
error).
Best
-francis
Bill K. (Guest)
on 2006-05-09 04:58
(Received via mailing list)
From: "Francis C." <removed_email_address@domain.invalid>
>   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
Francis C. (Guest)
on 2006-05-09 05:04
(Received via mailing list)
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.)
Francis C. (Guest)
on 2006-05-09 05:14
(Received via mailing list)
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.
James G. (Guest)
on 2006-05-09 05:17
(Received via mailing list)
On May 8, 2006, at 7:36 PM, Francis C. 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 G. II
Francis C. (Guest)
on 2006-05-09 05:23
(Received via mailing list)
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.)
Bill K. (Guest)
on 2006-05-09 05:41
(Received via mailing list)
From: "Francis C." <removed_email_address@domain.invalid>
>
> 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... :)


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.  :)


Regards,

Bill
Francis C. (Guest)
on 2006-05-09 05:54
(Received via mailing list)
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.
Tanaka A. (Guest)
on 2006-05-09 06:52
(Received via mailing list)
In article <01ed01c67309$01656540$6442a8c0@musicbox>,
  "Bill K." <removed_email_address@domain.invalid> 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... :)

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)
Bill K. (Guest)
on 2006-05-09 07:25
(Received via mailing list)
"Tanaka A." <removed_email_address@domain.invalid> wrote:

> 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 A. (Guest)
on 2006-05-09 07:58
(Received via mailing list)
In article <021f01c67317$905a5900$6442a8c0@musicbox>,
  "Bill K." <removed_email_address@domain.invalid> 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.
Giles B. (Guest)
on 2006-05-09 08:44
(Received via mailing list)
Francis -- just out of curiousity -- is the Reactor model in
Eventmachine similar to what happens inside the Twisted networking
framework in Python?
Francis C. (Guest)
on 2006-05-09 16:04
(Received via mailing list)
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.
Francis C. (Guest)
on 2006-05-09 16:20
(Received via mailing list)
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.
Sam R. (Guest)
on 2006-05-11 08:21
(Received via mailing list)
Quoting removed_email_address@domain.invalid, 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
Francis C. (Guest)
on 2006-05-11 08:36
(Received via mailing list)
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?)
Sam R. (Guest)
on 2006-05-16 08:17
(Received via mailing list)
Quoting removed_email_address@domain.invalid, 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
This topic is locked and can not be replied to.