diff options
author | Eric Wong <normalperson@yhbt.net> | 2011-05-15 00:33:04 +0000 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2011-05-19 20:39:33 +0000 |
commit | c41c94fd1a7d3c4c92ffc81c8816e629bfa3d8a6 (patch) | |
tree | b03ec8a36816f0c3a334e63cc02ebef581c02aec | |
parent | 6df27bca6c4aba321ee24e4dacfa507e1deaac73 (diff) | |
download | sleepy_penguin-c41c94fd1a7d3c4c92ffc81c8816e629bfa3d8a6.tar.gz |
-rw-r--r-- | ext/sleepy_penguin/epoll.c | 51 | ||||
-rw-r--r-- | test/test_epoll.rb | 13 |
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 |