ext/asyncengine/ae_timer.c in asyncengine-0.0.1.testing1 vs ext/asyncengine/ae_timer.c in asyncengine-0.0.2.alpha1
- old
+ new
@@ -2,151 +2,425 @@
#include "ae_handle_common.h"
#include "ae_timer.h"
static VALUE cAsyncEngineTimer;
+static VALUE cAsyncEnginePeriodicTimer;
typedef struct {
uv_timer_t *_uv_handle;
int periodic;
- VALUE rb_block_id;
- int has_rb_instance;
- VALUE rb_instance; // Points to the owner AE::Timer (if there is).
-} struct_ae_timer_cdata;
+ long delay;
+ long interval;
+ VALUE ae_handle_id;
+ VALUE on_fire_proc;
+} struct_cdata;
+struct timer_callback_data {
+ struct_cdata* cdata;
+};
+
+// Used for storing information about the last timer callback.
+static struct timer_callback_data last_timer_callback_data;
+
+
+/** Predeclaration of static functions. */
+
+static VALUE AsyncEngineTimer_alloc(VALUE klass);
+static void AsyncEngineTimer_mark(struct_cdata* cdata);
+static void AsyncEngineTimer_free(struct_cdata* cdata);
+static void init_instance(VALUE self, long delay, long interval, VALUE proc);
+static void _uv_timer_callback(uv_timer_t* handle, int status);
+static VALUE _ae_timer_callback(void);
+static void close_handle(struct_cdata *cdata);
+
+
void init_ae_timer()
{
- cAsyncEngineTimer = rb_define_class_under(mAsyncEngine, "Timer", rb_cObject);
- rb_define_module_function(mAsyncEngine, "_c_add_timer", AsyncEngine_c_add_timer, 4);
- rb_define_method(cAsyncEngineTimer, "cancel", AsyncEngineTimer_cancel, 0);
- rb_define_alias(cAsyncEngineTimer, "stop", "cancel");
- rb_define_private_method(cAsyncEngineTimer, "_c_set_interval", AsyncEngineTimer_c_set_interval, 1);
+ AE_TRACE();
+
+ cAsyncEngineTimer = rb_define_class_under(mAsyncEngine, "Timer", cAsyncEngineHandle);
+ cAsyncEnginePeriodicTimer = rb_define_class_under(mAsyncEngine, "PeriodicTimer", cAsyncEngineTimer);
+
+ rb_define_alloc_func(cAsyncEngineTimer, AsyncEngineTimer_alloc);
+
+ rb_define_singleton_method(cAsyncEngineTimer, "new", AsyncEngineTimer_new, -1);
+ rb_define_singleton_method(cAsyncEnginePeriodicTimer, "new", AsyncEnginePeriodicTimer_new, -1);
+
+ rb_define_method(cAsyncEngineTimer, "pause", AsyncEngineTimer_pause, 0);
+ rb_define_method(cAsyncEngineTimer, "restart", AsyncEngineTimer_restart, -1);
+ rb_define_method(cAsyncEnginePeriodicTimer, "restart", AsyncEnginePeriodicTimer_restart, -1);
+ rb_define_method(cAsyncEngineTimer, "alive?", AsyncEngineTimer_is_alive, 0);
+ rb_define_method(cAsyncEngineTimer, "delay", AsyncEngineTimer_delay, 0);
+ rb_define_method(cAsyncEnginePeriodicTimer, "interval", AsyncEnginePeriodicTimer_interval, 0);
+ rb_define_method(cAsyncEngineTimer, "alive?", AsyncEngineTimer_is_alive, 0);
+ rb_define_method(cAsyncEngineTimer, "close", AsyncEngineTimer_close, 0);
+ rb_define_alias(cAsyncEngineTimer, "stop", "close");
+ rb_define_private_method(cAsyncEngineTimer, "destroy", AsyncEngineTimer_destroy, 0);
}
+/** Class alloc, mark and free functions. */
+
static
-void deallocate(struct_ae_timer_cdata* cdata)
+VALUE AsyncEngineTimer_alloc(VALUE klass)
{
AE_TRACE();
- // Let the GC work.
- ae_remove_block(cdata->rb_block_id);
- // Close the timer so it's unreferenced by uv.
- uv_close((uv_handle_t *)cdata->_uv_handle, ae_handle_close_callback_0);
- // Free memory.
+ struct_cdata* cdata = ALLOC(struct_cdata);
+
+ /* IMPORTANT: Set the _uv_handle to NULL right now since GC could
+ * call our mark() function before cdata->on_fire_proc is set.
+ */
+ cdata->_uv_handle = NULL;
+
+ return Data_Wrap_Struct(klass, AsyncEngineTimer_mark, AsyncEngineTimer_free, cdata);
+}
+
+
+static
+void AsyncEngineTimer_mark(struct_cdata* cdata)
+{
+ AE_TRACE();
+
+ // This tells Ruby not to GC cdata->on_fire_proc if the Timer
+ // instance continues alive in Ruby land.
+ if (cdata->_uv_handle)
+ rb_gc_mark(cdata->on_fire_proc);
+}
+
+
+static
+void AsyncEngineTimer_free(struct_cdata* cdata)
+{
+ AE_TRACE();
+
xfree(cdata);
}
+/** Timer.new() method.
+ *
+ * Arguments:
+ * - delay (Float).
+ * - proc (Proc) (optional).
+ *
+ * Block optional.
+ */
+
static
-void execute_timer_with_gvl(uv_timer_t* handle)
+VALUE AsyncEngineTimer_new(int argc, VALUE *argv, VALUE self)
{
AE_TRACE();
+ long delay;
+ VALUE proc, instance;
- struct_ae_timer_cdata* cdata = (struct_ae_timer_cdata*)handle->data;
- VALUE block = ae_get_block(cdata->rb_block_id);
- int exception_tag;
+ AE_CHECK_STATUS();
+ AE_RB_CHECK_NUM_ARGS(1,2);
+ AE_RB_ENSURE_BLOCK_OR_PROC(2, proc);
- exception_tag = ae_protect_block_call_0(block);
+ // Parameter 1: delay.
+ delay = (long)(NUM2DBL(argv[0]) * 1000);
+ if (delay < 1)
+ delay = 1;
- // Terminate the timer if it is not periodic.
- if (cdata->periodic == 0) {
- // If the timer has a ruby AE::Timer instance then set its attribute
- // @_handle_terminated to true.
- if (cdata->has_rb_instance == 1)
- rb_ivar_set(cdata->rb_instance, att_handle_terminated, Qtrue);
- deallocate(cdata);
- }
+ // Allocate the Ruby instance.
+ instance = rb_obj_alloc(self);
- if (exception_tag)
- ae_manage_exception(exception_tag);
+ // Init the UV stuff within the instance.
+ init_instance(instance, delay, 0, proc);
+
+ return instance;
}
+/** PeriodicTimer.new() method.
+ *
+ * Arguments:
+ * - interval (Float).
+ * - delay (Float) (optional).
+ * - proc (Proc) (optional).
+ *
+ * Block optional.
+ */
+
static
-void timer_callback(uv_timer_t* handle, int status)
+VALUE AsyncEnginePeriodicTimer_new(int argc, VALUE *argv, VALUE self)
{
AE_TRACE();
- rb_thread_call_with_gvl(execute_timer_with_gvl, handle);
+ long interval, delay;
+ VALUE proc, instance;
+
+ AE_CHECK_STATUS();
+ AE_RB_CHECK_NUM_ARGS(1,3);
+ AE_RB_ENSURE_BLOCK_OR_PROC(3, proc);
+
+ // Parameter 1: interval.
+ interval = (long)(NUM2DBL(argv[0]) * 1000);
+ if (interval < 1)
+ interval = 1;
+
+ // Parameter 2: delay (optional).
+ if (argc >= 2 && ! NIL_P(argv[1])) {
+ delay = (long)(NUM2DBL(argv[1]) * 1000);
+ if (delay < 1)
+ delay = 1;
+ }
+ else
+ delay = interval;
+
+ // Allocate the Ruby instance.
+ instance = rb_obj_alloc(self);
+
+ // Init the UV stuff within the instance.
+ init_instance(instance, delay, interval, proc);
+
+ return instance;
}
-VALUE AsyncEngine_c_add_timer(VALUE self, VALUE rb_delay, VALUE rb_interval, VALUE block, VALUE instance)
+static
+void init_instance(VALUE self, long delay, long interval, VALUE proc)
{
AE_TRACE();
- uv_timer_t* _uv_handle = ALLOC(uv_timer_t);
- struct_ae_timer_cdata* cdata = ALLOC(struct_ae_timer_cdata);
- long delay, interval;
+ uv_timer_t *_uv_handle;
+ int r;
- cdata->_uv_handle = _uv_handle;
+ // Create and init the UV handle.
+ _uv_handle = ALLOC(uv_timer_t);
+ r = uv_timer_init(AE_uv_loop, _uv_handle);
+ if (r != 0) {
+ xfree(_uv_handle);
+ ae_raise_last_uv_error();
+ }
- delay = NUM2LONG(rb_delay);
- if (NIL_P(rb_interval)) {
- interval = 0;
+ // Fill cdata struct.
+ GET_CDATA_FROM_SELF;
+ cdata->_uv_handle = _uv_handle;
+ cdata->delay = delay;
+ cdata->interval = interval;
+ if (interval)
+ cdata->periodic = 1;
+ else
cdata->periodic = 0;
+ cdata->on_fire_proc = proc;
+ cdata->ae_handle_id = ae_store_handle(self); // Avoid GC.
+
+ // Fill data field of the UV handle.
+ cdata->_uv_handle->data = (void *)cdata;
+
+ // Run the timer.
+ r = uv_timer_start(_uv_handle, _uv_timer_callback, delay, interval);
+ if (r != 0) {
+ close_handle(cdata);
+ ae_raise_last_uv_error();
}
- else {
- interval = NUM2LONG(rb_interval);
- if (interval == 0) interval = 1;
- cdata->periodic = 1;
+}
+
+
+static
+void _uv_timer_callback(uv_timer_t* handle, int status)
+{
+ AE_TRACE();
+
+ last_timer_callback_data.cdata = (struct_cdata*)handle->data;
+ ae_take_gvl_and_run_with_error_handler(_ae_timer_callback);
+}
+
+
+static
+VALUE _ae_timer_callback(void)
+{
+ AE_TRACE();
+
+ struct_cdata* cdata = last_timer_callback_data.cdata;
+
+ // Terminate the timer if it is not periodic.
+ if (cdata->periodic == 0)
+ close_handle(cdata);
+
+ return ae_proc_call_0(cdata->on_fire_proc);
+}
+
+
+/** Timer#pause() method. */
+
+static
+VALUE AsyncEngineTimer_pause(VALUE self)
+{
+ AE_TRACE();
+ int r;
+
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
+
+ r = uv_timer_stop(cdata->_uv_handle);
+ if (r != 0)
+ ae_raise_last_uv_error();
+
+ return Qtrue;
+}
+
+
+/**
+ * Timer#restart() method.
+ *
+ * Arguments:
+ * - new delay (Float) (optional). If not set, previous delay is used.
+ */
+
+static
+VALUE AsyncEngineTimer_restart(int argc, VALUE *argv, VALUE self)
+{
+ AE_TRACE();
+ long delay;
+ int r;
+
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
+ AE_RB_CHECK_NUM_ARGS(0,1);
+
+ // Parameter 1: delay (optional).
+ if (argc == 1 && ! NIL_P(argv[0])) {
+ delay = (long)(NUM2DBL(argv[0]) * 1000);
+ if (delay < 1)
+ delay = 1;
+ cdata->delay = delay;
}
- // Save the block from being GC'd.
- cdata->rb_block_id = ae_store_block(block);
+ r = uv_timer_stop(cdata->_uv_handle);
+ if (r != 0)
+ ae_raise_last_uv_error();
- if (NIL_P(instance))
- cdata->has_rb_instance = 0;
- else {
- cdata->has_rb_instance = 1;
- cdata->rb_instance = instance;
+ r = uv_timer_start(cdata->_uv_handle, _uv_timer_callback, cdata->delay, 0);
+ if (r != 0)
+ ae_raise_last_uv_error();
+
+ return Qtrue;
+}
+
+
+/**
+ * PeriodicTimer#restart() method.
+ *
+ * Arguments:
+ * - new interval (Float) (optional). If not set, previous interval is used.
+ * - new delay (Float) (optional). If not set, new interval value is used,
+ * or previous delay value if no new interval is set.
+ */
+
+static
+VALUE AsyncEnginePeriodicTimer_restart(int argc, VALUE *argv, VALUE self)
+{
+ AE_TRACE();
+ long interval, delay;
+ int r;
+
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
+ AE_RB_CHECK_NUM_ARGS(0,2);
+
+ // Parameter 1: interval (optional).
+ if (argc >= 1 && ! NIL_P(argv[0])) {
+ interval = (long)(NUM2DBL(argv[0]) * 1000);
+ if (interval < 1)
+ interval = 1;
+ cdata->interval = interval;
+
+ // Parameter 2: delay (optional).
+ if (argc == 2 && ! NIL_P(argv[1])) {
+ delay = (long)(NUM2DBL(argv[1]) * 1000);
+ if (delay < 1)
+ delay = 1;
+ cdata->delay = delay;
+ }
+ else
+ cdata->delay = interval;
}
- // Initialize.
- uv_timer_init(uv_default_loop(), _uv_handle);
- _uv_handle->data = cdata;
+ r = uv_timer_stop(cdata->_uv_handle);
+ if (r != 0)
+ ae_raise_last_uv_error();
- uv_timer_start(_uv_handle, timer_callback, delay, interval);
+ r = uv_timer_start(cdata->_uv_handle, _uv_timer_callback, cdata->delay, cdata->interval);
+ if (r != 0)
+ ae_raise_last_uv_error();
- return Data_Wrap_Struct(cAsyncEngineCData, NULL, NULL, cdata);
+ return Qtrue;
}
-VALUE AsyncEngineTimer_cancel(VALUE self)
+/** Timer#delay() method. */
+
+static
+VALUE AsyncEngineTimer_delay(VALUE self)
{
AE_TRACE();
- struct_ae_timer_cdata* cdata;
- if (! NIL_P(rb_ivar_get(self, att_handle_terminated)))
- return Qfalse;
- rb_ivar_set(self, att_handle_terminated, Qtrue);
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
- Data_Get_Struct(rb_ivar_get(self, att_cdata), struct_ae_timer_cdata, cdata);
+ return rb_float_new((double)(cdata->delay / 1000.0));
+}
- // Stop timer.
- uv_timer_stop(cdata->_uv_handle);
- // Terminate the timer.
- deallocate(cdata);
+/** PeriodicTimer#interval() method. */
+static
+VALUE AsyncEnginePeriodicTimer_interval(VALUE self)
+{
+ AE_TRACE();
+
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
+
+ return rb_float_new((double)(uv_timer_get_repeat(cdata->_uv_handle) / 1000.0));
+}
+
+
+/** Timer#alive?() method. */
+
+static
+VALUE AsyncEngineTimer_is_alive(VALUE self)
+{
+ AE_TRACE();
+
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
+
return Qtrue;
}
-VALUE AsyncEngineTimer_c_set_interval(VALUE self, VALUE rb_interval)
+/** Timer#close() method. */
+
+static
+VALUE AsyncEngineTimer_close(VALUE self)
{
AE_TRACE();
- struct_ae_timer_cdata* cdata;
- long interval;
-
- if (! NIL_P(rb_ivar_get(self, att_handle_terminated)))
- return Qfalse;
- Data_Get_Struct(rb_ivar_get(self, att_cdata), struct_ae_timer_cdata, cdata);
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
- interval = NUM2LONG(rb_interval);
- if (interval == 0) interval = 1;
-
- uv_timer_set_repeat(cdata->_uv_handle, interval);
- return rb_interval;
-}
\ No newline at end of file
+ close_handle(cdata);
+ return Qtrue;
+}
+
+
+/** Timer#destroy() private method. */
+
+static
+VALUE AsyncEngineTimer_destroy(VALUE self)
+{
+ AE_TRACE();
+
+ GET_CDATA_FROM_SELF_AND_CHECK_UV_HANDLE_IS_OPEN;
+
+ close_handle(cdata);
+ return Qtrue;
+}
+
+
+static
+void close_handle(struct_cdata *cdata)
+{
+ AE_TRACE();
+
+ AE_CLOSE_UV_HANDLE(cdata->_uv_handle);
+ cdata->_uv_handle = NULL;
+ ae_remove_handle(cdata->ae_handle_id);
+}