sleepy_penguin 1.4.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -4,15 +4,31 @@
4
4
  #include "value2timespec.h"
5
5
  static ID id_for_fd;
6
6
 
7
- static VALUE create(int argc, VALUE *argv, VALUE klass)
7
+ /*
8
+ * call-seq:
9
+ * TimerFD.new([clockid[, flags]]) -> TimerFD IO object
10
+ *
11
+ * Creates a new timer as an IO object.
12
+ *
13
+ * If set +clockid+ must be be one of the following:
14
+ * - :REALTIME - use the settable clock
15
+ * - :MONOTONIC - use the non-settable clock unaffected by manual changes
16
+ *
17
+ * +clockid+ defaults to :MONOTONIC if unspecified
18
+ * +flags+ may be any or none of the following:
19
+ *
20
+ * - :CLOEXEC - set the close-on-exec flag on the new object
21
+ * - :NONBLOCK - set the non-blocking I/O flag on the new object
22
+ */
23
+ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
8
24
  {
9
25
  VALUE cid, fl;
10
- int clockid, flags = 0;
26
+ int clockid, flags;
11
27
  int fd;
12
28
 
13
29
  rb_scan_args(argc, argv, "02", &cid, &fl);
14
- clockid = NIL_P(cid) ? CLOCK_MONOTONIC : NUM2INT(cid);
15
- flags = NIL_P(fl) ? 0 : NUM2INT(fl);
30
+ clockid = NIL_P(cid) ? CLOCK_MONOTONIC : rb_sp_get_flags(klass, cid);
31
+ flags = rb_sp_get_flags(klass, fl);
16
32
 
17
33
  fd = timerfd_create(clockid, flags);
18
34
  if (fd == -1) {
@@ -35,10 +51,22 @@ static VALUE itimerspec2ary(struct itimerspec *its)
35
51
  return rb_ary_new3(2, interval, value);
36
52
  }
37
53
 
54
+ /*
55
+ * call-seq:
56
+ * tfd.settime(flags, interval, value) -> [ old_interval, old_value ]
57
+ *
58
+ * Arms (starts) or disarms (stops) the timer referred by the TimerFD object
59
+ * and returns the old value of the timer.
60
+ *
61
+ * +flags+ is either zero (or nil) to start a relative timer or :ABSTIME
62
+ * to start an absolute timer. If the +interval+ is zero, the timer fires
63
+ * only once, otherwise the timer is fired every +interval+ seconds.
64
+ * +value+ is the time of the initial expiration in seconds.
65
+ */
38
66
  static VALUE settime(VALUE self, VALUE fl, VALUE interval, VALUE value)
39
67
  {
40
- int fd = my_fileno(self);
41
- int flags = NUM2INT(fl);
68
+ int fd = rb_sp_fileno(self);
69
+ int flags = rb_sp_get_flags(self, fl);
42
70
  struct itimerspec old, new;
43
71
 
44
72
  value2timespec(&new.it_interval, interval);
@@ -50,9 +78,15 @@ static VALUE settime(VALUE self, VALUE fl, VALUE interval, VALUE value)
50
78
  return itimerspec2ary(&old);
51
79
  }
52
80
 
81
+ /*
82
+ * call-seq:
83
+ * tfd#gettime -> [ interval, value ]
84
+ *
85
+ * Returns the current +interval+ and +value+ of the timer as an Array.
86
+ */
53
87
  static VALUE gettime(VALUE self)
54
88
  {
55
- int fd = my_fileno(self);
89
+ int fd = rb_sp_fileno(self);
56
90
  struct itimerspec curr;
57
91
 
58
92
  if (timerfd_gettime(fd, &curr) == -1)
@@ -61,7 +95,6 @@ static VALUE gettime(VALUE self)
61
95
  return itimerspec2ary(&curr);
62
96
  }
63
97
 
64
- #ifdef HAVE_RB_THREAD_BLOCKING_REGION
65
98
  static VALUE tfd_read(void *args)
66
99
  {
67
100
  uint64_t *buf = args;
@@ -71,29 +104,31 @@ static VALUE tfd_read(void *args)
71
104
  return (VALUE)r;
72
105
  }
73
106
 
74
- static VALUE expirations(VALUE self)
107
+ /*
108
+ * call-seq:
109
+ * tfd.expirations([nonblock]) -> Integer
110
+ *
111
+ * Returns the number of expirations that have occurred. This will block
112
+ * if no expirations have occurred at the time of the call. Returns +nil+
113
+ * if +nonblock+ is passed and is +true+
114
+ */
115
+ static VALUE expirations(int argc, VALUE *argv, VALUE self)
75
116
  {
76
117
  ssize_t r;
77
- uint64_t buf = (int)my_fileno(self);
78
-
79
- r = (VALUE)rb_thread_blocking_region(tfd_read, &buf, RUBY_UBF_IO, 0);
80
- if (r == -1)
81
- rb_sys_fail("read(timerfd)");
82
-
83
- return ULL2NUM(buf);
84
- }
85
- #else /* ! HAVE_RB_THREAD_BLOCKING_REGION */
86
- #include "nonblock.h"
87
- static VALUE expirations(VALUE self)
88
- {
89
- int fd = my_fileno(self);
90
- uint64_t buf;
91
- ssize_t r;
92
-
93
- set_nonblock(fd);
118
+ int fd = rb_sp_fileno(self);
119
+ uint64_t buf = (uint64_t)fd;
120
+ VALUE nonblock;
121
+
122
+ rb_scan_args(argc, argv, "01", &nonblock);
123
+ if (RTEST(nonblock))
124
+ rb_sp_set_nonblock(fd);
125
+ else
126
+ blocking_io_prepare(fd);
94
127
  retry:
95
- r = read(fd, &buf, sizeof(uint64_t));
128
+ r = (ssize_t)rb_sp_io_region(tfd_read, &buf);
96
129
  if (r == -1) {
130
+ if (errno == EAGAIN && RTEST(nonblock))
131
+ return Qnil;
97
132
  if (rb_io_wait_readable(fd))
98
133
  goto retry;
99
134
  rb_sys_fail("read(timerfd)");
@@ -101,28 +136,35 @@ retry:
101
136
 
102
137
  return ULL2NUM(buf);
103
138
  }
104
- #endif
105
139
 
106
140
  void sleepy_penguin_init_timerfd(void)
107
141
  {
108
142
  VALUE mSleepyPenguin, cTimerFD;
109
143
 
110
144
  mSleepyPenguin = rb_define_module("SleepyPenguin");
145
+
146
+ /*
147
+ * Document-class: SleepyPenguin::TimerFD
148
+ *
149
+ * TimerFD exposes kernel timers as IO objects that may be monitored
150
+ * by IO.select or Epoll. IO#close disarms the timers and returns
151
+ * resources back to the kernel.
152
+ */
111
153
  cTimerFD = rb_define_class_under(mSleepyPenguin, "TimerFD", rb_cIO);
112
- rb_define_singleton_method(cTimerFD, "create", create, -1);
113
- rb_define_singleton_method(cTimerFD, "new", create, -1);
114
- rb_define_const(cTimerFD, "REALTIME", UINT2NUM(CLOCK_REALTIME));
115
- rb_define_const(cTimerFD, "MONOTONIC", UINT2NUM(CLOCK_MONOTONIC));
116
- rb_define_const(cTimerFD, "ABSTIME", UINT2NUM(TFD_TIMER_ABSTIME));
154
+ rb_define_singleton_method(cTimerFD, "new", s_new, -1);
155
+ NODOC_CONST(cTimerFD, "REALTIME", UINT2NUM(CLOCK_REALTIME));
156
+ NODOC_CONST(cTimerFD, "MONOTONIC", UINT2NUM(CLOCK_MONOTONIC));
157
+ NODOC_CONST(cTimerFD, "ABSTIME", UINT2NUM(TFD_TIMER_ABSTIME));
117
158
  #ifdef TFD_NONBLOCK
118
- rb_define_const(cTimerFD, "NONBLOCK", UINT2NUM(TFD_NONBLOCK));
159
+ NODOC_CONST(cTimerFD, "NONBLOCK", UINT2NUM(TFD_NONBLOCK));
119
160
  #endif
120
161
  #ifdef TFD_CLOEXEC
121
- rb_define_const(cTimerFD, "CLOEXEC", UINT2NUM(TFD_CLOEXEC));
162
+ NODOC_CONST(cTimerFD, "CLOEXEC", UINT2NUM(TFD_CLOEXEC));
122
163
  #endif
123
164
 
124
165
  rb_define_method(cTimerFD, "settime", settime, 3);
125
- rb_define_method(cTimerFD, "expirations", expirations, 0);
166
+ rb_define_method(cTimerFD, "gettime", gettime, 0);
167
+ rb_define_method(cTimerFD, "expirations", expirations, -1);
126
168
  id_for_fd = rb_intern("for_fd");
127
169
  }
128
170
  #endif /* HAVE_SYS_TIMERFD_H */
@@ -0,0 +1,149 @@
1
+ #include "sleepy_penguin.h"
2
+
3
+ static VALUE klass_for(VALUE klass)
4
+ {
5
+ return (TYPE(klass) == T_CLASS) ? klass : CLASS_OF(klass);
6
+ }
7
+
8
+ int rb_sp_get_flags(VALUE klass, VALUE flags)
9
+ {
10
+ switch (TYPE(flags)) {
11
+ case T_NIL: return 0;
12
+ case T_FIXNUM: return FIX2INT(flags);
13
+ case T_BIGNUM: return NUM2INT(flags);
14
+ case T_SYMBOL:
15
+ return NUM2INT(rb_const_get(klass_for(klass), SYM2ID(flags)));
16
+ case T_ARRAY: {
17
+ VALUE *ptr = RARRAY_PTR(flags);
18
+ long len = RARRAY_LEN(flags);
19
+ int rv = 0;
20
+
21
+ klass = klass_for(klass);
22
+ while (--len >= 0) {
23
+ VALUE tmp = *ptr++;
24
+
25
+ Check_Type(tmp, T_SYMBOL);
26
+ tmp = rb_const_get(klass, SYM2ID(tmp));
27
+ rv |= NUM2INT(tmp);
28
+ }
29
+ return rv;
30
+ }
31
+ }
32
+ rb_raise(rb_eTypeError, "invalid flags");
33
+ return 0;
34
+ }
35
+
36
+ unsigned rb_sp_get_uflags(VALUE klass, VALUE flags)
37
+ {
38
+ switch (TYPE(flags)) {
39
+ case T_NIL: return 0;
40
+ case T_FIXNUM: return FIX2UINT(flags);
41
+ case T_BIGNUM: return NUM2UINT(flags);
42
+ case T_SYMBOL:
43
+ return NUM2UINT(rb_const_get(klass_for(klass), SYM2ID(flags)));
44
+ case T_ARRAY: {
45
+ VALUE *ptr = RARRAY_PTR(flags);
46
+ long len = RARRAY_LEN(flags);
47
+ unsigned rv = 0;
48
+
49
+ klass = klass_for(klass);
50
+ while (--len >= 0) {
51
+ VALUE tmp = *ptr++;
52
+
53
+ Check_Type(tmp, T_SYMBOL);
54
+ tmp = rb_const_get(klass, SYM2ID(tmp));
55
+ rv |= NUM2UINT(tmp);
56
+ }
57
+ return rv;
58
+ }
59
+ }
60
+ rb_raise(rb_eTypeError, "invalid flags");
61
+ return 0;
62
+ }
63
+
64
+ #if ! HAVE_RB_IO_T
65
+ # define rb_io_t OpenFile
66
+ #endif
67
+
68
+ #ifdef GetReadFile
69
+ # define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
70
+ #else
71
+ # if !HAVE_RB_IO_T || (RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8)
72
+ # define FPTR_TO_FD(fptr) fileno(fptr->f)
73
+ # else
74
+ # define FPTR_TO_FD(fptr) fptr->fd
75
+ # endif
76
+ #endif
77
+
78
+ static int fixint_closed_p(VALUE io)
79
+ {
80
+ return (fcntl(FIX2INT(io), F_GETFD) == -1 && errno == EBADF);
81
+ }
82
+
83
+ #if defined(RFILE) && defined(HAVE_ST_FD)
84
+ static int my_rb_io_closed(VALUE io)
85
+ {
86
+ return RFILE(io)->fptr->fd < 0;
87
+ }
88
+ #else
89
+ static int my_rb_io_closed(VALUE io)
90
+ {
91
+ return rb_funcall(io, rb_intern("closed?"), 0) == Qtrue;
92
+ }
93
+ #endif
94
+
95
+ int rb_sp_io_closed(VALUE io)
96
+ {
97
+ switch (TYPE(io)) {
98
+ case T_FIXNUM:
99
+ return fixint_closed_p(io);
100
+ case T_FILE:
101
+ break;
102
+ default:
103
+ io = rb_convert_type(io, T_FILE, "IO", "to_io");
104
+ }
105
+
106
+ return my_rb_io_closed(io);
107
+ }
108
+
109
+ int rb_sp_fileno(VALUE io)
110
+ {
111
+ rb_io_t *fptr;
112
+
113
+ switch (TYPE(io)) {
114
+ case T_FIXNUM: return FIX2INT(io);
115
+ case T_FILE:
116
+ GetOpenFile(io, fptr);
117
+ return FPTR_TO_FD(fptr);
118
+ }
119
+ io = rb_convert_type(io, T_FILE, "IO", "to_io");
120
+ GetOpenFile(io, fptr);
121
+ return FPTR_TO_FD(fptr);
122
+ }
123
+
124
+ void rb_sp_set_nonblock(int fd)
125
+ {
126
+ int flags = fcntl(fd, F_GETFL);
127
+
128
+ if (flags == -1)
129
+ rb_sys_fail("fcntl(F_GETFL)");
130
+ if ((flags & O_NONBLOCK) == O_NONBLOCK)
131
+ return;
132
+ flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
133
+ if (flags == -1)
134
+ rb_sys_fail("fcntl(F_SETFL)");
135
+ }
136
+
137
+ #ifndef HAVE_RB_THREAD_BLOCKING_REGION
138
+ #include <rubysig.h>
139
+ VALUE rb_sp_io_region(rb_blocking_function_t *func, void *data)
140
+ {
141
+ VALUE rv;
142
+
143
+ TRAP_BEG;
144
+ rv = func(data);
145
+ TRAP_END;
146
+
147
+ return rv;
148
+ }
149
+ #endif
@@ -1,7 +1 @@
1
- # -*- encoding: binary -*-
2
- module SleepyPenguin
3
-
4
- # the version of sleepy_penguin, currently 1.4.0
5
- SLEEPY_PENGUIN_VERSION = '1.4.0'
6
- end
7
1
  require 'sleepy_penguin_ext'
@@ -0,0 +1,20 @@
1
+ # :stopdoc:
2
+ class SleepyPenguin::SignalFD::SigInfo
3
+
4
+ def to_hash
5
+ Hash[*MEMBERS.inject([]) { |ary,k| ary << k << __send__(k) }]
6
+ end
7
+
8
+ def hash
9
+ to_hash.hash
10
+ end
11
+
12
+ def inspect
13
+ "#<#{self.class}:#{to_hash.inspect}>"
14
+ end
15
+
16
+ def ==(other)
17
+ other.kind_of?(self.class) && to_hash == other.to_hash
18
+ end
19
+ end
20
+ # :startdoc:
@@ -0,0 +1,4 @@
1
+ # :stopdoc:
2
+ require "sleepy_penguin"
3
+ SP = SleepyPenguin
4
+ # :startdoc:
data/pkg.mk CHANGED
@@ -46,22 +46,23 @@ lib := $(lib):$(ext_pfx)/$(ext)
46
46
  build: $(ext_dl)
47
47
  endif
48
48
 
49
- pkg_extra := GIT-VERSION-FILE NEWS ChangeLog LATEST
49
+ pkg_extra += GIT-VERSION-FILE NEWS ChangeLog LATEST
50
50
  ChangeLog: GIT-VERSION-FILE .wrongdoc.yml
51
51
  $(WRONGDOC) prepare
52
+ NEWS LATEST: ChangeLog
52
53
 
53
54
  manifest:
54
55
  $(RM) .manifest
55
56
  $(MAKE) .manifest
56
57
 
57
- .manifest: ChangeLog
58
+ .manifest: $(pkg_extra)
58
59
  (git ls-files && for i in $@ $(pkg_extra); do echo $$i; done) | \
59
60
  LC_ALL=C sort > $@+
60
61
  cmp $@+ $@ || mv $@+ $@
61
62
  $(RM) $@+
62
63
 
63
- doc:: .document .wrongdoc.yml
64
- find lib -type f -name '*.rbc' -exec rm -f '{}' ';'
64
+ doc:: .document .wrongdoc.yml $(pkg_extra)
65
+ -find lib -type f -name '*.rbc' -exec rm -f '{}' ';'
65
66
  -find ext -type f -name '*.rbc' -exec rm -f '{}' ';'
66
67
  $(RM) -r doc
67
68
  $(WRONGDOC) all
data/test/test_epoll.rb CHANGED
@@ -14,6 +14,14 @@ class TestEpoll < Test::Unit::TestCase
14
14
  @ep = Epoll.new
15
15
  end
16
16
 
17
+ def test_constants
18
+ Epoll.constants.each do |const|
19
+ next if const.to_sym == :IO
20
+ nr = Epoll.const_get(const)
21
+ assert nr <= 0xffffffff, "#{const}=#{nr}"
22
+ end
23
+ end
24
+
17
25
  def test_cross_thread
18
26
  tmp = []
19
27
  Thread.new { sleep 0.100; @ep.add(@wr, Epoll::OUT) }
@@ -347,6 +355,27 @@ class TestEpoll < Test::Unit::TestCase
347
355
  assert_nil @ep.flags_for(@rd)
348
356
  end
349
357
 
358
+ def test_flags_for_sym
359
+ @ep.add @rd, :IN
360
+ assert_equal Epoll::IN, @ep.flags_for(@rd.fileno)
361
+ assert_equal Epoll::IN, @ep.flags_for(@rd)
362
+
363
+ @ep.del @rd
364
+ assert_nil @ep.flags_for(@rd.fileno)
365
+ assert_nil @ep.flags_for(@rd)
366
+ end
367
+
368
+ def test_flags_for_sym_ary
369
+ @ep.add @rd, [:IN, :ET]
370
+ expect = Epoll::IN | Epoll::ET
371
+ assert_equal expect, @ep.flags_for(@rd.fileno)
372
+ assert_equal expect, @ep.flags_for(@rd)
373
+
374
+ @ep.del @rd
375
+ assert_nil @ep.flags_for(@rd.fileno)
376
+ assert_nil @ep.flags_for(@rd)
377
+ end
378
+
350
379
  def test_include?
351
380
  assert ! @ep.include?(@rd)
352
381
  @ep.add @rd, Epoll::IN
@@ -44,4 +44,4 @@ class TestEpollGC < Test::Unit::TestCase
44
44
  end
45
45
  end while true
46
46
  end
47
- end
47
+ end if ENV["GC_STRESS"].to_i != 0
@@ -1,5 +1,8 @@
1
1
  require 'test/unit'
2
- require 'strace'
2
+ begin
3
+ require 'strace'
4
+ rescue LoadError
5
+ end
3
6
  $-w = true
4
7
 
5
8
  require 'sleepy_penguin'
@@ -148,4 +151,4 @@ class TestEpollOptimizations < Test::Unit::TestCase
148
151
  assert_equal 1, lines.grep(/^epoll_ctl/).size
149
152
  assert_equal 1, lines.grep(/EBADF/).size
150
153
  end
151
- end
154
+ end if defined?(Strace)