Class: SleepyPenguin::Kqueue::IO

Inherits:
IO
  • Object
show all
Defined in:
ext/sleepy_penguin/kqueue.c,
ext/sleepy_penguin/kqueue.c

Overview

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).

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.newObject

SleepyPenguin::Kqueue::IO.new -> Kqueue::IO object

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.



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'ext/sleepy_penguin/kqueue.c', line 112

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

Instance Method Details

#kevent(*args) ⇒ Object

kq_io.kevent([changelist[, nevents[, timeout]]]) { |ident,filter,flags,fflags,data,udata| … }

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.



370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
# File 'ext/sleepy_penguin/kqueue.c', line 370

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