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.
@@ -1,19 +1,34 @@
1
1
  #ifdef HAVE_SYS_EVENTFD_H
2
2
  #include "sleepy_penguin.h"
3
3
  #include <sys/eventfd.h>
4
- #include "nonblock.h"
5
4
  static ID id_for_fd;
6
5
 
7
- static VALUE create(int argc, VALUE *argv, VALUE klass)
6
+ /*
7
+ * call-seq:
8
+ * EventFD.new(initial_value [, flags]) -> EventFD IO object
9
+ *
10
+ * Creates an EventFD object. +initial_value+ is a non-negative Integer
11
+ * to start the internal counter at.
12
+ *
13
+ * Starting with Linux 2.6.27, +flags+ may be a mask that consists of any
14
+ * of the following:
15
+ *
16
+ * - :CLOEXEC - set the close-on-exec flag on the new object
17
+ * - :NONBLOCK - set the non-blocking I/O flag on the new object
18
+ *
19
+ * Since Linux 2.6.30, +flags+ may also include:
20
+ * - :SEMAPHORE - provides semaphore-like semantics (see EventFD#value)
21
+ */
22
+ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
8
23
  {
9
24
  VALUE _initval, _flags;
10
25
  unsigned initval;
11
- int flags = 0;
26
+ int flags;
12
27
  int fd;
13
28
 
14
29
  rb_scan_args(argc, argv, "11", &_initval, &_flags);
15
30
  initval = NUM2UINT(_initval);
16
- flags = NIL_P(_flags) ? 0 : NUM2INT(_flags);
31
+ flags = rb_sp_get_flags(klass, _flags);
17
32
 
18
33
  fd = eventfd(initval, flags);
19
34
  if (fd == -1) {
@@ -28,7 +43,6 @@ static VALUE create(int argc, VALUE *argv, VALUE klass)
28
43
  return rb_funcall(klass, id_for_fd, 1, INT2NUM(fd));
29
44
  }
30
45
 
31
- #ifdef HAVE_RB_THREAD_BLOCKING_REGION
32
46
  struct efd_args {
33
47
  int fd;
34
48
  uint64_t val;
@@ -37,7 +51,7 @@ struct efd_args {
37
51
  static VALUE efd_write(void *_args)
38
52
  {
39
53
  struct efd_args *args = _args;
40
- ssize_t w = write(args->fd, &args->buf, sizeof(uint64_t));
54
+ ssize_t w = write(args->fd, &args->val, sizeof(uint64_t));
41
55
 
42
56
  return (VALUE)w;
43
57
  }
@@ -45,111 +59,80 @@ static VALUE efd_write(void *_args)
45
59
  static VALUE efd_read(void *_args)
46
60
  {
47
61
  struct efd_args *args = _args;
48
- ssize_t r = read(args->fd, &args->buf, sizeof(uint64_t));
62
+ ssize_t r = read(args->fd, &args->val, sizeof(uint64_t));
49
63
 
50
64
  return (VALUE)r;
51
65
  }
52
66
 
53
- static VALUE incr(VALUE self, VALUE value)
67
+ /*
68
+ * call-seq:
69
+ * efd.incr(integer_value[, nonblock ]) -> true or nil
70
+ *
71
+ * Increments the internal counter by +integer_value+ which is an unsigned
72
+ * Integer value.
73
+ *
74
+ * If +nonblock+ is specified and true, this will return +nil+ if the
75
+ * internal counter will overflow the value of EventFD::MAX.
76
+ * Otherwise it will block until the counter may be incremented without
77
+ * overflowing.
78
+ */
79
+ static VALUE incr(int argc, VALUE *argv, VALUE self)
54
80
  {
55
81
  struct efd_args x;
56
82
  ssize_t w;
83
+ VALUE value, nonblock;
57
84
 
58
- x.fd = my_fileno(self);
85
+ rb_scan_args(argc, argv, "11", &value, &nonblock);
86
+ x.fd = rb_sp_fileno(self);
87
+ RTEST(nonblock) ? rb_sp_set_nonblock(x.fd) : blocking_io_prepare(x.fd);
59
88
  x.val = (uint64_t)NUM2ULL(value);
60
-
61
89
  retry:
62
- w = (ssize_t)rb_thread_blocking_region(efd_write, &x, RUBY_UBF_IO, 0);
90
+ w = (ssize_t)rb_sp_io_region(efd_write, &x);
63
91
  if (w == -1) {
92
+ if (errno == EAGAIN && RTEST(nonblock))
93
+ return Qfalse;
64
94
  if (rb_io_wait_writable(x.fd))
65
95
  goto retry;
66
96
  rb_sys_fail("write(eventfd)");
67
97
  }
68
98
 
69
- return Qnil;
99
+ return Qtrue;
70
100
  }
71
101
 
72
- static VALUE getvalue(VALUE self)
102
+ /*
103
+ * call-seq:
104
+ * efd.value([nonblock]) -> Integer or nil
105
+ *
106
+ * If not created as a semaphore, returns the current value and resets
107
+ * the counter to zero.
108
+ *
109
+ * If created as a semaphore, this decrements the counter value by one
110
+ * and returns +1+.
111
+ *
112
+ * If the counter is zero at the time of the call, this will block until
113
+ * the counter becomes non-zero unless +nonblock+ is +true+, in which
114
+ * case it returns +nil+.
115
+ */
116
+ static VALUE getvalue(int argc, VALUE argv, VALUE self)
73
117
  {
74
118
  struct efd_args x;
75
-
76
- x.fd = my_fileno(self);
77
-
78
- retry:
79
- w = (ssize_t)rb_thread_blocking_region(efd_read, &x, RUBY_UBF_IO, 0);
80
- if (w == -1) {
81
- if (rb_io_wait_readable(x.fd))
82
- goto retry;
83
- rb_sys_fail("read(eventfd)");
84
- }
85
-
86
- return ULL2NUM(x.buf);
87
- }
88
- #else /* !HAVE_RB_THREAD_BLOCKING_REGION */
89
-
90
- static VALUE incr(VALUE self, VALUE value)
91
- {
92
- int fd = my_fileno(self);
93
- uint64_t val = (uint64_t)NUM2ULL(value);
94
119
  ssize_t w;
120
+ VALUE nonblock;
95
121
 
96
- set_nonblock(fd);
122
+ rb_scan_args(argc, argv, "01", &nonblock);
123
+ x.fd = rb_sp_fileno(self);
124
+ RTEST(nonblock) ? rb_sp_set_nonblock(x.fd) : blocking_io_prepare(x.fd);
97
125
  retry:
98
- w = write(fd, &val, sizeof(uint64_t));
126
+ w = (ssize_t)rb_sp_io_region(efd_read, &x);
99
127
  if (w == -1) {
100
- if (rb_io_wait_writable(fd))
101
- goto retry;
102
- rb_sys_fail("write(eventfd)");
103
- }
104
-
105
- return Qnil;
106
- }
107
-
108
- static VALUE getvalue(VALUE self)
109
- {
110
- int fd = my_fileno(self);
111
- uint64_t val;
112
- ssize_t r;
113
-
114
- set_nonblock(fd);
115
- retry:
116
- r = read(fd, &val, sizeof(uint64_t));
117
- if (r == -1) {
118
- if (rb_io_wait_readable(fd))
128
+ if (errno == EAGAIN && RTEST(nonblock))
129
+ return Qnil;
130
+ if (rb_io_wait_readable(x.fd))
119
131
  goto retry;
120
132
  rb_sys_fail("read(eventfd)");
121
133
  }
122
134
 
123
- return ULL2NUM(val);
124
- }
125
- #endif /* !HAVE_RB_THREAD_BLOCKING_REGION */
126
-
127
- static VALUE getvalue_nonblock(VALUE self)
128
- {
129
- int fd = my_fileno(self);
130
- uint64_t val;
131
- ssize_t r;
132
-
133
- set_nonblock(fd);
134
- r = read(fd, &val, sizeof(uint64_t));
135
- if (r == -1)
136
- rb_sys_fail("read(eventfd)");
137
-
138
- return ULL2NUM(val);
139
- }
140
-
141
- static VALUE incr_nonblock(VALUE self, VALUE value)
142
- {
143
- int fd = my_fileno(self);
144
- uint64_t val = (uint64_t)NUM2ULL(value);
145
- ssize_t w;
146
-
147
- set_nonblock(fd);
148
- w = write(fd, &val, sizeof(uint64_t));
149
- if (w == -1)
150
- rb_sys_fail("write(eventfd)");
151
-
152
- return Qnil;
135
+ return ULL2NUM(x.val);
153
136
  }
154
137
 
155
138
  void sleepy_penguin_init_eventfd(void)
@@ -157,21 +140,36 @@ void sleepy_penguin_init_eventfd(void)
157
140
  VALUE mSleepyPenguin, cEventFD;
158
141
 
159
142
  mSleepyPenguin = rb_define_module("SleepyPenguin");
143
+
144
+ /*
145
+ * Document-class: SleepyPenguin::EventFD
146
+ *
147
+ * Applications may use EventFD instead of a pipe in cases where
148
+ * a pipe is only used to signal events. The kernel overhead for
149
+ * an EventFD descriptor is much lower than that of a pipe.
150
+ *
151
+ * As of Linux 2.6.30, an EventFD may also be used as a semaphore.
152
+ */
160
153
  cEventFD = rb_define_class_under(mSleepyPenguin, "EventFD", rb_cIO);
161
- rb_define_singleton_method(cEventFD, "new", create, -1);
154
+ rb_define_singleton_method(cEventFD, "new", s_new, -1);
155
+
156
+ /*
157
+ * the maximum value that may be stored in an EventFD,
158
+ * currently 0xfffffffffffffffe
159
+ */
160
+ rb_define_const(cEventFD, "MAX", ULL2NUM(0xfffffffffffffffe));
161
+
162
162
  #ifdef EFD_NONBLOCK
163
- rb_define_const(cEventFD, "NONBLOCK", UINT2NUM(EFD_NONBLOCK));
163
+ NODOC_CONST(cEventFD, "NONBLOCK", INT2NUM(EFD_NONBLOCK));
164
164
  #endif
165
165
  #ifdef EFD_CLOEXEC
166
- rb_define_const(cEventFD, "CLOEXEC", UINT2NUM(EFD_CLOEXEC));
166
+ NODOC_CONST(cEventFD, "CLOEXEC", INT2NUM(EFD_CLOEXEC));
167
167
  #endif
168
168
  #ifdef EFD_SEMAPHORE
169
- rb_define_const(cEventFD, "SEMAPHORE", UINT2NUM(EFD_SEMAPHORE));
169
+ NODOC_CONST(cEventFD, "SEMAPHORE", INT2NUM(EFD_SEMAPHORE));
170
170
  #endif
171
- rb_define_method(cEventFD, "value", getvalue, 0);
172
- rb_define_method(cEventFD, "incr", incr, 1);
173
- rb_define_method(cEventFD, "value_nonblock", getvalue_nonblock, 0);
174
- rb_define_method(cEventFD, "incr_nonblock", incr_nonblock, 1);
171
+ rb_define_method(cEventFD, "value", getvalue, -1);
172
+ rb_define_method(cEventFD, "incr", incr, -1);
175
173
  id_for_fd = rb_intern("for_fd");
176
174
  }
177
175
  #endif /* HAVE_SYS_EVENTFD_H */
@@ -5,6 +5,7 @@ have_header('sys/eventfd.h')
5
5
  have_header('sys/signalfd.h')
6
6
  have_header('sys/timerfd.h')
7
7
  have_header('sys/inotify.h')
8
+ have_header('sys/signalfd.h')
8
9
  have_header('ruby/io.h') and have_struct_member('rb_io_t', 'fd', 'ruby/io.h')
9
10
  have_func('rb_memerror') or abort 'need rb_memerror()'
10
11
  have_func('rb_io_close') or abort 'need rb_io_close()'
@@ -18,10 +18,17 @@ void sleepy_penguin_init_inotify(void);
18
18
  # define sleepy_penguin_init_inotify() for(;0;)
19
19
  #endif
20
20
 
21
+ #ifdef HAVE_SYS_SIGNALFD_H
22
+ void sleepy_penguin_init_signalfd(void);
23
+ #else
24
+ # define sleepy_penguin_init_signalfd() for(;0;)
25
+ #endif
26
+
21
27
  void Init_sleepy_penguin_ext(void)
22
28
  {
23
29
  sleepy_penguin_init_epoll();
24
30
  sleepy_penguin_init_timerfd();
25
31
  sleepy_penguin_init_eventfd();
26
32
  sleepy_penguin_init_inotify();
33
+ sleepy_penguin_init_signalfd();
27
34
  }
@@ -1,77 +1,32 @@
1
1
  #ifdef HAVE_SYS_INOTIFY_H
2
2
  #include "sleepy_penguin.h"
3
- #include "nonblock.h"
4
3
  #include <sys/inotify.h>
5
4
  #include <sys/ioctl.h>
5
+ #include "missing_inotify.h"
6
+ #if defined(RFILE) && defined(HAVE_ST_FD) && \
7
+ defined(HAVE_RB_THREAD_BLOCKING_REGION)
8
+ # define NOGVL_CLOSE
9
+ #endif
10
+
6
11
  static ID id_for_fd, id_inotify_buf, id_inotify_tmp, id_mask;
7
12
  static VALUE cEvent, checks;
8
13
 
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
14
  /*
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.
15
+ * call-seq:
16
+ * Inotify.new([flags]) -> Inotify IO object
17
+ *
18
+ * Flags may be any of the following as an Array of Symbols or Integer mask:
19
+ * - :NONBLOCK - sets the non-blocking flag on the descriptor watched.
20
+ * - :CLOEXEC - sets the close-on-exec flag
39
21
  */
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)
22
+ static VALUE s_new(int argc, VALUE *argv, VALUE klass)
68
23
  {
69
24
  VALUE _flags, rv;
70
25
  int flags;
71
26
  int fd;
72
27
 
73
28
  rb_scan_args(argc, argv, "01", &_flags);
74
- flags = NIL_P(_flags) ? 0 : NUM2INT(_flags);
29
+ flags = rb_sp_get_flags(klass, _flags);
75
30
 
76
31
  fd = inotify_init1(flags);
77
32
  if (fd == -1) {
@@ -90,11 +45,51 @@ static VALUE s_init(int argc, VALUE *argv, VALUE klass)
90
45
  return rv;
91
46
  }
92
47
 
48
+ /*
49
+ * call-seq:
50
+ * ino.add_watch(path, flags) -> Integer
51
+ *
52
+ * Adds a watch on an object specified by its +mask+, returns an unsigned
53
+ * Integer watch descriptor. +flags+ may be a mask of the following
54
+ * Inotify constants or array of their symbolic names.
55
+ *
56
+ * - :ACCESS - File was accessed (read) (*)
57
+ * - :ATTRIB - Metadata changed.
58
+ * - :CLOSE_WRITE - File opened for writing was closed (*)
59
+ * - :CLOSE_NOWRITE - File not opened for writing was closed (*)
60
+ * - :CREATE - File/directory created in watched directory (*)
61
+ * - :DELETE - File/directory deleted from watched directory (*)
62
+ * - :DELETE_SELF - Watched file/directory was itself deleted
63
+ * - :MODIFY - File was modified (*)
64
+ * - :MOVE_SELF - Watched file/directory was itself moved
65
+ * - :MOVED_FROM - File moved out of watched directory (*)
66
+ * - :MOVED_TO - File moved into watched directory (*)
67
+ * - :OPEN - File was opened (*)
68
+ *
69
+ * When monitoring a directory, the events marked with an asterisk (*)
70
+ * above can occur for files in the directory, in which case the name
71
+ * field in the Event structure identifies the name of the file in the
72
+ * directory.
73
+ *
74
+ * Shortcut flags:
75
+ *
76
+ * - :ALL_EVENTS - a bitmask of all the above events
77
+ * - :MOVE - :MOVED_FROM or :MOVED_TO
78
+ * - :CLOSE - :CLOSE_WRITE or :CLOSE_NOWRITE
79
+ *
80
+ * The following watch attributes may also be included in flags:
81
+ *
82
+ * - :DONT_FOLLOW - don't dereference symlinks (since Linux 2.6.15)
83
+ * - :EXCL_UNLINK - don't generate unlink events for children (since 2.6.36)
84
+ * - :MASK_ADD - add events to an existing watch mask if it exists
85
+ * - :ONESHOT - monitor for one event and then remove it from the watch
86
+ * - :ONLYDIR - only watch the pathname if it is a directory
87
+ */
93
88
  static VALUE add_watch(VALUE self, VALUE path, VALUE vmask)
94
89
  {
95
- int fd = my_fileno(self);
96
- const char *pathname = StringValuePtr(path);
97
- uint32_t mask = NUM2UINT(vmask);
90
+ int fd = rb_sp_fileno(self);
91
+ const char *pathname = StringValueCStr(path);
92
+ uint32_t mask = rb_sp_get_uflags(self, vmask);
98
93
  int rc = inotify_add_watch(fd, pathname, mask);
99
94
 
100
95
  if (rc == -1) {
@@ -108,10 +103,17 @@ static VALUE add_watch(VALUE self, VALUE path, VALUE vmask)
108
103
  return UINT2NUM((uint32_t)rc);
109
104
  }
110
105
 
106
+ /*
107
+ * call-seq:
108
+ * ino.rm_watch(watch_descriptor) -> 0
109
+ *
110
+ * Removes a watch based on a watch descriptor Integer. The watch
111
+ * descriptor is a return value given by Inotify#add_watch
112
+ */
111
113
  static VALUE rm_watch(VALUE self, VALUE vwd)
112
114
  {
113
115
  uint32_t wd = NUM2UINT(vwd);
114
- int fd = my_fileno(self);
116
+ int fd = rb_sp_fileno(self);
115
117
  int rc = inotify_rm_watch(fd, wd);
116
118
 
117
119
  if (rc == -1)
@@ -137,14 +139,32 @@ static VALUE event_new(struct inotify_event *e)
137
139
  return rb_struct_new(cEvent, wd, mask, cookie, name);
138
140
  }
139
141
 
142
+ struct inread_args {
143
+ int fd;
144
+ struct inotify_event *ptr;
145
+ long len;
146
+ };
147
+
148
+ static VALUE inread(void *ptr)
149
+ {
150
+ struct inread_args *args = ptr;
151
+
152
+ return (VALUE)read(args->fd, args->ptr, args->len);
153
+ }
154
+
155
+ /*
156
+ * call-seq:
157
+ * ino.take([nonblock]) -> Inotify::Event or nil
158
+ *
159
+ * Returns the next Inotify::Event processed. May return +nil+ if +nonblock+
160
+ * is +true+.
161
+ */
140
162
  static VALUE take(int argc, VALUE *argv, VALUE self)
141
163
  {
142
- int fd = my_fileno(self);
143
- VALUE buf = rb_ivar_get(self, id_inotify_buf);
164
+ struct inread_args args;
165
+ VALUE buf;
144
166
  VALUE tmp = rb_ivar_get(self, id_inotify_tmp);
145
- struct inotify_event *ptr;
146
167
  struct inotify_event *e, *end;
147
- long len;
148
168
  ssize_t r;
149
169
  VALUE rv = Qnil;
150
170
  VALUE nonblock;
@@ -154,31 +174,41 @@ static VALUE take(int argc, VALUE *argv, VALUE self)
154
174
 
155
175
  rb_scan_args(argc, argv, "01", &nonblock);
156
176
 
157
- len = RSTRING_LEN(buf);
158
- ptr = (struct inotify_event *)RSTRING_PTR(buf);
177
+ args.fd = rb_sp_fileno(self);
178
+ buf = rb_ivar_get(self, id_inotify_buf);
179
+ args.len = RSTRING_LEN(buf);
180
+ args.ptr = (struct inotify_event *)RSTRING_PTR(buf);
181
+
182
+ if (RTEST(nonblock))
183
+ rb_sp_set_nonblock(args.fd);
184
+ else
185
+ blocking_io_prepare(args.fd);
159
186
  do {
160
- set_nonblock(fd);
161
- r = read(fd, ptr, len);
162
- if (r == 0 || (r < 0 && errno == EINVAL)) {
187
+ r = rb_sp_io_region(inread, &args);
188
+ if (r == 0 /* Linux < 2.6.21 */
189
+ ||
190
+ (r < 0 && errno == EINVAL) /* Linux >= 2.6.21 */
191
+ ) {
192
+ /* resize internal buffer */
163
193
  int newlen;
164
- if (len > 0x10000)
194
+ if (args.len > 0x10000)
165
195
  rb_raise(rb_eRuntimeError, "path too long");
166
- if (ioctl(fd, FIONREAD, &newlen) != 0)
196
+ if (ioctl(args.fd, FIONREAD, &newlen) != 0)
167
197
  rb_sys_fail("ioctl(inotify,FIONREAD)");
168
198
  rb_str_resize(buf, newlen);
169
- ptr = (struct inotify_event *)RSTRING_PTR(buf);
170
- len = newlen;
199
+ args.ptr = (struct inotify_event *)RSTRING_PTR(buf);
200
+ args.len = newlen;
171
201
  } else if (r < 0) {
172
- if (errno == EAGAIN) {
173
- if (RTEST(nonblock))
174
- return Qnil;
175
- rb_io_wait_readable(fd);
202
+ if (errno == EAGAIN && RTEST(nonblock)) {
203
+ return Qnil;
176
204
  } else {
177
- rb_sys_fail("read(inotify)");
205
+ if (!rb_io_wait_readable(args.fd))
206
+ rb_sys_fail("read(inotify)");
178
207
  }
179
208
  } else {
180
- end = (struct inotify_event *)((char *)ptr + r);
181
- for (e = ptr; e < end; ) {
209
+ /* buffer in userspace to minimize read() calls */
210
+ end = (struct inotify_event *)((char *)args.ptr + r);
211
+ for (e = args.ptr; e < end; ) {
182
212
  VALUE event = event_new(e);
183
213
  if (NIL_P(rv))
184
214
  rv = event;
@@ -193,6 +223,13 @@ static VALUE take(int argc, VALUE *argv, VALUE self)
193
223
  return rv;
194
224
  }
195
225
 
226
+ /*
227
+ * call-seq:
228
+ * inotify_event.events => [ :MOVED_TO, ... ]
229
+ *
230
+ * Returns an array of symbolic event names based on the contents of
231
+ * the +mask+ field.
232
+ */
196
233
  static VALUE events(VALUE self)
197
234
  {
198
235
  long len = RARRAY_LEN(checks);
@@ -213,30 +250,132 @@ static VALUE events(VALUE self)
213
250
  return rv;
214
251
  }
215
252
 
253
+ /*
254
+ * call-seq:
255
+ * inotify.dup -> another Inotify object
256
+ *
257
+ * Duplicates an Inotify object, allowing it to be used in a blocking
258
+ * fashion in another thread. Ensures duplicated Inotify objects do
259
+ * not share read buffers, but do share the userspace Array buffer.
260
+ */
216
261
  static VALUE init_copy(VALUE dest, VALUE orig)
217
262
  {
218
263
  VALUE tmp;
219
264
 
220
- dest = rb_call_super(1, &orig);
265
+ dest = rb_call_super(1, &orig); /* copy all other ivars as-is */
221
266
  rb_ivar_set(dest, id_inotify_buf, rb_str_new(0, 128));
222
267
 
223
268
  return dest;
224
269
  }
225
270
 
271
+ /*
272
+ * call-seq:
273
+ * ino.each { |event| ... } -> ino
274
+ *
275
+ * Yields each Inotify::Event received in a blocking fashion.
276
+ */
277
+ static VALUE each(VALUE self)
278
+ {
279
+ VALUE argv = Qfalse;
280
+
281
+ while (1)
282
+ rb_yield(take(0, &argv, self));
283
+
284
+ return self;
285
+ }
286
+
287
+ #if defined(NOGVL_CLOSE)
288
+ static VALUE fptr_close(void *ptr)
289
+ {
290
+ rb_io_t *fptr = ptr;
291
+ return (VALUE)close(fptr->fd);
292
+ }
293
+
294
+ /*
295
+ * call-seq:
296
+ * ino.close -> nil
297
+ *
298
+ * Closes the underlying file descriptor and releases resources used by the
299
+ * kernel. Unlike other file descriptors, Inotify descriptors can take
300
+ * a long time to close(2). Calling this explicitly releases the GVL under
301
+ * Ruby 1.9
302
+ */
303
+ static VALUE nogvl_close(VALUE self)
304
+ {
305
+ rb_io_t *fptr;
306
+
307
+ GetOpenFile(self, fptr);
308
+
309
+ if ((int)rb_sp_io_region(fptr_close, fptr) < 0)
310
+ rb_sys_fail("close(inotify)");
311
+ fptr->fd = -1;
312
+
313
+ return Qnil;
314
+ }
315
+ #endif /* NOGVL_CLOSE */
316
+
226
317
  void sleepy_penguin_init_inotify(void)
227
318
  {
228
319
  VALUE mSleepyPenguin, cInotify;
229
320
 
230
321
  mSleepyPenguin = rb_define_module("SleepyPenguin");
322
+
323
+ /*
324
+ * Document-class: SleepyPenguin::Inotify
325
+ *
326
+ * Inotify objects are used for monitoring file system events,
327
+ * it can monitor individual files or directories. When a directory
328
+ * is monitored it will return events for the directory itself and
329
+ * all files inside the directory.
330
+ *
331
+ * Inotify IO objects can be watched using IO.select or Epoll.
332
+ * IO#close may be called on the object when it is no longer needed.
333
+ *
334
+ * Inotify is available on Linux 2.6.13 or later.
335
+ *
336
+ * require "sleepy_penguin/sp"
337
+ * ino = SP::Inotify.new
338
+ * ino.add_watch("/path/to/foo", :OPEN)
339
+ * ino.each do |event|
340
+ * p event.events # => [ :OPEN ]
341
+ * end
342
+ */
231
343
  cInotify = rb_define_class_under(mSleepyPenguin, "Inotify", rb_cIO);
232
344
  rb_define_method(cInotify, "add_watch", add_watch, 2);
233
345
  rb_define_method(cInotify, "rm_watch", rm_watch, 1);
234
346
  rb_define_method(cInotify, "initialize_copy", init_copy, 1);
235
347
  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);
348
+ rb_define_method(cInotify, "each", each, 0);
349
+ #ifdef NOGVL_CLOSE
350
+ rb_define_method(cInotify, "close", nogvl_close, 0);
351
+ #endif
352
+
353
+ /*
354
+ * Document-class: SleepyPenguin::Inotify::Event
355
+ *
356
+ * Returned by SleepyPenguin::Inotify#take. It is a Struct with the
357
+ * following elements:
358
+ *
359
+ * - wd - watch descriptor (unsigned Integer)
360
+ * - mask - mask of events (unsigned Integer)
361
+ * - cookie - unique cookie associated related events (for rename)
362
+ * - name - optional string name (may be nil)
363
+ *
364
+ * The mask is a bitmask of the event flags accepted by
365
+ * Inotify#add_watch and may also include the following flags:
366
+ *
367
+ * - :IGNORED - watch was removed
368
+ * - :ISDIR - event occured on a directory
369
+ * - :Q_OVERFLOW - event queue overflowed (wd is -1)
370
+ * - :UNMOUNT - filesystem containing watched object was unmounted
371
+ *
372
+ * Use the Event#events method to get an array of symbols for the
373
+ * matched events.
374
+ */
375
+ cEvent = rb_struct_define("Event", "wd", "mask", "cookie", "name", 0);
376
+ cEvent = rb_define_class_under(cInotify, "Event", cEvent);
238
377
  rb_define_method(cEvent, "events", events, 0);
239
- rb_define_singleton_method(cInotify, "new", s_init, -1);
378
+ rb_define_singleton_method(cInotify, "new", s_new, -1);
240
379
  id_for_fd = rb_intern("for_fd");
241
380
  id_inotify_buf = rb_intern("@inotify_buf");
242
381
  id_inotify_tmp = rb_intern("@inotify_tmp");
@@ -251,8 +390,6 @@ void sleepy_penguin_init_inotify(void)
251
390
  rb_ary_push(checks, val); \
252
391
  } while (0)
253
392
 
254
- rb_define_const(cInotify, "FIONREAD", INT2NUM(FIONREAD));
255
-
256
393
  IN(ALL_EVENTS);
257
394
 
258
395
  /* events a user can watch for */
@@ -287,7 +424,8 @@ void sleepy_penguin_init_inotify(void)
287
424
  IN(ONESHOT);
288
425
 
289
426
  /* for inotify_init1() */
290
- IN(NONBLOCK);
291
- IN(CLOEXEC);
427
+
428
+ NODOC_CONST(cInotify, "NONBLOCK", INT2NUM(IN_NONBLOCK));
429
+ NODOC_CONST(cInotify, "CLOEXEC", INT2NUM(IN_CLOEXEC));
292
430
  }
293
431
  #endif /* HAVE_SYS_INOTIFY_H */