sleepy_penguin 3.1.0.26.g7181 → 3.2.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/.document CHANGED
@@ -10,3 +10,4 @@ ext/sleepy_penguin/init.c
10
10
  ext/sleepy_penguin/inotify.c
11
11
  ext/sleepy_penguin/signalfd.c
12
12
  ext/sleepy_penguin/timerfd.c
13
+ ext/sleepy_penguin/kqueue.c
data/.gitignore CHANGED
@@ -21,3 +21,4 @@ tags
21
21
  TAGS
22
22
  /LATEST
23
23
  /tmp
24
+ git_version.h
data/GIT-VERSION-GEN CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v3.1.0.GIT
4
+ DEF_VER=v3.2.0
5
+ GVH=ext/sleepy_penguin/git_version.h
5
6
 
6
7
  LF='
7
8
  '
@@ -35,6 +36,12 @@ else
35
36
  VC=unset
36
37
  fi
37
38
  test "$VN" = "$VC" || {
39
+ {
40
+ echo '#ifndef MY_GIT_VERSION'
41
+ echo '#define MY_GIT_VERSION "'$VN'"'
42
+ echo '#endif /* MY_GIT_VERSION */'
43
+ } >$GVH.tmp.$$
44
+ mv $GVH.tmp.$$ $GVH
38
45
  echo >&2 "GIT_VERSION = $VN"
39
46
  echo "GIT_VERSION = $VN" >$GVF
40
47
  }
data/GNUmakefile CHANGED
@@ -3,6 +3,7 @@ RSYNC_DEST := bogomips.org:/srv/bogomips/sleepy_penguin
3
3
  rfproject := rainbows
4
4
  rfpackage := sleepy_penguin
5
5
  include pkg.mk
6
+ pkg_extra += ext/sleepy_penguin/git_version.h
6
7
  ifneq ($(VERSION),)
7
8
  release::
8
9
  $(RAKE) raa_update VERSION=$(VERSION)
data/README CHANGED
@@ -2,19 +2,21 @@
2
2
 
3
3
  sleepy_penguin provides access to newer, Linux-only system calls to wait
4
4
  on events from traditionally non-I/O sources. Bindings to the eventfd,
5
- timerfd, inotify, and epoll interfaces are provided.
5
+ timerfd, inotify, and epoll interfaces are provided. Experimental support
6
+ for kqueue on FreeBSD (and likely OpenBSD/NetBSD) are also provided.
6
7
 
7
8
  == Features
8
9
 
9
- * Thread-safe blocking operations for all versions of Ruby
10
+ * Thread-safe blocking operations for all versions of Matz Ruby and Rubinius
10
11
 
11
12
  * IO-like objects are backwards-compatible with IO.select.
12
13
 
13
- * Epoll interface is fork-safe and GC-safe
14
+ * High-level Epoll interface is fork-safe and GC-safe
14
15
 
15
16
  * Unlike portable event frameworks, the Linux-only epoll interfaces
16
17
  allow using edge-triggered or one-shot notifications for possibly
17
- improved performance
18
+ improved performance. Likewise, the kqueue interface supports
19
+ one-shot notifiactions, too.
18
20
 
19
21
  * Fully-documented and user-friendly API
20
22
 
@@ -1,4 +1,5 @@
1
1
  #include "sleepy_penguin.h"
2
+ #ifdef HAVE_SYS_EPOLL_H
2
3
  #include <sys/epoll.h>
3
4
  #include <unistd.h>
4
5
  #include <time.h>
@@ -28,16 +29,6 @@ static VALUE unpack_event_data(struct epoll_event *event)
28
29
  return (VALUE)event->data.ptr;
29
30
  }
30
31
 
31
- #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
32
- # define FLEX_ARRAY
33
- #elif defined(__GNUC__)
34
- # if (__GNUC__ >= 3)
35
- # define FLEX_ARRAY
36
- # else
37
- # define FLEX_ARRAY 0
38
- # endif
39
- #endif
40
-
41
32
  struct ep_per_thread {
42
33
  VALUE io;
43
34
  int fd;
@@ -156,8 +147,12 @@ static VALUE epwait_result(struct ep_per_thread *ept, int n)
156
147
  struct epoll_event *epoll_event = ept->events;
157
148
  VALUE obj_events, obj;
158
149
 
159
- if (n < 0)
160
- rb_sys_fail("epoll_wait");
150
+ if (n < 0) {
151
+ if (errno == EINTR)
152
+ n = 0;
153
+ else
154
+ rb_sys_fail("epoll_wait");
155
+ }
161
156
 
162
157
  for (i = n; --i >= 0; epoll_event++) {
163
158
  obj_events = UINT2NUM(epoll_event->events);
@@ -348,4 +343,8 @@ void sleepy_penguin_init_epoll(void)
348
343
 
349
344
  if (RB_SP_GREEN_THREAD)
350
345
  rb_require("sleepy_penguin/epoll/io");
346
+
347
+ /* the high-level interface is implemented in Ruby: */
348
+ rb_require("sleepy_penguin/epoll");
351
349
  }
350
+ #endif /* HAVE_SYS_EPOLL_H */
@@ -1,5 +1,9 @@
1
1
  require 'mkmf'
2
- have_header('sys/epoll.h') or abort 'sys/epoll.h not found'
2
+ have_header('sys/epoll.h')
3
+ dir_config('kqueue')
4
+ have_library('kqueue')
5
+ have_header('sys/event.h')
6
+ have_header('sys/mount.h')
3
7
  have_header('sys/eventfd.h')
4
8
 
5
9
  # it's impossible to use signalfd reliably with Ruby since Ruby currently
@@ -1,10 +1,22 @@
1
1
  #define _GNU_SOURCE
2
+ #include <ruby.h>
2
3
  #include <unistd.h>
3
4
  #include <sys/types.h>
5
+ #include "git_version.h"
4
6
  #define L1_CACHE_LINE_MAX 128 /* largest I've seen (Pentium 4) */
5
7
  size_t rb_sp_l1_cache_line_size;
6
8
 
9
+ #ifdef HAVE_SYS_EVENT_H
10
+ void sleepy_penguin_init_kqueue(void);
11
+ #else
12
+ # define sleepy_penguin_init_kqueue() for(;0;)
13
+ #endif
14
+
15
+ #ifdef HAVE_SYS_EPOLL_H
7
16
  void sleepy_penguin_init_epoll(void);
17
+ #else
18
+ # define sleepy_penguin_init_epoll() for(;0;)
19
+ #endif
8
20
 
9
21
  #ifdef HAVE_SYS_TIMERFD_H
10
22
  void sleepy_penguin_init_timerfd(void);
@@ -43,8 +55,15 @@ static size_t l1_cache_line_size_detect(void)
43
55
 
44
56
  void Init_sleepy_penguin_ext(void)
45
57
  {
58
+ VALUE mSleepyPenguin;
59
+
46
60
  rb_sp_l1_cache_line_size = l1_cache_line_size_detect();
47
61
 
62
+ mSleepyPenguin = rb_define_module("SleepyPenguin");
63
+ rb_define_const(mSleepyPenguin, "SLEEPY_PENGUIN_VERSION",
64
+ rb_str_new2(MY_GIT_VERSION));
65
+
66
+ sleepy_penguin_init_kqueue();
48
67
  sleepy_penguin_init_epoll();
49
68
  sleepy_penguin_init_timerfd();
50
69
  sleepy_penguin_init_eventfd();
@@ -0,0 +1,675 @@
1
+ #include "sleepy_penguin.h"
2
+ #ifdef HAVE_SYS_EVENT_H
3
+ #include <sys/types.h>
4
+ #include <sys/event.h>
5
+ #include <sys/time.h>
6
+ #include <unistd.h>
7
+ #include <time.h>
8
+ #include "missing_rb_thread_fd_close.h"
9
+ #include "missing_rb_update_max_fd.h"
10
+ #include "value2timespec.h"
11
+
12
+ #ifdef HAVE_SYS_MOUNT_H /* for VQ_* flags on FreeBSD */
13
+ # include <sys/mount.h>
14
+ #endif
15
+
16
+ /* not bothering with overflow checking for backwards compat */
17
+ #ifndef RARRAY_LENINT
18
+ # define RARRAY_LENINT(ary) (int)RARRAY_LEN(ary)
19
+ #endif
20
+ #ifndef NUM2SHORT
21
+ # define NUM2SHORT(n) (short)NUM2INT(n)
22
+ #endif
23
+ #ifndef NUM2USHORT
24
+ # define NUM2USHORT(n) (short)NUM2UINT(n)
25
+ #endif
26
+
27
+ /*
28
+ * Rubinius does not support RSTRUCT_* in the C API:
29
+ * ref: https://github.com/rubinius/rubinius/issues/494
30
+ */
31
+ #if defined(RUBINIUS)
32
+ # define RBX_STRUCT (1)
33
+ # define RSTRUCT_LEN(s) 0, rb_bug("RSTRUCT_LEN attempted in Rubinius")
34
+ # define RSTRUCT_PTR(s) NULL, rb_bug("RSTRUCT_PTR attempted in Rubinius")
35
+ #else
36
+ # define RBX_STRUCT (0)
37
+ #endif
38
+
39
+ static const long NANO_PER_SEC = 1000000000;
40
+ static ID id_for_fd;
41
+ static VALUE mEv, mEvFilt, mNote, mVQ;
42
+
43
+ struct kq_per_thread {
44
+ VALUE io;
45
+ int fd;
46
+ int nchanges;
47
+ int nevents;
48
+ int capa;
49
+ struct timespec *ts;
50
+ struct kevent events[FLEX_ARRAY];
51
+ };
52
+
53
+ static void tssub(struct timespec *a, struct timespec *b, struct timespec *res)
54
+ {
55
+ res->tv_sec = a->tv_sec - b->tv_sec;
56
+ res->tv_nsec = a->tv_nsec - b->tv_nsec;
57
+ if (res->tv_nsec < 0) {
58
+ res->tv_sec--;
59
+ res->tv_nsec += NANO_PER_SEC;
60
+ }
61
+ }
62
+
63
+ /* this will raise if the IO is closed */
64
+ static int kq_fd_check(struct kq_per_thread *kpt)
65
+ {
66
+ int save_errno = errno;
67
+
68
+ kpt->fd = rb_sp_fileno(kpt->io);
69
+ errno = save_errno;
70
+
71
+ return 1;
72
+ }
73
+
74
+ static struct kq_per_thread *kpt_get(VALUE self, int nchanges, int nevents)
75
+ {
76
+ static __thread struct kq_per_thread *kpt;
77
+ size_t size;
78
+ void *ptr;
79
+ int err;
80
+ int max = nchanges > nevents ? nchanges : nevents;
81
+
82
+ /* error check here to prevent OOM from posix_memalign */
83
+ if (max < 0) {
84
+ errno = EINVAL;
85
+ rb_sys_fail("kevent got negative events < 0");
86
+ }
87
+
88
+ if (kpt && kpt->capa >= max)
89
+ goto out;
90
+
91
+ size = sizeof(struct kq_per_thread) + sizeof(struct kevent) * max;
92
+
93
+ free(kpt); /* free(NULL) is POSIX */
94
+ err = posix_memalign(&ptr, rb_sp_l1_cache_line_size, size);
95
+ if (err) {
96
+ errno = err;
97
+ rb_memerror();
98
+ }
99
+ kpt = ptr;
100
+ kpt->capa = max;
101
+ out:
102
+ kpt->nchanges = nchanges;
103
+ kpt->nevents = nevents;
104
+ kpt->io = self;
105
+ kpt->fd = rb_sp_fileno(kpt->io);
106
+
107
+ return kpt;
108
+ }
109
+
110
+ /*
111
+ * call-seq:
112
+ * SleepyPenguin::Kqueue::IO.new -> Kqueue::IO object
113
+ *
114
+ * Creates a new Kqueue::IO object. This is a wrapper around the kqueue(2)
115
+ * system call which creates a Ruby IO object around the kqueue descriptor.
116
+ *
117
+ * kqueue descriptors are automatically invalidated across fork, so care
118
+ * must be taken when forking.
119
+ * Setting IO#autoclose=false is recommended for applications which fork
120
+ * after kqueue creation. Ruby 1.8 does not have IO#autoclose=, so using
121
+ * this class is not recommended under Ruby 1.8
122
+ */
123
+ static VALUE s_new(VALUE klass)
124
+ {
125
+ VALUE rv;
126
+ int fd = kqueue();
127
+
128
+ if (fd < 0) {
129
+ /*
130
+ * ENOMEM/EMFILE/ENFILE are the only documented errors
131
+ * for kqueue(), hope GC can give us some space to retry:
132
+ */
133
+ rb_gc();
134
+ fd = kqueue();
135
+ if (fd < 0)
136
+ rb_sys_fail("kqueue");
137
+ }
138
+
139
+ rv = INT2FIX(fd);
140
+
141
+ /* This will set FD_CLOEXEC on Ruby 2.0.0+: */
142
+ return rb_call_super(1, &rv);
143
+ }
144
+
145
+ static void yield_kevent(struct kevent *event)
146
+ {
147
+ VALUE ident = ULONG2NUM((unsigned long)event->ident); /* uintptr_t */
148
+ VALUE filter = INT2NUM((int)event->filter); /* short */
149
+ VALUE flags = UINT2NUM((unsigned)event->flags); /* u_short */
150
+ VALUE fflags = UINT2NUM((unsigned)event->fflags); /* u_int */
151
+ VALUE data = LONG2NUM((long)event->data); /* intptr_t */
152
+ VALUE udata = (VALUE)event->udata; /* void * */
153
+
154
+ rb_yield_values(6, ident, filter, flags, fflags, data, udata);
155
+ }
156
+
157
+ static VALUE kevent_result(struct kq_per_thread *kpt, int nevents)
158
+ {
159
+ int i;
160
+ struct kevent *event = kpt->events;
161
+
162
+ if (nevents < 0) {
163
+ if (errno == EINTR)
164
+ nevents = 0;
165
+ else
166
+ rb_sys_fail("kevent");
167
+ }
168
+
169
+ for (i = nevents; --i >= 0; event++)
170
+ yield_kevent(event);
171
+
172
+ return INT2NUM(nevents);
173
+ }
174
+
175
+ /*
176
+ * returns true if we were interrupted by a signal and resumable,
177
+ * updating the timeout timespec with the remaining time if needed.
178
+ */
179
+ static int
180
+ kevent_resume_p(struct timespec *expire_at, struct kq_per_thread *kpt)
181
+ {
182
+ struct timespec now;
183
+
184
+ kq_fd_check(kpt); /* may raise IOError */
185
+
186
+ if (errno != EINTR)
187
+ return 0;
188
+
189
+ /*
190
+ * kevent is not interruptible until changes are sent,
191
+ * so if we got here, we already got our changes in
192
+ */
193
+ kpt->nchanges = 0;
194
+
195
+ /* we're waiting forever */
196
+ if (kpt->ts == NULL)
197
+ return 1;
198
+
199
+ clock_gettime(CLOCK_MONOTONIC, &now);
200
+ if (now.tv_sec > expire_at->tv_sec)
201
+ return 0;
202
+ if (now.tv_sec == expire_at->tv_sec && now.tv_nsec > expire_at->tv_nsec)
203
+ return 0;
204
+
205
+ tssub(expire_at, &now, kpt->ts);
206
+ return 1;
207
+ }
208
+
209
+ static VALUE nogvl_kevent(void *args)
210
+ {
211
+ struct kq_per_thread *kpt = args;
212
+ int nevents = kevent(kpt->fd, kpt->events, kpt->nchanges,
213
+ kpt->events, kpt->nevents, kpt->ts);
214
+
215
+ return (VALUE)nevents;
216
+ }
217
+
218
+ static VALUE do_kevent(struct kq_per_thread *kpt)
219
+ {
220
+ long nevents;
221
+ struct timespec expire_at;
222
+
223
+ if (kpt->ts) {
224
+ clock_gettime(CLOCK_MONOTONIC, &expire_at);
225
+
226
+ expire_at.tv_sec += kpt->ts->tv_sec;
227
+ expire_at.tv_nsec += kpt->ts->tv_nsec;
228
+ if (expire_at.tv_nsec > NANO_PER_SEC) {
229
+ expire_at.tv_sec++;
230
+ expire_at.tv_nsec -= NANO_PER_SEC;
231
+ }
232
+ }
233
+
234
+ do {
235
+ nevents = (long)rb_sp_fd_region(nogvl_kevent, kpt, kpt->fd);
236
+ } while (nevents < 0 && kevent_resume_p(&expire_at, kpt));
237
+
238
+ return kevent_result(kpt, (int)nevents);
239
+ }
240
+
241
+ static void event_set(struct kevent *event, VALUE *chg)
242
+ {
243
+ uintptr_t ident = (uintptr_t)NUM2ULONG(chg[0]);
244
+ short filter = NUM2SHORT(chg[1]);
245
+ unsigned short flags = NUM2USHORT(chg[2]);
246
+ unsigned fflags = (unsigned)NUM2UINT(chg[3]);
247
+ intptr_t data = (intptr_t)NUM2LONG(chg[4]);
248
+ void *udata = (void *)chg[5];
249
+
250
+ EV_SET(event, ident, filter, flags, fflags, data, udata);
251
+ }
252
+
253
+ /* sets ptr and len */
254
+ static void unpack_event(VALUE **ptr, VALUE *len, VALUE *event)
255
+ {
256
+ switch (TYPE(*event)) {
257
+ case T_STRUCT:
258
+ if (RBX_STRUCT) {
259
+ *event = rb_funcall(*event, rb_intern("to_a"), 0, 0);
260
+ /* fall-through to T_ARRAY */
261
+ } else {
262
+ *len = RSTRUCT_LEN(*event);
263
+ *ptr = RSTRUCT_PTR(*event);
264
+ return;
265
+ }
266
+ case T_ARRAY:
267
+ *len = RARRAY_LEN(*event);
268
+ *ptr = RARRAY_PTR(*event);
269
+ return;
270
+ default:
271
+ rb_raise(rb_eTypeError, "unsupported type in changelist");
272
+ }
273
+ }
274
+
275
+ static void ary2eventlist(struct kevent *events, VALUE changelist)
276
+ {
277
+ VALUE *chg = RARRAY_PTR(changelist);
278
+ long i = RARRAY_LEN(changelist);
279
+ VALUE event;
280
+
281
+ for (; --i >= 0; chg++) {
282
+ VALUE clen;
283
+ VALUE *cptr;
284
+
285
+ event = *chg;
286
+ unpack_event(&cptr, &clen, &event);
287
+ if (clen != 6)
288
+ goto out_list;
289
+ event_set(events++, cptr);
290
+ }
291
+ return;
292
+ out_list:
293
+ rb_raise(rb_eTypeError,
294
+ "changelist must be an array of 6-element arrays or structs");
295
+ }
296
+
297
+ /*
298
+ * Convert an Ruby representation of the changelist to "struct kevent"
299
+ */
300
+ static void changelist_prepare(struct kevent *events, VALUE changelist)
301
+ {
302
+ VALUE *cptr;
303
+ VALUE clen;
304
+ VALUE event;
305
+
306
+ switch (TYPE(changelist)) {
307
+ case T_ARRAY:
308
+ ary2eventlist(events, changelist);
309
+ return;
310
+ case T_STRUCT:
311
+ event = changelist;
312
+ unpack_event(&cptr, &clen, &event);
313
+ if (clen != 6)
314
+ rb_raise(rb_eTypeError, "event is not a Kevent struct");
315
+ event_set(events, cptr);
316
+ return;
317
+ default:
318
+ rb_bug("changelist_prepare not type filtered by sp_kevent");
319
+ }
320
+ }
321
+
322
+ /*
323
+ * call-seq:
324
+ * kq_io.kevent([changelist[, nevents[, timeout]]]) { |ident,filter,flags,fflags,data,udata| ... }
325
+ *
326
+ * This is a wrapper around the kevent(2) system call to change and/or
327
+ * retrieve events from the underlying kqueue descriptor.
328
+ *
329
+ * +changelist+ may be nil, a single Kevent struct or an array of Kevent
330
+ * structs. If +changelist+ is nil, no changes will be made to the
331
+ * underlying kqueue object.
332
+ *
333
+ * +nevents+ may be non-negative integer or nil. If +nevents+ is zero or
334
+ * nil, no events are retrieved. If +nevents+ is positive, a block must
335
+ * be passed to kevent for each event.
336
+ *
337
+ * +timeout+ is the numeric timeout in seconds to wait for +nevents+.
338
+ * If nil and +nevents+ is positive, kevent will sleep forever.
339
+ * +timeout+ may be in a floating point number if subsecond resolution
340
+ * is required. If +nevents+ is nil or zero and +timeout+ is not specified,
341
+ * +timeout+ is implied to be zero.
342
+ *
343
+ * If event retrieval is desired, a block taking 6-elements (one for each
344
+ * field of the kevent struct) must be passed.
345
+ */
346
+ static VALUE sp_kevent(int argc, VALUE *argv, VALUE self)
347
+ {
348
+ struct timespec ts;
349
+ VALUE changelist, events, timeout;
350
+ struct kq_per_thread *kpt;
351
+ int nchanges, nevents;
352
+
353
+ rb_scan_args(argc, argv, "03", &changelist, &events, &timeout);
354
+
355
+ switch (TYPE(changelist)) {
356
+ case T_NIL: nchanges = 0; break;
357
+ case T_STRUCT: nchanges = 1; break;
358
+ case T_ARRAY: nchanges = RARRAY_LENINT(changelist); break;
359
+ default:
360
+ rb_raise(rb_eTypeError, "unhandled type for kevent changelist");
361
+ }
362
+
363
+ if (rb_block_given_p()) {
364
+ if (NIL_P(events))
365
+ rb_raise(rb_eArgError,
366
+ "block given but nevents not specified");
367
+ nevents = NUM2INT(events);
368
+ if (nevents < 0)
369
+ rb_raise(rb_eArgError, "nevents must be non-negative");
370
+ } else {
371
+ if (!NIL_P(events))
372
+ rb_raise(rb_eArgError,
373
+ "nevents specified but block not given");
374
+ nevents = 0;
375
+ }
376
+
377
+ kpt = kpt_get(self, nchanges, nevents);
378
+ kpt->ts = NIL_P(timeout) ? NULL : value2timespec(&ts, timeout);
379
+ if (nchanges)
380
+ changelist_prepare(kpt->events, changelist);
381
+
382
+ return do_kevent(kpt);
383
+ }
384
+
385
+ /* initialize constants in the SleepyPenguin::Ev namespace */
386
+ static void init_ev(VALUE mSleepyPenguin)
387
+ {
388
+ /*
389
+ * Document-module: SleepyPenguin::Ev
390
+ *
391
+ * Constants in the SleepyPenguin::Ev namespace are for the +flags+
392
+ * field in Kevent structs.
393
+ */
394
+ mEv = rb_define_module_under(mSleepyPenguin, "Ev");
395
+
396
+ /* See EV_ADD in the kevent(2) man page */
397
+ rb_define_const(mEv, "ADD", UINT2NUM(EV_ADD));
398
+
399
+ /* See EV_ENABLE in the kevent(2) man page */
400
+ rb_define_const(mEv, "ENABLE", UINT2NUM(EV_ENABLE));
401
+
402
+ /* See EV_DISABLE in the kevent(2) man page */
403
+ rb_define_const(mEv, "DISABLE", UINT2NUM(EV_DISABLE));
404
+
405
+ /* See EV_DISPATCH in the kevent(2) man page */
406
+ rb_define_const(mEv, "DISPATCH", UINT2NUM(EV_DISPATCH));
407
+
408
+ /* See EV_DELETE in the kevent(2) man page */
409
+ rb_define_const(mEv, "DELETE", UINT2NUM(EV_DELETE));
410
+
411
+ /* See EV_RECEIPT in the kevent(2) man page */
412
+ rb_define_const(mEv, "RECEIPT", UINT2NUM(EV_RECEIPT));
413
+
414
+ /* See EV_ONESHOT in the kevent(2) man page */
415
+ rb_define_const(mEv, "ONESHOT", UINT2NUM(EV_ONESHOT));
416
+
417
+ /* See EV_CLEAR in the kevent(2) man page */
418
+ rb_define_const(mEv, "CLEAR", UINT2NUM(EV_CLEAR));
419
+
420
+ /* See EV_EOF in the kevent(2) man page */
421
+ rb_define_const(mEv, "EOF", UINT2NUM(EV_EOF));
422
+
423
+ /* This is a return value in the proc passed to kevent */
424
+ rb_define_const(mEv, "ERROR", UINT2NUM(EV_ERROR));
425
+ }
426
+
427
+ /* initialize constants in the SleepyPenguin::EvFilt namespace */
428
+ static void init_evfilt(VALUE mSleepyPenguin)
429
+ {
430
+ /*
431
+ * Document-module: SleepyPenguin::EvFilt
432
+ *
433
+ * Pre-defined system filters for Kqueue events. Not all filters
434
+ * are supported on all platforms. Consult the kevent(2) man page
435
+ * and source code for your operating system for more information.
436
+ */
437
+ mEvFilt = rb_define_module_under(mSleepyPenguin, "EvFilt");
438
+
439
+ /* See EVFILT_READ in the kevent(2) man page */
440
+ rb_define_const(mEvFilt, "READ", INT2NUM(EVFILT_READ));
441
+
442
+ /* See EVFILT_WRITE in the kevent(2) man page */
443
+ rb_define_const(mEvFilt, "WRITE", INT2NUM(EVFILT_WRITE));
444
+
445
+ /*
446
+ * See EVFILT_AIO in the kevent(2) man page, not supported by libkqueue
447
+ */
448
+ rb_define_const(mEvFilt, "AIO", INT2NUM(EVFILT_AIO));
449
+
450
+ /* See EVFILT_VNODE in the kevent(2) man page */
451
+ rb_define_const(mEvFilt, "VNODE", INT2NUM(EVFILT_VNODE));
452
+
453
+ #ifdef EVFILT_PROC
454
+ /* Monitor process IDs, not supported by libkqueue */
455
+ rb_define_const(mEvFilt, "PROC", INT2NUM(EVFILT_PROC));
456
+ #endif
457
+
458
+ /*
459
+ * Note: the use of EvFilt::SIGNAL is NOT supported in Ruby
460
+ * Ruby runtimes already manage all signal handling in the process,
461
+ * so attempting to manage them with a kqueue causes conflicts.
462
+ * We disable the Linux SignalFD interface for the same reason.
463
+ */
464
+ rb_define_const(mEvFilt, "SIGNAL", INT2NUM(EVFILT_SIGNAL));
465
+
466
+ /* See EVFILT_TIMER in the kevent(2) man page */
467
+ rb_define_const(mEvFilt, "TIMER", INT2NUM(EVFILT_TIMER));
468
+
469
+ #ifdef EVFILT_NETDEV
470
+ /* network devices, no longer supported */
471
+ rb_define_const(mEvFilt, "NETDEV", INT2NUM(EVFILT_NETDEV));
472
+ #endif
473
+
474
+ #ifdef EVFILT_FS
475
+ /*
476
+ * See EVFILT_FS in the kevent(2) man page,
477
+ * not supported by libkqueue
478
+ */
479
+ rb_define_const(mEvFilt, "FS", INT2NUM(EVFILT_FS));
480
+ #endif
481
+
482
+ #ifdef EVFILT_LIO
483
+ /* attached to lio requests, not supported by libkqueue */
484
+ rb_define_const(mEvFilt, "LIO", INT2NUM(EVFILT_LIO));
485
+ #endif
486
+
487
+ /* see EVFILT_USER in the kevent(2) man page */
488
+ rb_define_const(mEvFilt, "USER", INT2NUM(EVFILT_USER));
489
+ }
490
+
491
+ /* initialize constants in the SleepyPenguin::Note namespace */
492
+ static void init_note(VALUE mSleepyPenguin)
493
+ {
494
+ /*
495
+ * Document-module: SleepyPenguin::Note
496
+ *
497
+ * Data/hint flags/masks for EVFILT_USER and friends in Kqueue
498
+ * On input, the top two bits of fflags specifies how the lower
499
+ * twenty four bits should be applied to the stored value of fflags.
500
+ *
501
+ * On output, the top two bits will always be set to Note::FFNOP
502
+ * and the remaining twenty four bits will contain the stored
503
+ * fflags value.
504
+ */
505
+ mNote = rb_define_module_under(mSleepyPenguin, "Note");
506
+
507
+ /* ignore input fflags */
508
+ rb_define_const(mNote, "FFNOP", UINT2NUM(NOTE_FFNOP));
509
+
510
+ /* bitwise AND fflags */
511
+ rb_define_const(mNote, "FFAND", UINT2NUM(NOTE_FFAND));
512
+
513
+ /* bitwise OR fflags */
514
+ rb_define_const(mNote, "FFOR", UINT2NUM(NOTE_FFOR));
515
+
516
+ /* copy fflags */
517
+ rb_define_const(mNote, "FFCOPY", UINT2NUM(NOTE_FFCOPY));
518
+
519
+ /* control mask for fflags */
520
+ rb_define_const(mNote, "FFCTRLMASK", UINT2NUM(NOTE_FFCTRLMASK));
521
+
522
+ /* user-defined flag mask for fflags */
523
+ rb_define_const(mNote, "FFLAGSMASK", UINT2NUM(NOTE_FFLAGSMASK));
524
+
525
+ /* Cause the event to be triggered for output */
526
+ rb_define_const(mNote, "TRIGGER", UINT2NUM(NOTE_TRIGGER));
527
+
528
+ #ifdef NOTE_LOWAT
529
+ /*
530
+ * data/hint flags for EVFILT_{READ|WRITE}, shared with userspace
531
+ * Not supported by libkqueue in Linux
532
+ */
533
+ rb_define_const(mNote, "LOWAT", UINT2NUM(NOTE_LOWAT));
534
+ #endif
535
+
536
+ #ifdef EVFILT_VNODE
537
+ /* vnode was removed */
538
+ rb_define_const(mNote, "DELETE", UINT2NUM(NOTE_DELETE));
539
+
540
+ /* vnode data contents changed */
541
+ rb_define_const(mNote, "WRITE", UINT2NUM(NOTE_WRITE));
542
+
543
+ /* vnode size increased */
544
+ rb_define_const(mNote, "EXTEND", UINT2NUM(NOTE_EXTEND));
545
+
546
+ /* vnode attributes changes */
547
+ rb_define_const(mNote, "ATTRIB", UINT2NUM(NOTE_ATTRIB));
548
+
549
+ /* vnode link count changed */
550
+ rb_define_const(mNote, "LINK", UINT2NUM(NOTE_LINK));
551
+
552
+ /* vnode was renamed */
553
+ rb_define_const(mNote, "RENAME", UINT2NUM(NOTE_RENAME));
554
+
555
+ # ifdef NOTE_REVOKE
556
+ /* vnode access was revoked, not supported on Linux */
557
+ rb_define_const(mNote, "REVOKE", UINT2NUM(NOTE_REVOKE));
558
+ # endif
559
+ #endif /* EVFILT_VNODE */
560
+
561
+ #ifdef EVFILT_PROC
562
+ /* process exited */
563
+ rb_define_const(mNote, "EXIT", UINT2NUM(NOTE_EXIT));
564
+
565
+ /* process forked */
566
+ rb_define_const(mNote, "FORK", UINT2NUM(NOTE_FORK));
567
+
568
+ /* process exec'd */
569
+ rb_define_const(mNote, "EXEC", UINT2NUM(NOTE_EXEC));
570
+
571
+ /* mask for hint bits */
572
+ rb_define_const(mNote, "PCTRLMASK", UINT2NUM(NOTE_PCTRLMASK));
573
+
574
+ /* mask for pid */
575
+ rb_define_const(mNote, "PDATAMASK", UINT2NUM(NOTE_PDATAMASK));
576
+
577
+ /* follow across forks */
578
+ rb_define_const(mNote, "TRACK", UINT2NUM(NOTE_TRACK));
579
+
580
+ /* could not track child */
581
+ rb_define_const(mNote, "TRACKERR", UINT2NUM(NOTE_TRACKERR));
582
+
583
+ /* am a child process */
584
+ rb_define_const(mNote, "CHILD", UINT2NUM(NOTE_CHILD));
585
+ #endif /* EVFILT_PROC */
586
+
587
+ #ifdef EVFILT_NETDEV
588
+ /* link is up */
589
+ rb_define_const(mNote, "LINKUP", UINT2NUM(NOTE_LINKUP));
590
+
591
+ /* link is down */
592
+ rb_define_const(mNote, "LINKDOWN", UINT2NUM(NOTE_LINKDOWN));
593
+
594
+ /* link state is valid */
595
+ rb_define_const(mNote, "LINKINV", UINT2NUM(NOTE_LINKINV));
596
+ #endif /* EVFILT_NETDEV */
597
+ }
598
+
599
+ static void init_vq(VALUE mSleepyPenguin)
600
+ {
601
+ #ifdef VQ_NOTRESP
602
+ /*
603
+ * Document-module: SleepyPenguin::VQ
604
+ *
605
+ * Constants used by the EvFilt::FS filter in the Kqueue interfaces
606
+ */
607
+ mVQ = rb_define_module_under(mSleepyPenguin, "VQ");
608
+
609
+ /* server down */
610
+ rb_define_const(mVQ, "NOTRESP", UINT2NUM(VQ_NOTRESP));
611
+
612
+ /* server bad auth */
613
+ rb_define_const(mVQ, "NEEDAUTH", UINT2NUM(VQ_NEEDAUTH));
614
+
615
+ /* low on space */
616
+ rb_define_const(mVQ, "LOWDISK", UINT2NUM(VQ_LOWDISK));
617
+
618
+ /* new filesystem mounted */
619
+ rb_define_const(mVQ, "MOUNT", UINT2NUM(VQ_MOUNT));
620
+
621
+ /* filesystem unmounted */
622
+ rb_define_const(mVQ, "UNMOUNT", UINT2NUM(VQ_UNMOUNT));
623
+
624
+ /* filesystem dead, needs force unmount */
625
+ rb_define_const(mVQ, "DEAD", UINT2NUM(VQ_DEAD));
626
+
627
+ /* filesystem needs assistance from external program */
628
+ rb_define_const(mVQ, "ASSIST", UINT2NUM(VQ_ASSIST));
629
+
630
+ /* server lockd down */
631
+ rb_define_const(mVQ, "NOTRESPLOCK", UINT2NUM(VQ_NOTRESPLOCK));
632
+ #endif /* VQ_NOTRESP */
633
+ }
634
+
635
+ void sleepy_penguin_init_kqueue(void)
636
+ {
637
+ VALUE mSleepyPenguin, cKqueue, cKqueue_IO;
638
+
639
+ mSleepyPenguin = rb_define_module("SleepyPenguin");
640
+ init_ev(mSleepyPenguin);
641
+ init_evfilt(mSleepyPenguin);
642
+ init_note(mSleepyPenguin);
643
+ init_vq(mSleepyPenguin);
644
+
645
+ cKqueue = rb_define_class_under(mSleepyPenguin, "Kqueue", rb_cObject);
646
+
647
+ /*
648
+ * Document-class: SleepyPenguin::Kqueue::IO
649
+ *
650
+ * Kqueue::IO is a low-level class. It does not provide fork nor
651
+ * GC-safety, so Ruby IO objects added via kevent must be retained
652
+ * by the application until IO#close is called.
653
+ *
654
+ * Warning: this class is easy to misuse, be careful as failure
655
+ * to preserve references objects passed as Kevent#udata may lead
656
+ * to crashes in Ruby. The high-level Kqueue class prevents these
657
+ * crashes (but may still return invalid objects).
658
+ */
659
+ cKqueue_IO = rb_define_class_under(cKqueue, "IO", rb_cIO);
660
+ rb_define_singleton_method(cKqueue_IO, "new", s_new, 0);
661
+
662
+ rb_define_method(cKqueue_IO, "kevent", sp_kevent, -1);
663
+
664
+ id_for_fd = rb_intern("for_fd");
665
+
666
+ if (RB_SP_GREEN_THREAD)
667
+ rb_require("sleepy_penguin/kqueue/io");
668
+
669
+ /* the high-level interface is implemented in Ruby: */
670
+ rb_require("sleepy_penguin/kqueue");
671
+
672
+ /* Kevent helper struct */
673
+ rb_require("sleepy_penguin/kevent");
674
+ }
675
+ #endif /* HAVE_SYS_EVENT_H */