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

SleepyPenguin::Kqueue::IO.new → Kqueue::IO object click to toggle source

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

kevent([changelist[, nevents[, timeout]]]) { |ident,filter,flags,fflags,data,udata| ... } click to toggle source

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);
}