about summary refs log tree commit homepage
diff options
context:
space:
mode:
-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;