about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2013-04-05 02:47:04 +0000
committerEric Wong <normalperson@yhbt.net>2013-04-05 02:47:04 +0000
commitf5774348b6fe5bb7e86717629fdb8c4d8c30731f (patch)
treef1c57b2a836fced43d6761971de665c9e1d745dd
parent92e549dfb5f19125f4b6131937b738eee5b046c7 (diff)
downloadmahoro-f5774348b6fe5bb7e86717629fdb8c4d8c30731f.tar.gz
release GVL if filesystem I/O is required
Filesystem I/O has unpredictable latency, release the GVL
in these cases since we will never know how long it lasts.
We enable interrupt processing since we may be operating
on FIFOs.

(Matz) Ruby 1.9 and 2.0 releases GVL for all file system
operations, too.
-rw-r--r--extconf.rb2
-rw-r--r--mahoro.c148
2 files changed, 130 insertions, 20 deletions
diff --git a/extconf.rb b/extconf.rb
index 026b6c3..0709016 100644
--- a/extconf.rb
+++ b/extconf.rb
@@ -2,6 +2,8 @@ require 'mkmf'
 
 dir_config('magic')
 have_library('magic', 'magic_open')
+have_func('rb_thread_call_without_gvl')
+have_func('rb_thread_blocking_region')
 create_makefile('mahoro')
 
 # arch-tag: extconf
diff --git a/mahoro.c b/mahoro.c
index fe02cf6..1548a76 100644
--- a/mahoro.c
+++ b/mahoro.c
@@ -19,6 +19,63 @@
 static VALUE cMahoro;
 static VALUE eMahoroError;
 
+struct nogvl_args {
+        magic_t cookie;
+        union {
+                const char *path;
+                int fd;
+        } as;
+};
+
+/*
+ * Compatibility layer for various GVL-releasing
+ *
+ * rb_thread_call_without_gvl was detectable via have_func in 1.9.3,
+ * but not usable.  So we must check for ruby/thread.h and use
+ * rb_thread_blocking_region if ruby/thread.h is not available
+ *
+ * HAVE_RUBY_THREAD_H is defined by ruby.h in 2.0.0, NOT using
+ * extconf.rb since that may find ruby/thread.h in a different
+ * installation
+ */
+#if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) && \
+        defined(HAVE_RUBY_THREAD_H) &&  \
+        HAVE_RUBY_THREAD_H
+#  include <ruby/thread.h>
+#  define NOGVL(fn,data1,ubf,data2) \
+      rb_thread_call_without_gvl((fn),(data1),(ubf),(data2))
+#elif defined(HAVE_RB_THREAD_BLOCKING_REGION) /* Ruby 1.9.x */
+#  define COMPAT_FN (VALUE (*)(void *))
+#  define NOGVL(fn,data1,ubf,data2) \
+      (void *)rb_thread_blocking_region(COMPAT_FN(fn),(data1),(ubf),(data2))
+#else /* Ruby 1.8 */
+/*
+ * Ruby 1.8 does not have a GVL, we'll just enable signal interrupts
+ * here in case we make interruptible syscalls
+ */
+#  define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
+#  include "rubysig.h"
+typedef void rb_unblock_function_t(void *);
+typedef void *rb_blocking_function_t(void *);
+
+static void *
+fake_nogvl(fn, data1, ubf, data2)
+        rb_blocking_function_t fn;
+        void *data1;
+        rb_unblock_function_t ubf;
+        void *data2;
+{
+        void *rv;
+
+        TRAP_BEG;
+        rv = fn(data1);
+        TRAP_END;
+
+        return rv;
+}
+#  define NOGVL(fn,data1,ubf,data2) fake_nogvl((fn),(data1),(ubf),(data2))
+#endif
+
 /* :nodoc: called automatically by GC */
 static void
 mahoro_free(ptr)
@@ -36,6 +93,15 @@ mahoro_allocate(klass)
         return Data_Wrap_Struct(klass, 0, mahoro_free, 0);
 }
 
+static void *
+nogvl_load(ptr)
+        void *ptr;
+{
+        struct nogvl_args *args = ptr;
+
+        return magic_load(args->cookie, args->as.path) ? ptr : NULL;
+}
+
 /*
  * call-seq:
  *        Mahoro.new(flags = Mahoro::NONE, path = nil)        ->        mahoro_obj
@@ -56,14 +122,15 @@ mahoro_initialize(argc, argv, self)
         VALUE *argv, self;
 {
         int flags = MAGIC_NONE;
-        char *path = 0;
-        magic_t cookie;
+        struct nogvl_args args;
         VALUE vpath, vflags;
 
+        args.as.path = NULL;
+
         switch(rb_scan_args(argc, argv, "02", &vflags, &vpath)) {
                 case 2:
                         if(!NIL_P(vpath)) {
-                                path = StringValueCStr(vpath);
+                                args.as.path = StringValueCStr(vpath);
                         }
                         /* fallthrough */
                 case 1:
@@ -71,20 +138,29 @@ mahoro_initialize(argc, argv, self)
                         break;
         }
 
-        if(!(cookie = magic_open(flags))) {
+        if(!(args.cookie = magic_open(flags))) {
                 rb_raise(eMahoroError, "failed to initialize magic cookie");
         }
 
-        if(magic_load(cookie, path)) {
+        if(NOGVL(nogvl_load, &args, RUBY_UBF_IO, NULL)) {
                 rb_raise(eMahoroError, "failed to load database: %s",
-                                      magic_error(cookie));
+                                      magic_error(args.cookie));
         }
 
-        DATA_PTR(self) = cookie;
+        DATA_PTR(self) = args.cookie;
 
         return self;
 }
 
+static void *
+nogvl_file(ptr)
+        void *ptr;
+{
+        struct nogvl_args *args = ptr;
+
+        return (void *)magic_file(args->cookie, args->as.path);
+}
+
 /*
  * call-seq:
  *        mahoro_obj.file(filename)        ->        String
@@ -99,10 +175,14 @@ mahoro_file(self, path)
         VALUE self, path;
 {
         const char *msg;
-        magic_t cookie = (magic_t)DATA_PTR(self);
+        struct nogvl_args args;
 
-        if(!(msg = magic_file(cookie, StringValueCStr(path)))) {
-                rb_raise(eMahoroError, "failed lookup: %s", magic_error(cookie));
+        args.cookie = (magic_t)DATA_PTR(self);
+        args.as.path = StringValueCStr(path);
+
+        if(!(msg = NOGVL(nogvl_file, &args, RUBY_UBF_IO, NULL))) {
+                rb_raise(eMahoroError, "failed lookup: %s",
+                        magic_error(args.cookie));
         }
 
         return rb_str_new2(msg);
@@ -177,6 +257,15 @@ mahoro_set_flags(self, flags)
         return INT2FIX(magic_setflags(cookie, FIX2INT(flags)));
 }
 
+static void *
+nogvl_check(ptr)
+        void *ptr;
+{
+        struct nogvl_args *args = ptr;
+
+        return magic_check(args->cookie, args->as.path) ? ptr : NULL;
+}
+
 /*
  * call-seq:
  *        mahoro_obj.check(path = nil)        ->        true or false
@@ -190,25 +279,36 @@ mahoro_check(argc, argv, self)
         int argc;
         VALUE *argv, self;
 {
-        char *path = 0;
+        struct nogvl_args args;
         VALUE vpath;
-        magic_t cookie = (magic_t)DATA_PTR(self);
+
+        args.cookie = (magic_t)DATA_PTR(self);
+        args.as.path = NULL;
 
         switch(rb_scan_args(argc, argv, "01", &vpath)) {
                 case 1:
                         if(!NIL_P(vpath)) {
-                                path = StringValueCStr(vpath);
+                                args.as.path = StringValueCStr(vpath);
                         }
                         break;
         }
 
-        if(!magic_check(cookie, path)) {
+        if(!NOGVL(nogvl_check, &args, RUBY_UBF_IO, 0)) {
                 return Qtrue;
         } else {
                 return Qfalse;
         }
 }
 
+static void *
+nogvl_compile(ptr)
+        void *ptr;
+{
+        struct nogvl_args *args = ptr;
+
+        return magic_compile(args->cookie, args->as.path) ? ptr : NULL;
+}
+
 /*
  * call-seq:
  *        mahoro_obj.compile(path)        ->        true
@@ -228,10 +328,14 @@ static VALUE
 mahoro_compile(self, path)
         VALUE self, path;
 {
-        magic_t cookie = (magic_t)DATA_PTR(self);
+        struct nogvl_args args;
+
+        args.cookie = (magic_t)DATA_PTR(self);
+        args.as.path = StringValueCStr(path);
 
-        if(magic_compile(cookie, StringValueCStr(path))) {
-                rb_raise(eMahoroError, "failed compile: %s", magic_error(cookie));
+        if(NOGVL(nogvl_compile, &args, RUBY_UBF_IO, NULL)) {
+                rb_raise(eMahoroError, "failed compile: %s",
+                        magic_error(args.cookie));
         }
 
         return Qtrue;
@@ -276,10 +380,14 @@ static VALUE
 mahoro_load(self, path)
         VALUE self, path;
 {
-        magic_t cookie = (magic_t)DATA_PTR(self);
+        struct nogvl_args args;
+
+        args.cookie = (magic_t)DATA_PTR(self);
+        args.as.path = StringValueCStr(path);
 
-        if(magic_load(cookie, StringValueCStr(path))) {
-                rb_raise(eMahoroError, "failed load: %s", magic_error(cookie));
+        if(NOGVL(nogvl_load, &args, RUBY_UBF_IO, NULL)) {
+                rb_raise(eMahoroError, "failed load: %s",
+                        magic_error(args.cookie));
         }
 
         return self;