diff --git a/ext/libev/ev.c b/ext/libev/ev.c index e5bd5ab..10f6ff2 100644 --- a/ext/libev/ev.c +++ b/ext/libev/ev.c @@ -37,6 +37,10 @@ * either the BSD or the GPL. */ +/* ########## COOLIO PATCHERY HO! ########## */ +#include "ruby.h" +/* ######################################## */ + /* this big block deduces configuration from config.h */ #ifndef EV_STANDALONE # ifdef EV_CONFIG_H @@ -3237,9 +3241,27 @@ time_update (EV_P_ ev_tstamp max_block) } } +/* ########## COOLIO PATCHERY HO! ########## */ +#if defined(HAVE_RB_THREAD_BLOCKING_REGION) +static +VALUE ev_backend_poll(void **args) +{ + struct ev_loop *loop = (struct ev_loop *)args[0]; + ev_tstamp waittime = *(ev_tstamp *)args[1]; + backend_poll (EV_A_ waittime); +} +#endif +/* ######################################## */ + int ev_run (EV_P_ int flags) { +/* ########## COOLIO PATCHERY HO! ########## */ +#if defined(HAVE_RB_THREAD_BLOCKING_REGION) + void *poll_args[2]; +#endif +/* ######################################## */ + #if EV_FEATURE_API ++loop_depth; #endif @@ -3357,7 +3379,53 @@ ev_run (EV_P_ int flags) ++loop_count; #endif assert ((loop_done = EVBREAK_RECURSE, 1)); /* assert for side effect */ + +/* +########################## COOLIO PATCHERY HO! ########################## + +The original patch file is made by Tony Arcieri. +https://github.com/celluloid/nio4r/blob/680143345726c5a64bb22376ca8fc3c6857019ae/ext/libev/ruby_gil.patch. + +According to the grandwizards of Ruby, locking and unlocking of the global +interpreter lock are apparently too powerful a concept for a mere mortal to +wield (although redefining what + and - do to numbers is totally cool). +And so it came to pass that the only acceptable way to release the global +interpreter lock is through a convoluted callback system that thakes a +function pointer. While the grandwizard of libev foresaw this sort of scenario, +he too attempted to place an API with callbacks on it, one that runs before +the system call, and one that runs immediately after. + +And so it came to pass that trying to wrap everything up in callbacks created +two incompatible APIs, Ruby's which releases the global interpreter lock and +reacquires it when the callback returns, and libev's, which wants two +callbacks, one which runs before the polling operation starts, and one which +runs after it finishes. + +These two systems are incompatible as they both want to use callbacks to +solve the same problem, however libev wants to use before/after callbacks, +and Ruby wants to use an "around" callback. This presents a significant +problem as these two patterns of callbacks are diametrical opposites of each +other and thus cannot be composed. + +And thus we are left with no choice but to patch the internals of libev in +order to release a mutex at just the precise moment. + +Let this be a lesson to the all: CALLBACKS FUCKING BLOW + +####################################################################### +*/ + +#if defined(HAVE_RB_THREAD_BLOCKING_REGION) + poll_args[0] = (void *)loop; + poll_args[1] = (void *)&waittime; + rb_thread_blocking_region(ev_backend_poll, (void *)&poll_args, RUBY_UBF_IO, 0); +#else backend_poll (EV_A_ waittime); +#endif +/* +############################# END PATCHERY ############################ +*/ + assert ((loop_done = EVBREAK_CANCEL, 1)); /* assert for side effect */ pipe_write_wanted = 0; /* just an optimisation, no fence needed */