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.
@@ -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 */