diff options
author | Koichi Sasada <ko1@atdot.net> | 2019-10-20 04:52:20 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-10-20 04:52:20 +0900 |
commit | caac5f777ae288b5982708b8690e712e1cae0cf6 (patch) | |
tree | e4257d65c062b7e8c9e4c4b962cee3ca7c5e1d4c /ext/monitor/monitor.c | |
parent | 434966bffddd4299d34f5d1f7f225bf7396d0807 (diff) |
make monitor.so for performance. (#2576)
Recent monitor.rb has performance problem because of interrupt
handlers. 'Monitor#synchronize' is frequently used primitive
so the performance of this method is important.
This patch rewrite 'monitor.rb' with 'monitor.so' (C-extension)
and make it faster. See [Feature #16255] for details.
Monitor class objects are normal object which include MonitorMixin.
This patch introduce a Monitor class which is implemented on C
and MonitorMixin uses Monitor object as re-entrant (recursive)
Mutex. This technique improve performance because we don't need
to care atomicity and we don't need accesses to instance variables
any more on Monitor class.
Notes
Notes:
Merged-By: ko1 <ko1@atdot.net>
Diffstat (limited to 'ext/monitor/monitor.c')
-rw-r--r-- | ext/monitor/monitor.c | 189 |
1 files changed, 189 insertions, 0 deletions
diff --git a/ext/monitor/monitor.c b/ext/monitor/monitor.c new file mode 100644 index 0000000000..cf9e7fe07d --- /dev/null +++ b/ext/monitor/monitor.c @@ -0,0 +1,189 @@ +#include "ruby/ruby.h" + +/* Thread::Monitor */ + +struct rb_monitor { + long count; + const VALUE owner; + const VALUE mutex; +}; + +static void +monitor_mark(void *ptr) +{ + struct rb_monitor *mc = ptr; + rb_gc_mark(mc->owner); + rb_gc_mark(mc->mutex); +} + +static size_t +monitor_memsize(const void *ptr) +{ + return sizeof(struct rb_monitor); +} + +static const rb_data_type_t monitor_data_type = { + "monitor", + {monitor_mark, RUBY_TYPED_DEFAULT_FREE, monitor_memsize,}, + 0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED +}; + +static VALUE +monitor_alloc(VALUE klass) +{ + struct rb_monitor *mc; + VALUE obj; + + obj = TypedData_Make_Struct(klass, struct rb_monitor, &monitor_data_type, mc); + RB_OBJ_WRITE(obj, &mc->mutex, rb_mutex_new()); + RB_OBJ_WRITE(obj, &mc->owner, Qnil); + mc->count = 0; + + return obj; +} + +static struct rb_monitor * +monitor_ptr(VALUE monitor) +{ + struct rb_monitor *mc; + TypedData_Get_Struct(monitor, struct rb_monitor, &monitor_data_type, mc); + return mc; +} + +static int +mc_owner_p(struct rb_monitor *mc) +{ + return mc->owner == rb_thread_current(); +} + +static VALUE +monitor_try_enter(VALUE monitor) +{ + struct rb_monitor *mc = monitor_ptr(monitor); + + if (!mc_owner_p(mc)) { + if (!rb_mutex_trylock(mc->mutex)) { + return Qfalse; + } + RB_OBJ_WRITE(monitor, &mc->owner, rb_thread_current()); + mc->count = 0; + } + mc->count += 1; + return Qtrue; +} + +static VALUE +monitor_enter(VALUE monitor) +{ + struct rb_monitor *mc = monitor_ptr(monitor); + if (!mc_owner_p(mc)) { + rb_mutex_lock(mc->mutex); + RB_OBJ_WRITE(monitor, &mc->owner, rb_thread_current()); + mc->count = 0; + } + mc->count++; + return Qnil; +} + +static VALUE +monitor_exit(VALUE monitor) +{ + struct rb_monitor *mc = monitor_ptr(monitor); + mc->count--; + if (mc->count == 0) { + RB_OBJ_WRITE(monitor, &mc->owner, Qnil); + rb_mutex_unlock(mc->mutex); + } + return Qnil; +} + +static VALUE +monitor_locked_p(VALUE monitor) +{ + struct rb_monitor *mc = monitor_ptr(monitor); + return rb_mutex_locked_p(mc->mutex); +} + +static VALUE +monitor_owned_p(VALUE monitor) +{ + struct rb_monitor *mc = monitor_ptr(monitor); + return (rb_mutex_locked_p(mc->mutex) && mc_owner_p(mc)) ? Qtrue : Qfalse; +} + +static VALUE +monitor_check_owner(VALUE monitor) +{ + struct rb_monitor *mc = monitor_ptr(monitor); + if (!mc_owner_p(mc)) { + rb_raise(rb_eThreadError, "current thread not owner"); + } + return Qnil; +} + +static VALUE +monitor_enter_for_cond(VALUE monitor, VALUE count) +{ + struct rb_monitor *mc = monitor_ptr(monitor); + RB_OBJ_WRITE(monitor, &mc->owner, rb_thread_current()); + mc->count = NUM2LONG(count); + return Qnil; +} + +static VALUE +monitor_exit_for_cond(VALUE monitor) +{ + struct rb_monitor *mc = monitor_ptr(monitor); + long cnt = mc->count; + RB_OBJ_WRITE(monitor, &mc->owner, Qnil); + mc->count = 0; + return LONG2NUM(cnt); +} + +static VALUE +monitor_mutex_for_cond(VALUE monitor) +{ + struct rb_monitor *mc = monitor_ptr(monitor); + return mc->mutex; +} + +static VALUE +monitor_sync_body(VALUE monitor) +{ + return rb_yield_values(0); +} + +static VALUE +monitor_sync_ensure(VALUE monitor) +{ + return monitor_exit(monitor); +} + +static VALUE +monitor_synchronize(VALUE monitor) +{ + monitor_enter(monitor); + return rb_ensure(monitor_sync_body, monitor, monitor_sync_ensure, monitor); +} + +void +Init_monitor(void) +{ + VALUE rb_cMonitor = rb_define_class("Monitor", rb_cObject); + rb_define_alloc_func(rb_cMonitor, monitor_alloc); + + rb_define_method(rb_cMonitor, "try_enter", monitor_try_enter, 0); + rb_define_method(rb_cMonitor, "enter", monitor_enter, 0); + rb_define_method(rb_cMonitor, "exit", monitor_exit, 0); + rb_define_method(rb_cMonitor, "synchronize", monitor_synchronize, 0); + + /* internal methods for MonitorMixin */ + rb_define_method(rb_cMonitor, "mon_locked?", monitor_locked_p, 0); + rb_define_method(rb_cMonitor, "mon_check_owner", monitor_check_owner, 0); + rb_define_method(rb_cMonitor, "mon_owned?", monitor_owned_p, 0); + + /* internal methods for MonitorMixin::ConditionalVariable */ + rb_define_private_method(rb_cMonitor, "enter_for_cond", monitor_enter_for_cond, 1); + rb_define_private_method(rb_cMonitor, "exit_for_cond", monitor_exit_for_cond, 0); + rb_define_private_method(rb_cMonitor, "mutex_for_cond", monitor_mutex_for_cond, 0); +} |