ext/rev/rev_loop.c in rev-0.1.4 vs ext/rev/rev_loop.c in rev-0.2.0

- old
+ new

@@ -10,26 +10,23 @@ #define EV_STANDALONE 1 #include "../libev/ev.h" #include "rev.h" -/* Module and object handles */ static VALUE mRev = Qnil; static VALUE cRev_Loop = Qnil; -/* Data allocators and deallocators */ static VALUE Rev_Loop_allocate(VALUE klass); static void Rev_Loop_mark(struct Rev_Loop *loop); static void Rev_Loop_free(struct Rev_Loop *loop); -/* Method implementations */ static VALUE Rev_Loop_initialize(VALUE self); static VALUE Rev_Loop_ev_loop_new(VALUE self, VALUE flags); static VALUE Rev_Loop_run_once(VALUE self); -static VALUE Rev_Loop_run_once_blocking(void *ptr); static VALUE Rev_Loop_run_nonblock(VALUE self); +static void Rev_Loop_ev_loop_oneshot(struct Rev_Loop *loop_data); static void Rev_Loop_dispatch_events(struct Rev_Loop *loop_data); #define DEFAULT_EVENTBUF_SIZE 32 /* @@ -182,25 +179,70 @@ rb_raise(rb_eRuntimeError, "cannot run loop from within a callback"); assert(loop_data->ev_loop && !loop_data->events_received); loop_data->running = 1; - rb_thread_blocking_region(Rev_Loop_run_once_blocking, loop_data->ev_loop, RB_UBF_DFL, 0); + + Rev_Loop_ev_loop_oneshot(loop_data); Rev_Loop_dispatch_events(loop_data); loop_data->events_received = 0; + loop_data->running = 0; return Qnil; } -static VALUE Rev_Loop_run_once_blocking(void *ptr) +/* Ruby 1.9 supports blocking system calls through rb_thread_blocking_region() */ +#ifdef HAVE_RB_THREAD_BLOCKING_REGION +#define HAVE_EV_LOOP_ONESHOT +static VALUE Rev_Loop_ev_loop_oneshot_blocking(void *ptr) { /* The libev loop has now escaped through the Global VM Lock unscathed! */ struct ev_loop *loop = (struct ev_loop *)ptr; ev_loop(loop, EVLOOP_ONESHOT); return Qnil; } + +static void Rev_Loop_ev_loop_oneshot(struct Rev_Loop *loop_data) +{ + /* Use Ruby 1.9's rb_thread_blocking_region call to make a blocking system call */ + rb_thread_blocking_region(Rev_Loop_ev_loop_oneshot_blocking, loop_data->ev_loop, RB_UBF_DFL, 0); +} +#endif + +/* Ruby 1.8 requires us to periodically run the event loop then defer back to + * the green threads scheduler */ +#ifndef HAVE_EV_LOOP_ONESHOT +#define BLOCKING_INTERVAL 0.01 /* Block for 10ms at a time */ + +/* Stub callback */ +static void timer_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents) +{ +} + +static void Rev_Loop_ev_loop_oneshot(struct Rev_Loop *loop_data) +{ + struct ev_timer timer; + struct timeval tv; + + /* Set up an ev_timer to unblock the loop every 10ms */ + ev_timer_init(&timer, timer_callback, BLOCKING_INTERVAL, BLOCKING_INTERVAL); + ev_timer_start(loop_data->ev_loop, &timer); + + do { + /* Since blocking calls would hang the Ruby 1.8 thread scheduler, don't block */ + ev_loop(loop_data->ev_loop, EVLOOP_ONESHOT); + + /* Call rb_thread_select to resume the Ruby scheduler */ + tv.tv_sec = 0; + tv.tv_usec = 0; + rb_thread_select(0, NULL, NULL, NULL, &tv); + } while(!loop_data->events_received); + + ev_timer_stop(loop_data->ev_loop, &timer); +} +#endif /** * call-seq: * Rev::Loop.run_once -> nil *