sleepy_penguin 1.4.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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)