sleepy_penguin 3.1.0.26.g7181 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
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 */