about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2011-05-15 00:33:04 +0000
committerEric Wong <normalperson@yhbt.net>2011-05-19 20:39:33 +0000
commitc41c94fd1a7d3c4c92ffc81c8816e629bfa3d8a6 (patch)
treeb03ec8a36816f0c3a334e63cc02ebef581c02aec
parent6df27bca6c4aba321ee24e4dacfa507e1deaac73 (diff)
downloadsleepy_penguin-c41c94fd1a7d3c4c92ffc81c8816e629bfa3d8a6.tar.gz
-rw-r--r--ext/sleepy_penguin/epoll.c51
-rw-r--r--test/test_epoll.rb13
2 files changed, 55 insertions, 9 deletions
diff --git a/ext/sleepy_penguin/epoll.c b/ext/sleepy_penguin/epoll.c
index 81e7623..66bfc8c 100644
--- a/ext/sleepy_penguin/epoll.c
+++ b/ext/sleepy_penguin/epoll.c
@@ -1,6 +1,7 @@
 #include "sleepy_penguin.h"
 #include <sys/epoll.h>
 #include <pthread.h>
+#include <time.h>
 #include "missing_epoll.h"
 #ifdef HAVE_RUBY_ST_H
 #  include <ruby/st.h>
@@ -303,6 +304,36 @@ static VALUE epwait_result(struct rb_epoll *ep, int n)
         return INT2NUM(n);
 }
 
+static int epoll_expired_p(struct timespec *expire, struct rb_epoll *ep)
+{
+        struct timespec now;
+
+        fprintf(stderr, "old_timeout: %d\n", ep->timeout);
+        if (ep->timeout < 0)
+                return 0;
+        if (ep->timeout == 0)
+                return 1;
+
+        clock_gettime(CLOCK_MONOTONIC, &now);
+        if ((expire->tv_sec > now.tv_sec) ||
+            (expire->tv_sec == now.tv_sec) &&
+             (expire->tv_nsec < now.tv_nsec)) {
+                now.tv_sec = expire->tv_sec - now.tv_sec;
+                now.tv_nsec = expire->tv_nsec - now.tv_nsec;
+                if (now.tv_nsec < 0) {
+                        now.tv_nsec += 1000000000;
+                        now.tv_sec--;
+                }
+                ep->timeout = (now.tv_sec * 1000);
+                ep->timeout += (now.tv_nsec + 500000) / 1000000;
+                if (ep->timeout < 0)
+                        ep->timeout = 0;
+                fprintf(stderr, "new_timeout: %d\n", ep->timeout);
+                return 0;
+        }
+        return 1;
+}
+
 #if defined(HAVE_RB_THREAD_BLOCKING_REGION)
 static VALUE nogvl_wait(void *args)
 {
@@ -314,7 +345,17 @@ static VALUE nogvl_wait(void *args)
 
 static VALUE real_epwait(struct rb_epoll *ep)
 {
-        int n = (int)rb_sp_io_region(nogvl_wait, ep);
+        int n;
+        struct timespec expire;
+
+        if (ep->timeout > 0) {
+                clock_gettime(CLOCK_MONOTONIC, &expire);
+                expire.tv_sec += ep->timeout / 1000;
+                expire.tv_nsec += (ep->timeout % 1000) * 1000000;
+        }
+        do {
+                n = (int)rb_sp_io_region(nogvl_wait, ep);
+        } while (n == -1 && errno == EINTR && ! epoll_expired_p(&expire, ep));
 
         return epwait_result(ep, n);
 }
@@ -343,9 +384,11 @@ static int safe_epoll_wait(struct rb_epoll *ep)
 {
         int n;
 
-        TRAP_BEG;
-        n = epoll_wait(ep->fd, ep->events, ep->maxevents, 0);
-        TRAP_END;
+        do {
+                TRAP_BEG;
+                n = epoll_wait(ep->fd, ep->events, ep->maxevents, 0);
+                TRAP_END;
+        } while (n == -1 && errno == EINTR);
 
         return n;
 }
diff --git a/test/test_epoll.rb b/test/test_epoll.rb
index 69e4f4c..d3a5bdb 100644
--- a/test/test_epoll.rb
+++ b/test/test_epoll.rb
@@ -129,26 +129,29 @@ class TestEpoll < Test::Unit::TestCase
 
   def test_signal_safe
     time = {}
-    trap(:USR1) { time[:USR1] = Time.now; sleep 0.1; @wr.write '.' }
+    trap(:USR1) { time[:USR1] ||= Time.now; sleep 0.1; @wr.write '.' }
     @ep.add @rd, Epoll::IN
     tmp = []
     pid = fork do
-      sleep 0.1 # slightly racy :<
-      Process.kill(:USR1, Process.ppid)
+      4.times do
+        sleep 0.1 # slightly racy :<
+        Process.kill(:USR1, Process.ppid)
+      end
     end
     time[:START_WAIT] = Time.now
     begin
       @ep.wait { |flags, obj| tmp << [ flags, obj ]; time[:EP] = Time.now }
     rescue Errno::EINTR
+      puts "EINTR"
       retry
     end
     assert_equal([[Epoll::IN, @rd]], tmp)
     _, status = Process.waitpid2(pid)
     assert status.success?
     assert((time[:USR1] - time[:START_WAIT]) >= 0.1)
-    assert((time[:USR1] - time[:START_WAIT]) < 0.15)
+    assert((time[:USR1] - time[:START_WAIT]) < 0.25)
     assert((time[:EP] - time[:USR1]) >= 0.1)
-    assert((time[:EP] - time[:USR1]) < 0.15)
+    assert((time[:EP] - time[:USR1]) < 0.25)
     ensure
       trap(:USR1, 'DEFAULT')
   end unless RBX