From f5774348b6fe5bb7e86717629fdb8c4d8c30731f Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 5 Apr 2013 02:47:04 +0000 Subject: 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. --- extconf.rb | 2 + mahoro.c | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 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 +# 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; -- cgit v1.2.3-24-ge0c7