class SleepyPenguin::Kqueue::IO
Kqueue::IO
is a low-level class. It does not provide fork nor GC-safety, so Ruby IO
objects added via kevent must be retained by the application until IO#close is called.
Warning: this class is easy to misuse, be careful as failure to preserve references objects passed as Kevent#udata may lead to crashes in Ruby. The high-level Kqueue
class prevents these crashes (but may still return invalid objects).
Public Class Methods
Creates a new Kqueue::IO
object. This is a wrapper around the kqueue(2) system call which creates a Ruby IO
object around the kqueue descriptor.
kqueue descriptors are automatically invalidated by the OS across fork, so care must be taken when forking. Setting IO#autoclose=false is recommended for applications which fork after kqueue creation.
static VALUE s_new(VALUE klass) { VALUE rv; int fd = kqueue(); int flags; if (fd < 0) { /* * ENOMEM/EMFILE/ENFILE are the only documented errors * for kqueue(), hope GC can give us some space to retry: */ rb_gc(); fd = kqueue(); if (fd < 0) rb_sys_fail("kqueue"); } flags = fcntl(fd, F_GETFD); if (flags != -1) fcntl(fd, F_SETFD, flags | FD_CLOEXEC); rv = INT2FIX(fd); return rb_call_super(1, &rv); }
Public Instance Methods
This is a wrapper around the kevent(2) system call to change and/or retrieve events from the underlying kqueue descriptor.
changelist
may be nil, a single Kevent struct or an array of Kevent structs. If changelist
is nil, no changes will be made to the underlying kqueue object.
nevents
may be non-negative integer or nil. If nevents
is zero or nil, no events are retrieved. If nevents
is positive, a block must be passed to kevent for each event.
timeout
is the numeric timeout in seconds to wait for nevents
. If nil and nevents
is positive, kevent will sleep forever. timeout
may be in a floating point number if subsecond resolution is required. If nevents
is nil or zero and timeout
is not specified, timeout
is implied to be zero.
If event retrieval is desired, a block taking 6-elements (one for each field of the kevent struct) must be passed.
static VALUE sp_kevent(int argc, VALUE *argv, VALUE self) { struct timespec ts, *t; VALUE changelist, events, timeout; struct kq_per_thread *kpt; int nchanges, nevents; rb_scan_args(argc, argv, "03", &changelist, &events, &timeout); switch (TYPE(changelist)) { case T_NIL: nchanges = 0; break; case T_STRUCT: nchanges = 1; break; case T_ARRAY: nchanges = RARRAY_LENINT(changelist); break; default: rb_raise(rb_eTypeError, "unhandled type for kevent changelist"); } if (rb_block_given_p()) { if (NIL_P(events)) rb_raise(rb_eArgError, "block given but nevents not specified"); nevents = NUM2INT(events); if (nevents < 0) rb_raise(rb_eArgError, "nevents must be non-negative"); } else { if (!NIL_P(events)) rb_raise(rb_eArgError, "nevents specified but block not given"); nevents = 0; } t = NIL_P(timeout) ? NULL : value2timespec(&ts, timeout); kpt = kpt_get(nchanges, nevents); kpt->ts = t; kpt->changelist = changelist; kpt->io = self; kpt->fd = rb_sp_fileno(kpt->io); return rb_ensure(do_kevent, (VALUE)kpt, rb_sp_puttlsbuf, (VALUE)kpt); }