nio4r and the Ruby GIL revisited

Tony Arcieri tony.arcieri at
Fri Mar 2 08:35:32 CET 2012

Hello, you might remember me from Rev/

I'm back with a new libev-based project, called nio4r:

It implements an API somewhat resembling the Java NIO Selector API, using
Ruby classes. However, if you remember from before with Rev/ I ran
into problems with the Ruby global interpreter lock API. Unless I can
unlock the GIL, other Ruby threads can't run while libev is making a
blocking system call.

The function signature is:

    rb_blocking_function_t *func, void *data1,
    rb_unblock_function_t *ubf, void *data2)

Where the argument to note is *func, which Ruby calls after releasing the
global interpreter lock, then after it completes it ensures the lock is
reacquired. The Ruby gods apparently decided it was too dangerous to have
separate lock/unlock functions and only provide this function pointer-based
API. More documentation available here:

After a lot of discussion about the dangers of garbage collection and
having the GIL unlocked, I *really* wanted to limit the exposure of when
the GIL is unlocked to just when libev is making a system call. During the
subsequent callbacks, I really need to have the GIL acquired, as I am
attempting to handle Ruby objects in C code, and at any given point in time
the garbage collector may ask me to mark references to them, so I really
can't be juggling references to them out in C land while another thread is
trying to mark them for garbage collection.

In order to do that I concocted this ugly patch to put the global
interpreter locking/unlocking logic directly into libev:

(I hope you'll take my comments about callbacks with a grain of salt ;)

Is there a better solution here? I would really love to get rid of this
patch and use an unmodified libev, but I can't figure out how to make this
work without patching libev.

Tony Arcieri
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the libev mailing list