blocking functions do not raise Errno::EINTR
authorEric Wong <normalperson@yhbt.net>
Wed, 4 Jul 2012 00:57:44 +0000 (3 17:57 -0700)
committerEric Wong <normalperson@yhbt.net>
Thu, 5 Jul 2012 20:17:02 +0000 (5 13:17 -0700)
Blocking functions should not raise Errno::EINTR to match
existing semantics of Ruby IO methods (e.g. IO.select, IO#read,
IO#write).  This makes user code easier to read/write.

Like th Ruby methods we emulate, we only reacquire the GVL on
EINTR to fire signal handlers, but otherwise emulate SA_RESTART
semantics.

This is a backwards-incompatible API change (but unlikely
to break existing code).

ext/posix_mq/posix_mq.c
test/test_posix_mq.rb

index 2958352..df667e3 100644 (file)
@@ -542,8 +542,11 @@ static VALUE _send(int sflags, int argc, VALUE *argv, VALUE self)
        x.timeout = convert_timeout(&expire, timeout);
        x.msg_prio = NIL_P(prio) ? 0 : NUM2UINT(prio);
 
+retry:
        rv = (mqd_t)rb_thread_blocking_region(xsend, &x, RUBY_UBF_IO, 0);
        if (rv == MQD_INVALID) {
+               if (errno == EINTR)
+                       goto retry;
                if (errno == EAGAIN && (sflags & PMQ_TRY))
                        return Qfalse;
                rb_sys_fail("mq_send");
@@ -574,9 +577,13 @@ static VALUE send0(VALUE self, VALUE buffer)
        x.timeout = NULL;
        x.msg_prio = 0;
 
+retry:
        rv = (mqd_t)rb_thread_blocking_region(xsend, &x, RUBY_UBF_IO, 0);
-       if (rv == MQD_INVALID)
+       if (rv == MQD_INVALID) {
+               if (errno == EINTR)
+                       goto retry;
                rb_sys_fail("mq_send");
+       }
 
        return self;
 }
@@ -678,8 +685,11 @@ static VALUE _receive(int rflags, int argc, VALUE *argv, VALUE self)
        x.msg_len = (size_t)mq->attr.mq_msgsize;
        x.des = mq->des;
 
+retry:
        r = (ssize_t)rb_thread_blocking_region(xrecv, &x, RUBY_UBF_IO, 0);
        if (r < 0) {
+               if (errno == EINTR)
+                       goto retry;
                if (errno == EAGAIN && (rflags & PMQ_TRY))
                        return Qnil;
                rb_sys_fail("mq_receive");
index 5b78c22..8d6b014 100644 (file)
@@ -118,16 +118,18 @@ class Test_POSIX_MQ < Test::Unit::TestCase
     end
     alarm or return warn "alarm() not found in #{libcs.inspect}"
     alarms = 0
-    trap("ALRM") { alarms += 1 }
+    trap("ALRM") do
+      alarms += 1
+      Thread.new { @mq.send("HI") }
+    end
     interval = 1
     alarm.call interval
     @mq = POSIX_MQ.new(@path, :rw)
     assert ! @mq.nonblock?
     t0 = Time.now
-    a = nil
-    assert_raises(Errno::EINTR) { a = @mq.receive }
+    a = @mq.receive
     elapsed = Time.now - t0
-    assert_nil a
+    assert_equal(["HI", 0], a)
     assert elapsed >= interval, elapsed.inspect
     assert elapsed < 1.10, elapsed.inspect
     assert_equal 1, alarms