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

- old
+ new

@@ -4,10 +4,11 @@ * See LICENSE for details */ #include <assert.h> #include "ruby.h" +#include "rubysig.h" #define EV_STANDALONE 1 #include "../libev/ev.h" #include "rev.h" @@ -26,10 +27,14 @@ 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 +#define RUN_LOOP(loop_data, options) \ + loop_data->running = 1; \ + ev_loop(loop_data->ev_loop, options); \ + loop_data->running = 0; /* * Rev::Loop represents an event loop. Event watchers can be attached and * unattached. When an event loop is run, all currently attached watchers * are monitored for events, and their respective callbacks are signaled @@ -50,11 +55,10 @@ static VALUE Rev_Loop_allocate(VALUE klass) { struct Rev_Loop *loop = (struct Rev_Loop *)xmalloc(sizeof(struct Rev_Loop)); loop->ev_loop = 0; - loop->running = 0; loop->events_received = 0; loop->eventbuf_size = DEFAULT_EVENTBUF_SIZE; loop->eventbuf = (struct Rev_Event *)xmalloc(sizeof(struct Rev_Event) * DEFAULT_EVENTBUF_SIZE); @@ -171,103 +175,100 @@ * Run the Rev::Loop once, blocking until events are received. */ static VALUE Rev_Loop_run_once(VALUE self) { struct Rev_Loop *loop_data; + VALUE nevents; + Data_Get_Struct(self, struct Rev_Loop, loop_data); - if(loop_data->running) - rb_raise(rb_eRuntimeError, "cannot run loop from within a callback"); - assert(loop_data->ev_loop && !loop_data->events_received); - - loop_data->running = 1; - - Rev_Loop_ev_loop_oneshot(loop_data); + + Rev_Loop_ev_loop_oneshot(loop_data); Rev_Loop_dispatch_events(loop_data); + + nevents = INT2NUM(loop_data->events_received); loop_data->events_received = 0; - - loop_data->running = 0; - - return Qnil; + + return nevents; } /* 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; + struct Rev_Loop *loop_data = (struct Rev_Loop *)ptr; - ev_loop(loop, EVLOOP_ONESHOT); + RUN_LOOP(loop_data, 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); + rb_thread_blocking_region(Rev_Loop_ev_loop_oneshot_blocking, loop_data, 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 */ +/* Stub for scheduler's ev_timer callback */ static void timer_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents) { } +/* Run the event loop, calling rb_thread_schedule every 10ms */ 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); + /* Loop until we receive events */ + while(!loop_data->events_received) { + TRAP_BEG; + RUN_LOOP(loop_data, EVLOOP_ONESHOT); + TRAP_END; - /* 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); + rb_thread_schedule(); + } ev_timer_stop(loop_data->ev_loop, &timer); } #endif /** * call-seq: - * Rev::Loop.run_once -> nil + * Rev::Loop.run_nonblock -> nil * * Run the Rev::Loop once, but return immediately if there are no pending events. */ static VALUE Rev_Loop_run_nonblock(VALUE self) { struct Rev_Loop *loop_data; + VALUE nevents; + Data_Get_Struct(self, struct Rev_Loop, loop_data); - if(loop_data->running) - rb_raise(rb_eRuntimeError, "cannot run loop from within a callback"); - assert(loop_data->ev_loop && !loop_data->events_received); - loop_data->running = 1; - ev_loop(loop_data->ev_loop, EVLOOP_NONBLOCK); + RUN_LOOP(loop_data, EVLOOP_NONBLOCK); Rev_Loop_dispatch_events(loop_data); + + nevents = INT2NUM(loop_data->events_received); loop_data->events_received = 0; - loop_data->running = 0; - - return Qnil; + + return nevents; } static void Rev_Loop_dispatch_events(struct Rev_Loop *loop_data) { int i; @@ -281,6 +282,6 @@ continue; Data_Get_Struct(loop_data->eventbuf[i].watcher, struct Rev_Watcher, watcher_data); watcher_data->dispatch_callback(loop_data->eventbuf[i].watcher, loop_data->eventbuf[i].revents); } -} +} \ No newline at end of file