sleepy_penguin 1.3.1 → 1.4.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.
data/.manifest CHANGED
@@ -17,6 +17,7 @@ ext/sleepy_penguin/epoll.c
17
17
  ext/sleepy_penguin/eventfd.c
18
18
  ext/sleepy_penguin/extconf.rb
19
19
  ext/sleepy_penguin/init.c
20
+ ext/sleepy_penguin/inotify.c
20
21
  ext/sleepy_penguin/nonblock.h
21
22
  ext/sleepy_penguin/sleepy_penguin.h
22
23
  ext/sleepy_penguin/timerfd.c
@@ -30,4 +31,5 @@ test/test_epoll.rb
30
31
  test/test_epoll_gc.rb
31
32
  test/test_epoll_optimizations.rb
32
33
  test/test_eventfd.rb
34
+ test/test_inotify.rb
33
35
  test/test_timerfd.rb
data/ChangeLog CHANGED
@@ -1,5 +1,124 @@
1
1
  ChangeLog from http://bogomips.org/sleepy_penguin.git
2
2
 
3
+ commit bba86c017d101c365c5a22b8e2b734ff7216ce56
4
+ Author: Eric Wong <normalperson@yhbt.net>
5
+ Date: Fri Feb 4 22:24:59 2011 +0000
6
+
7
+ sleepy_penguin 1.4.0 - Linux I/O events for Ruby
8
+
9
+ * Epoll#wait: do not automatically retry on EINTR
10
+ * preliminary Inotify support
11
+ * Epoll.new no longer defaults to close-on-exec
12
+
13
+ TODO: FANotify + SignalFD
14
+
15
+ commit 4465548dd80cf779af53bbfee133f0be5f8f2791
16
+ Author: Eric Wong <normalperson@yhbt.net>
17
+ Date: Fri Feb 4 22:23:12 2011 +0000
18
+
19
+ TODO: update with fanotify item
20
+
21
+ The fscking all notification system!
22
+
23
+ commit 6b54faf7d855b7d9eb45bf437b022e2774f39826
24
+ Author: Eric Wong <normalperson@yhbt.net>
25
+ Date: Fri Feb 4 22:19:23 2011 +0000
26
+
27
+ bump wrongdoc dependency
28
+
29
+ wrongdoc 1.5 is nicer
30
+
31
+ commit 67fa5292823f4c38a35dd83c4948441ce8438d40
32
+ Author: Eric Wong <normalperson@yhbt.net>
33
+ Date: Fri Feb 4 22:16:07 2011 +0000
34
+
35
+ safety fix for Inotify#dup
36
+
37
+ We do not want to share buffers between inotify descriptors.
38
+
39
+ commit 92a666f3ff27539655e9130db16bd1587db061de
40
+ Author: Eric Wong <normalperson@yhbt.net>
41
+ Date: Fri Feb 4 22:02:25 2011 +0000
42
+
43
+ Epoll.new does not default to close-on-exec
44
+
45
+ No need to differ from other file descriptors, even though
46
+ it would be better if *all* file descriptors would default to
47
+ O_CLOEXEC.
48
+
49
+ commit 6540be80d8dca8cf93e74273d04211c1a73a674e
50
+ Author: Eric Wong <normalperson@yhbt.net>
51
+ Date: Fri Feb 4 22:00:29 2011 +0000
52
+
53
+ require rb_memerror and rb_io_close
54
+
55
+ The latest Rubinius supports both of these as do all MRI
56
+
57
+ commit 9f51099a6651d314d1ff5a435b6bc349d3472486
58
+ Author: Eric Wong <normalperson@yhbt.net>
59
+ Date: Fri Feb 4 21:57:45 2011 +0000
60
+
61
+ epoll.c: safer replacement function naming
62
+
63
+ This can avoid confusion/conflicts in case we link with newer
64
+ versions that have the missing symbols.
65
+
66
+ commit 2cc41c38dc270ff764a5cc58fd81fbdacde9ff4b
67
+ Author: Eric Wong <e@yhbt.net>
68
+ Date: Fri Feb 4 10:58:40 2011 +0000
69
+
70
+ preliminary inotify support
71
+
72
+ It seems to basically work...
73
+
74
+ commit 7812324579b430fa604905410b506e9e88fdda55
75
+ Author: Eric Wong <e@yhbt.net>
76
+ Date: Fri Feb 4 06:24:20 2011 +0000
77
+
78
+ quiet build warnings for older systems
79
+
80
+ Harmless, but still
81
+
82
+ commit 7fbb141402974a91ff925e3f303b1bc2c698b0d4
83
+ Author: Eric Wong <e@yhbt.net>
84
+ Date: Fri Feb 4 06:21:07 2011 +0000
85
+
86
+ epoll.c: fix uninitialized fd variable
87
+
88
+ Oops :x This changes/fixes some minor misbehavior.
89
+
90
+ commit c02141cbfa965a19b236e4f9a09856bd2fcacca0
91
+ Author: Eric Wong <e@yhbt.net>
92
+ Date: Fri Feb 4 06:00:20 2011 +0000
93
+
94
+ Epoll#wait: do not automatically retry on EINTR
95
+
96
+ It's often used to drive event loops and EINTR should be
97
+ retried by the user (like IO.select and not like
98
+ IO#read_nonblock).
99
+
100
+ commit 9dda070b66753e04a179e0cd36d1eff29195a7cb
101
+ Author: Eric Wong <normalperson@yhbt.net>
102
+ Date: Wed Feb 2 18:29:09 2011 -0800
103
+
104
+ pkg.mk: use RbConfig instead of Config
105
+
106
+ Config is deprecated
107
+
108
+ commit fecdb9a57523cd2d38fc76fa8609388712aa075e
109
+ Author: Eric Wong <e@yhbt.net>
110
+ Date: Thu Jan 27 21:45:06 2011 +0000
111
+
112
+ pkg.mk: allow WRONGDOC variable to be overriden
113
+
114
+ This makes life easier for the wrongdoc package itself
115
+
116
+ commit 01e7012f27109295f7cc158f561091b07b831762
117
+ Author: Eric Wong <e@yhbt.net>
118
+ Date: Thu Jan 27 06:18:35 2011 +0000
119
+
120
+ pkg.mk: update in case we get rid of C ext
121
+
3
122
  commit 2c3c2b788c89b1ede5445bc930dd9dbb9a658c50
4
123
  Author: Eric Wong <normalperson@yhbt.net>
5
124
  Date: Fri Jan 21 16:51:23 2011 -0800
data/GIT-VERSION-FILE CHANGED
@@ -1 +1 @@
1
- GIT_VERSION = 1.3.1
1
+ GIT_VERSION = 1.4.0
data/GIT-VERSION-GEN CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v1.3.1.GIT
4
+ DEF_VER=v1.4.0.GIT
5
5
 
6
6
  LF='
7
7
  '
data/LATEST CHANGED
@@ -1,7 +1,8 @@
1
- === sleepy_penguin 1.3.1 - safety checks for delete / 2011-01-22 00:52 UTC
1
+ === sleepy_penguin 1.4.0 - Linux I/O events for Ruby / 2011-02-04 22:27 UTC
2
2
 
3
- One bugfix for Epoll#delete:
3
+ * Epoll#wait: do not automatically retry on EINTR
4
+ * preliminary Inotify support
5
+ * Epoll.new no longer defaults to close-on-exec
4
6
 
5
- Eric Wong (1):
6
- safer closed checks for proxied objects
7
+ TODO: FANotify + SignalFD
7
8
 
data/NEWS CHANGED
@@ -1,3 +1,11 @@
1
+ === sleepy_penguin 1.4.0 - Linux I/O events for Ruby / 2011-02-04 22:27 UTC
2
+
3
+ * Epoll#wait: do not automatically retry on EINTR
4
+ * preliminary Inotify support
5
+ * Epoll.new no longer defaults to close-on-exec
6
+
7
+ TODO: FANotify + SignalFD
8
+
1
9
  === sleepy_penguin 1.3.1 - safety checks for delete / 2011-01-22 00:52 UTC
2
10
 
3
11
  One bugfix for Epoll#delete:
data/TODO CHANGED
@@ -1,3 +1,3 @@
1
- * SignalFD interface
1
+ * FANotify interface
2
2
 
3
- * Inotify? Several libraries already support it, but it fits this project, too
3
+ * SignalFD interface
@@ -13,21 +13,6 @@
13
13
 
14
14
  #define EP_RECREATE (-2)
15
15
 
16
- #ifndef HAVE_RB_MEMERROR
17
- static void rb_memerror(void)
18
- {
19
- static const char e[] = "[FATAL] failed to allocate memory\n";
20
- write(2, e, sizeof(e) - 1);
21
- abort();
22
- }
23
- #endif
24
- #ifndef HAVE_RB_IO_CLOSE
25
- static VALUE rb_io_close(VALUE io)
26
- {
27
- return rb_funcall(io, rb_intern("close"), 0);
28
- }
29
- #endif
30
-
31
16
  static st_table *active;
32
17
  static const int step = 64; /* unlikely to grow unless you're huge */
33
18
  static VALUE cEpoll_IO;
@@ -70,7 +55,7 @@ static struct rb_epoll *ep_get(VALUE self)
70
55
  * Don't worry about thread-safety since current Ruby 1.9 won't
71
56
  * call this without GVL.
72
57
  */
73
- static int epoll_create1(int flags)
58
+ static int my_epoll_create1(int flags)
74
59
  {
75
60
  int fd = epoll_create(1024); /* size ignored since 2.6.8 */
76
61
 
@@ -88,6 +73,7 @@ err:
88
73
  return -1;
89
74
  }
90
75
  }
76
+ #define epoll_create1 my_epoll_create1
91
77
  #endif
92
78
 
93
79
  static void gcmark(void *ptr)
@@ -129,7 +115,7 @@ static VALUE alloc(VALUE klass)
129
115
  ep->marks = Qnil;
130
116
  ep->flag_cache = Qnil;
131
117
  ep->capa = step;
132
- ep->flags = EPOLL_CLOEXEC;
118
+ ep->flags = 0;
133
119
  ep->events = xmalloc(sizeof(struct epoll_event) * ep->capa);
134
120
 
135
121
  return self;
@@ -176,7 +162,7 @@ static VALUE init(int argc, VALUE *argv, VALUE self)
176
162
 
177
163
  rb_scan_args(argc, argv, "01", &fl);
178
164
  if (NIL_P(fl)) {
179
- flags = EPOLL_CLOEXEC;
165
+ flags = 0;
180
166
  } else {
181
167
  switch (TYPE(fl)) {
182
168
  case T_FIXNUM:
@@ -289,7 +275,7 @@ fallback_add:
289
275
  static VALUE delete(VALUE self, VALUE io)
290
276
  {
291
277
  struct rb_epoll *ep = ep_get(self);
292
- int fd;
278
+ int fd = my_fileno(io);
293
279
  int rv;
294
280
  VALUE cur_io;
295
281
 
@@ -297,7 +283,6 @@ static VALUE delete(VALUE self, VALUE io)
297
283
  if (my_io_closed(io))
298
284
  goto out;
299
285
 
300
- fd = my_fileno(io);
301
286
  cur_io = rb_ary_entry(ep->marks, fd);
302
287
  if (NIL_P(cur_io) || my_io_closed(cur_io))
303
288
  return Qnil;
@@ -355,12 +340,7 @@ static VALUE nogvl_wait(void *args)
355
340
 
356
341
  static VALUE real_epwait(struct rb_epoll *ep)
357
342
  {
358
- int n;
359
-
360
- do {
361
- n = (int)rb_thread_blocking_region(nogvl_wait, ep,
362
- RUBY_UBF_IO, 0);
363
- } while (n == -1 && errno == EINTR);
343
+ int n = (int)rb_thread_blocking_region(nogvl_wait, ep, RUBY_UBF_IO, 0);
364
344
 
365
345
  return epwait_result(ep, n);
366
346
  }
@@ -403,7 +383,7 @@ static int epwait_forever(struct rb_epoll *ep)
403
383
  do {
404
384
  (void)rb_io_wait_readable(ep->fd);
405
385
  n = safe_epoll_wait(ep);
406
- } while (n == 0 || (n == -1 && errno == EINTR));
386
+ } while (n == 0);
407
387
 
408
388
  return n;
409
389
  }
@@ -426,13 +406,7 @@ static int epwait_timed(struct rb_epoll *ep)
426
406
  gettimeofday(&t0, NULL);
427
407
  (void)rb_thread_select(ep->fd + 1, &rfds, NULL, NULL, &tv);
428
408
  n = safe_epoll_wait(ep);
429
-
430
- /*
431
- * if we got EINTR from epoll_wait /and/ timed out
432
- * just consider it a timeout and don't raise an error
433
- */
434
-
435
- if (n > 0 || (n == -1 && errno != EINTR))
409
+ if (n != 0)
436
410
  return n;
437
411
 
438
412
  /* XXX use CLOCK_MONOTONIC if people care about 1.8... */
@@ -4,9 +4,10 @@ have_header("pthread.h") or abort 'pthread.h not found'
4
4
  have_header('sys/eventfd.h')
5
5
  have_header('sys/signalfd.h')
6
6
  have_header('sys/timerfd.h')
7
+ have_header('sys/inotify.h')
7
8
  have_header('ruby/io.h') and have_struct_member('rb_io_t', 'fd', 'ruby/io.h')
8
- have_func('rb_memerror')
9
- have_func('rb_io_close')
9
+ have_func('rb_memerror') or abort 'need rb_memerror()'
10
+ have_func('rb_io_close') or abort 'need rb_io_close()'
10
11
  have_func('epoll_create1', %w(sys/epoll.h))
11
12
  have_func('rb_thread_blocking_region')
12
13
  have_library('pthread')
@@ -3,13 +3,19 @@ void sleepy_penguin_init_epoll(void);
3
3
  #ifdef HAVE_SYS_TIMERFD_H
4
4
  void sleepy_penguin_init_timerfd(void);
5
5
  #else
6
- # define sleepy_penguin_init_timerfd() if(0)
6
+ # define sleepy_penguin_init_timerfd() for(;0;)
7
7
  #endif
8
8
 
9
9
  #ifdef HAVE_SYS_EVENTFD_H
10
10
  void sleepy_penguin_init_eventfd(void);
11
11
  #else
12
- # define sleepy_penguin_init_eventfd() if(0)
12
+ # define sleepy_penguin_init_eventfd() for(;0;)
13
+ #endif
14
+
15
+ #ifdef HAVE_SYS_INOTIFY_H
16
+ void sleepy_penguin_init_inotify(void);
17
+ #else
18
+ # define sleepy_penguin_init_inotify() for(;0;)
13
19
  #endif
14
20
 
15
21
  void Init_sleepy_penguin_ext(void)
@@ -17,4 +23,5 @@ void Init_sleepy_penguin_ext(void)
17
23
  sleepy_penguin_init_epoll();
18
24
  sleepy_penguin_init_timerfd();
19
25
  sleepy_penguin_init_eventfd();
26
+ sleepy_penguin_init_inotify();
20
27
  }
@@ -0,0 +1,293 @@
1
+ #ifdef HAVE_SYS_INOTIFY_H
2
+ #include "sleepy_penguin.h"
3
+ #include "nonblock.h"
4
+ #include <sys/inotify.h>
5
+ #include <sys/ioctl.h>
6
+ static ID id_for_fd, id_inotify_buf, id_inotify_tmp, id_mask;
7
+ static VALUE cEvent, checks;
8
+
9
+ #ifndef IN_CLOEXEC
10
+ # define IN_CLOEXEC 02000000
11
+ #endif
12
+ #ifndef IN_NONBLOCK
13
+ # define IN_NONBLOCK O_NONBLOCK
14
+ #endif
15
+ #ifndef IN_ATTRIB
16
+ # define IN_ATTRIB 0x00000004
17
+ #endif
18
+ #ifndef IN_ONLYDIR
19
+ # define IN_ONLYDIR 0x01000000
20
+ #endif
21
+ #ifndef IN_DONT_FOLLOW
22
+ # define IN_DONT_FOLLOW 0x02000000
23
+ #endif
24
+ #ifndef IN_EXCL_UNLINK
25
+ # define IN_EXCL_UNLINK 0x04000000
26
+ #endif
27
+ #ifndef IN_MASK_ADD
28
+ # define IN_MASK_ADD 0x20000000
29
+ #endif
30
+ #ifndef IN_ONESHOT
31
+ # define IN_ONESHOT 0x80000000
32
+ #endif
33
+
34
+ #ifndef HAVE_INOTIFY_INIT1
35
+ /*
36
+ * fake inotify_init1() since some systems don't have it
37
+ * Don't worry about thread-safety since current Ruby 1.9 won't
38
+ * call this without GVL.
39
+ */
40
+ static int my_inotify_init1(int flags)
41
+ {
42
+ int fd = inotify_init();
43
+ int tmp;
44
+
45
+ if (fd < 0)
46
+ return fd;
47
+ if ((flags & IN_CLOEXEC) && (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1))
48
+ goto fcntl_err;
49
+ if (flags & IN_NONBLOCK) {
50
+ tmp = fcntl(fd, F_GETFL);
51
+ if (tmp == -1)
52
+ goto fcntl_err;
53
+ if ((fcntl(fd, F_SETFL, tmp | O_NONBLOCK) != 0))
54
+ goto fcntl_err;
55
+ }
56
+
57
+ return fd;
58
+ fcntl_err:
59
+ tmp = errno;
60
+ close(fd);
61
+ errno = tmp;
62
+ rb_sys_fail("fcntl");
63
+ }
64
+ # define inotify_init1 my_inotify_init1
65
+ #endif /* HAVE_INOTIFY_INIT1 */
66
+
67
+ static VALUE s_init(int argc, VALUE *argv, VALUE klass)
68
+ {
69
+ VALUE _flags, rv;
70
+ int flags;
71
+ int fd;
72
+
73
+ rb_scan_args(argc, argv, "01", &_flags);
74
+ flags = NIL_P(_flags) ? 0 : NUM2INT(_flags);
75
+
76
+ fd = inotify_init1(flags);
77
+ if (fd == -1) {
78
+ if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) {
79
+ rb_gc();
80
+ fd = inotify_init1(flags);
81
+ }
82
+ if (fd == -1)
83
+ rb_sys_fail("inotify_init1");
84
+ }
85
+
86
+ rv = rb_funcall(klass, id_for_fd, 1, INT2NUM(fd));
87
+ rb_ivar_set(rv, id_inotify_buf, rb_str_new(0, 128));
88
+ rb_ivar_set(rv, id_inotify_tmp, rb_ary_new());
89
+
90
+ return rv;
91
+ }
92
+
93
+ static VALUE add_watch(VALUE self, VALUE path, VALUE vmask)
94
+ {
95
+ int fd = my_fileno(self);
96
+ const char *pathname = StringValuePtr(path);
97
+ uint32_t mask = NUM2UINT(vmask);
98
+ int rc = inotify_add_watch(fd, pathname, mask);
99
+
100
+ if (rc == -1) {
101
+ if (errno == ENOMEM) {
102
+ rb_gc();
103
+ rc = inotify_add_watch(fd, pathname, mask);
104
+ }
105
+ if (rc == -1)
106
+ rb_sys_fail("inotify_add_watch");
107
+ }
108
+ return UINT2NUM((uint32_t)rc);
109
+ }
110
+
111
+ static VALUE rm_watch(VALUE self, VALUE vwd)
112
+ {
113
+ uint32_t wd = NUM2UINT(vwd);
114
+ int fd = my_fileno(self);
115
+ int rc = inotify_rm_watch(fd, wd);
116
+
117
+ if (rc == -1)
118
+ rb_sys_fail("inotify_rm_watch");
119
+ return INT2NUM(rc);
120
+ }
121
+
122
+ static size_t event_len(struct inotify_event *e)
123
+ {
124
+ return sizeof(struct inotify_event) + e->len;
125
+ }
126
+
127
+ static VALUE event_new(struct inotify_event *e)
128
+ {
129
+ VALUE wd = INT2NUM(e->wd);
130
+ VALUE mask = UINT2NUM(e->mask);
131
+ VALUE cookie = UINT2NUM(e->cookie);
132
+ VALUE name;
133
+
134
+ /* name may be zero-padded, so we do strlen() */
135
+ name = e->len ? rb_str_new(e->name, strlen(e->name)) : Qnil;
136
+
137
+ return rb_struct_new(cEvent, wd, mask, cookie, name);
138
+ }
139
+
140
+ static VALUE take(int argc, VALUE *argv, VALUE self)
141
+ {
142
+ int fd = my_fileno(self);
143
+ VALUE buf = rb_ivar_get(self, id_inotify_buf);
144
+ VALUE tmp = rb_ivar_get(self, id_inotify_tmp);
145
+ struct inotify_event *ptr;
146
+ struct inotify_event *e, *end;
147
+ long len;
148
+ ssize_t r;
149
+ VALUE rv = Qnil;
150
+ VALUE nonblock;
151
+
152
+ if (RARRAY_LEN(tmp) > 0)
153
+ return rb_ary_shift(tmp);
154
+
155
+ rb_scan_args(argc, argv, "01", &nonblock);
156
+
157
+ len = RSTRING_LEN(buf);
158
+ ptr = (struct inotify_event *)RSTRING_PTR(buf);
159
+ do {
160
+ set_nonblock(fd);
161
+ r = read(fd, ptr, len);
162
+ if (r == 0 || (r < 0 && errno == EINVAL)) {
163
+ int newlen;
164
+ if (len > 0x10000)
165
+ rb_raise(rb_eRuntimeError, "path too long");
166
+ if (ioctl(fd, FIONREAD, &newlen) != 0)
167
+ rb_sys_fail("ioctl(inotify,FIONREAD)");
168
+ rb_str_resize(buf, newlen);
169
+ ptr = (struct inotify_event *)RSTRING_PTR(buf);
170
+ len = newlen;
171
+ } else if (r < 0) {
172
+ if (errno == EAGAIN) {
173
+ if (RTEST(nonblock))
174
+ return Qnil;
175
+ rb_io_wait_readable(fd);
176
+ } else {
177
+ rb_sys_fail("read(inotify)");
178
+ }
179
+ } else {
180
+ end = (struct inotify_event *)((char *)ptr + r);
181
+ for (e = ptr; e < end; ) {
182
+ VALUE event = event_new(e);
183
+ if (NIL_P(rv))
184
+ rv = event;
185
+ else
186
+ rb_ary_push(tmp, event);
187
+ e = (struct inotify_event *)
188
+ ((char *)e + event_len(e));
189
+ }
190
+ }
191
+ } while (NIL_P(rv));
192
+
193
+ return rv;
194
+ }
195
+
196
+ static VALUE events(VALUE self)
197
+ {
198
+ long len = RARRAY_LEN(checks);
199
+ VALUE *ptr = RARRAY_PTR(checks);
200
+ VALUE pair;
201
+ VALUE sym;
202
+ VALUE rv = rb_ary_new();
203
+ uint32_t mask;
204
+ uint32_t event_mask = NUM2UINT(rb_funcall(self, id_mask, 0));
205
+
206
+ for (; (len -= 2) >= 0;) {
207
+ sym = *ptr++;
208
+ mask = NUM2UINT(*ptr++);
209
+ if ((event_mask & mask) == mask)
210
+ rb_ary_push(rv, sym);
211
+ }
212
+
213
+ return rv;
214
+ }
215
+
216
+ static VALUE init_copy(VALUE dest, VALUE orig)
217
+ {
218
+ VALUE tmp;
219
+
220
+ dest = rb_call_super(1, &orig);
221
+ rb_ivar_set(dest, id_inotify_buf, rb_str_new(0, 128));
222
+
223
+ return dest;
224
+ }
225
+
226
+ void sleepy_penguin_init_inotify(void)
227
+ {
228
+ VALUE mSleepyPenguin, cInotify;
229
+
230
+ mSleepyPenguin = rb_define_module("SleepyPenguin");
231
+ cInotify = rb_define_class_under(mSleepyPenguin, "Inotify", rb_cIO);
232
+ rb_define_method(cInotify, "add_watch", add_watch, 2);
233
+ rb_define_method(cInotify, "rm_watch", rm_watch, 1);
234
+ rb_define_method(cInotify, "initialize_copy", init_copy, 1);
235
+ rb_define_method(cInotify, "take", take, -1);
236
+ cEvent = rb_struct_define(NULL, "wd", "mask", "cookie", "name", NULL);
237
+ rb_define_const(cInotify, "Event", cEvent);
238
+ rb_define_method(cEvent, "events", events, 0);
239
+ rb_define_singleton_method(cInotify, "new", s_init, -1);
240
+ id_for_fd = rb_intern("for_fd");
241
+ id_inotify_buf = rb_intern("@inotify_buf");
242
+ id_inotify_tmp = rb_intern("@inotify_tmp");
243
+ id_mask = rb_intern("mask");
244
+ checks = rb_ary_new();
245
+ rb_global_variable(&checks);
246
+ #define IN(x) rb_define_const(cInotify,#x,UINT2NUM(IN_##x))
247
+ #define IN2(x) do { \
248
+ VALUE val = UINT2NUM(IN_##x); \
249
+ rb_define_const(cInotify,#x,val); \
250
+ rb_ary_push(checks, ID2SYM(rb_intern(#x))); \
251
+ rb_ary_push(checks, val); \
252
+ } while (0)
253
+
254
+ rb_define_const(cInotify, "FIONREAD", INT2NUM(FIONREAD));
255
+
256
+ IN(ALL_EVENTS);
257
+
258
+ /* events a user can watch for */
259
+ IN2(ACCESS);
260
+ IN2(MODIFY);
261
+ IN2(ATTRIB);
262
+ IN2(CLOSE_WRITE);
263
+ IN2(CLOSE_NOWRITE);
264
+ IN2(OPEN);
265
+ IN2(MOVED_FROM);
266
+ IN2(MOVED_TO);
267
+ IN2(CREATE);
268
+ IN2(DELETE);
269
+ IN2(DELETE_SELF);
270
+ IN2(MOVE_SELF);
271
+
272
+ /* sent as needed to any watch */
273
+ IN2(UNMOUNT);
274
+ IN2(Q_OVERFLOW);
275
+ IN2(IGNORED);
276
+ IN2(ISDIR);
277
+
278
+ /* helpers */
279
+ IN(CLOSE);
280
+ IN(MOVE);
281
+
282
+ /* special flags */
283
+ IN(ONLYDIR);
284
+ IN(DONT_FOLLOW);
285
+ IN(EXCL_UNLINK);
286
+ IN(MASK_ADD);
287
+ IN(ONESHOT);
288
+
289
+ /* for inotify_init1() */
290
+ IN(NONBLOCK);
291
+ IN(CLOEXEC);
292
+ }
293
+ #endif /* HAVE_SYS_INOTIFY_H */
@@ -1,7 +1,7 @@
1
1
  # -*- encoding: binary -*-
2
2
  module SleepyPenguin
3
3
 
4
- # the version of sleepy_penguin, currently 1.3.1
5
- SLEEPY_PENGUIN_VERSION = '1.3.1'
4
+ # the version of sleepy_penguin, currently 1.4.0
5
+ SLEEPY_PENGUIN_VERSION = '1.4.0'
6
6
  end
7
7
  require 'sleepy_penguin_ext'
data/pkg.mk CHANGED
@@ -1,12 +1,13 @@
1
1
  RUBY = ruby
2
2
  RAKE = rake
3
3
  RSYNC = rsync
4
+ WRONGDOC = wrongdoc
4
5
 
5
6
  GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
6
7
  @./GIT-VERSION-GEN
7
8
  -include GIT-VERSION-FILE
8
9
  -include local.mk
9
- DLEXT := $(shell $(RUBY) -rrbconfig -e 'puts Config::CONFIG["DLEXT"]')
10
+ DLEXT := $(shell $(RUBY) -rrbconfig -e 'puts RbConfig::CONFIG["DLEXT"]')
10
11
  RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
11
12
  RUBY_ENGINE := $(shell $(RUBY) -e 'puts((RUBY_ENGINE rescue "ruby"))')
12
13
  lib := lib
@@ -47,7 +48,7 @@ endif
47
48
 
48
49
  pkg_extra := GIT-VERSION-FILE NEWS ChangeLog LATEST
49
50
  ChangeLog: GIT-VERSION-FILE .wrongdoc.yml
50
- wrongdoc prepare
51
+ $(WRONGDOC) prepare
51
52
 
52
53
  manifest:
53
54
  $(RM) .manifest
@@ -59,10 +60,11 @@ manifest:
59
60
  cmp $@+ $@ || mv $@+ $@
60
61
  $(RM) $@+
61
62
 
62
- doc: .document .wrongdoc.yml
63
- find lib ext -type f -name '*.rbc' -exec rm -f '{}' ';'
63
+ doc:: .document .wrongdoc.yml
64
+ find lib -type f -name '*.rbc' -exec rm -f '{}' ';'
65
+ -find ext -type f -name '*.rbc' -exec rm -f '{}' ';'
64
66
  $(RM) -r doc
65
- wrongdoc all
67
+ $(WRONGDOC) all
66
68
  install -m644 COPYING doc/COPYING
67
69
  install -m644 $(shell grep '^[A-Z]' .document) doc/
68
70
 
@@ -75,10 +77,10 @@ release_changes := release_changes-$(VERSION)
75
77
  release-notes: $(release_notes)
76
78
  release-changes: $(release_changes)
77
79
  $(release_changes):
78
- wrongdoc release_changes > $@+
80
+ $(WRONGDOC) release_changes > $@+
79
81
  $(VISUAL) $@+ && test -s $@+ && mv $@+ $@
80
82
  $(release_notes):
81
- wrongdoc release_notes > $@+
83
+ $(WRONGDOC) release_notes > $@+
82
84
  $(VISUAL) $@+ && test -s $@+ && mv $@+ $@
83
85
 
84
86
  # ensures we're actually on the tagged $(VERSION), only used for release
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.summary = summary
21
21
  s.test_files = Dir['test/test_*.rb']
22
22
  s.extensions = %w(ext/sleepy_penguin/extconf.rb)
23
- s.add_development_dependency('wrongdoc', '~> 1.3')
23
+ s.add_development_dependency('wrongdoc', '~> 1.5')
24
24
  s.add_development_dependency('strace_me', '~> 1.0')
25
25
 
26
26
  # s.license = %w(LGPL) # disabled for compatibility with older RubyGems
data/test/test_epoll.rb CHANGED
@@ -129,7 +129,11 @@ class TestEpoll < Test::Unit::TestCase
129
129
  Process.kill(:USR1, Process.ppid)
130
130
  end
131
131
  time[:START_WAIT] = Time.now
132
- @ep.wait { |flags, obj| tmp << [ flags, obj ]; time[:EP] = Time.now }
132
+ begin
133
+ @ep.wait { |flags, obj| tmp << [ flags, obj ]; time[:EP] = Time.now }
134
+ rescue Errno::EINTR
135
+ retry
136
+ end
133
137
  assert_equal([[Epoll::IN, @rd]], tmp)
134
138
  _, status = Process.waitpid2(pid)
135
139
  assert status.success?
@@ -313,7 +317,7 @@ class TestEpoll < Test::Unit::TestCase
313
317
  def test_new
314
318
  @ep.close
315
319
  io = Epoll.new.to_io
316
- assert((io.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC) == Fcntl::FD_CLOEXEC)
320
+ assert_equal 0, io.fcntl(Fcntl::F_GETFD)
317
321
  end
318
322
 
319
323
  def test_delete
@@ -90,8 +90,8 @@ class TestEpollOptimizations < Test::Unit::TestCase
90
90
  @wr.close
91
91
  io, err = Strace.me { rv = @ep.delete(obj) }
92
92
  lines = io.readlines; io.close
93
- assert_nil err
94
- assert_equal obj, rv
93
+ assert_kind_of IOError, err
94
+ assert_nil rv
95
95
  assert_equal 0, lines.grep(/^epoll_ctl/).size
96
96
  end
97
97
 
@@ -0,0 +1,62 @@
1
+ require 'test/unit'
2
+ require 'fcntl'
3
+ require 'tempfile'
4
+ $-w = true
5
+ require 'sleepy_penguin'
6
+
7
+ class TestInotify < Test::Unit::TestCase
8
+ include SleepyPenguin
9
+
10
+ def test_new
11
+ ino = Inotify.new
12
+ assert_kind_of(IO, ino)
13
+ end
14
+
15
+ def test_dup
16
+ a = Inotify.new
17
+ b = a.dup
18
+ assert a.fileno != b.fileno
19
+ abuf = a.instance_variable_get(:@inotify_buf)
20
+ bbuf = b.instance_variable_get(:@inotify_buf)
21
+ assert abuf.object_id != bbuf.object_id
22
+
23
+ atmp = a.instance_variable_get(:@inotify_tmp)
24
+ btmp = b.instance_variable_get(:@inotify_tmp)
25
+ assert_equal atmp.object_id, btmp.object_id
26
+ end
27
+
28
+ def test_new_nonblock
29
+ ino = Inotify.new Inotify::NONBLOCK
30
+ flags = ino.fcntl(Fcntl::F_GETFL) & Fcntl::O_NONBLOCK
31
+ assert_equal(Fcntl::O_NONBLOCK, flags)
32
+ end
33
+
34
+ def test_new_cloeexec
35
+ ino = Inotify.new Inotify::CLOEXEC
36
+ flags = ino.fcntl(Fcntl::F_GETFD) & Fcntl::FD_CLOEXEC
37
+ assert_equal(Fcntl::FD_CLOEXEC, flags)
38
+ end
39
+
40
+ def test_add_take
41
+ ino = Inotify.new Inotify::CLOEXEC
42
+ tmp1 = Tempfile.new 'take'
43
+ tmp2 = Tempfile.new 'take'
44
+ wd = ino.add_watch File.dirname(tmp1.path), Inotify::MOVE
45
+ assert_kind_of Integer, wd
46
+ File.rename tmp1.path, tmp2.path
47
+ event = ino.take
48
+ assert_equal wd, event.wd
49
+ assert_kind_of Inotify::Event, event
50
+ assert_equal File.basename(tmp1.path), event.name
51
+ others = ino.instance_variable_get(:@inotify_tmp)
52
+ assert_kind_of Array, others
53
+ assert_equal 1, others.size
54
+ assert_equal File.basename(tmp2.path), others[0].name
55
+ assert_equal [ :MOVED_FROM ], event.events
56
+ assert_equal [ :MOVED_TO ], others[0].events
57
+ assert_equal wd, others[0].wd
58
+ second_id = others[0].object_id
59
+ assert_equal second_id, ino.take.object_id
60
+ assert_nil ino.take(true)
61
+ end
62
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sleepy_penguin
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 7
5
5
  prerelease: false
6
6
  segments:
7
7
  - 1
8
- - 3
9
- - 1
10
- version: 1.3.1
8
+ - 4
9
+ - 0
10
+ version: 1.4.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - sleepy_penguin hackers
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-01-22 00:00:00 +00:00
18
+ date: 2011-02-04 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -26,11 +26,11 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- hash: 9
29
+ hash: 5
30
30
  segments:
31
31
  - 1
32
- - 3
33
- version: "1.3"
32
+ - 5
33
+ version: "1.5"
34
34
  type: :development
35
35
  version_requirements: *id001
36
36
  - !ruby/object:Gem::Dependency
@@ -88,6 +88,7 @@ files:
88
88
  - ext/sleepy_penguin/eventfd.c
89
89
  - ext/sleepy_penguin/extconf.rb
90
90
  - ext/sleepy_penguin/init.c
91
+ - ext/sleepy_penguin/inotify.c
91
92
  - ext/sleepy_penguin/nonblock.h
92
93
  - ext/sleepy_penguin/sleepy_penguin.h
93
94
  - ext/sleepy_penguin/timerfd.c
@@ -101,6 +102,7 @@ files:
101
102
  - test/test_epoll_gc.rb
102
103
  - test/test_epoll_optimizations.rb
103
104
  - test/test_eventfd.rb
105
+ - test/test_inotify.rb
104
106
  - test/test_timerfd.rb
105
107
  has_rdoc: true
106
108
  homepage: http://bogomips.org/sleepy_penguin/
@@ -143,6 +145,7 @@ summary: Linux I/O events for Ruby
143
145
  test_files:
144
146
  - test/test_epoll_optimizations.rb
145
147
  - test/test_epoll.rb
148
+ - test/test_inotify.rb
146
149
  - test/test_eventfd.rb
147
150
  - test/test_epoll_gc.rb
148
151
  - test/test_timerfd.rb