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