aboutsummaryrefslogtreecommitdiffstats
path: root/ext/monitor/monitor.c
diff options
context:
space:
mode:
authorKoichi Sasada <ko1@atdot.net>2019-10-20 04:52:20 +0900
committerGitHub <noreply@github.com>2019-10-20 04:52:20 +0900
commitcaac5f777ae288b5982708b8690e712e1cae0cf6 (patch)
treee4257d65c062b7e8c9e4c4b962cee3ca7c5e1d4c /ext/monitor/monitor.c
parent434966bffddd4299d34f5d1f7f225bf7396d0807 (diff)
downloadruby-caac5f777ae288b5982708b8690e712e1cae0cf6.tar.gz
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.
Diffstat (limited to 'ext/monitor/monitor.c')
-rw-r--r--ext/monitor/monitor.c189
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);
+}