From 15476c695d708796f2271d99deb3cd5288d016d2 Mon Sep 17 00:00:00 2001 From: normal Date: Mon, 23 Nov 2015 21:20:56 +0000 Subject: fiddle: release GVL for ffi_call Some external functions I wish to call may take a long time and unnecessarily block other threads. This may lead to performance regressions for fast functions as releasing/acquiring the GVL is not cheap, but can improve performance for long-running functions in multi-threaded applications. This also means we must reacquire the GVL when calling Ruby-defined callbacks for Fiddle::Closure, meaning we must detect whether the current thread has the GVL by exporting ruby_thread_has_gvl_p in internal.h * ext/fiddle/function.c (struct nogvl_ffi_call_args): new struct for GVL release (nogvl_ffi_call): new function (function_call): adjust for GVL release [ruby-core:71642] [Feature #11607] * ext/fiddle/closure.c (struct callback_args): new struct for GVL acquire (with_gvl_callback): adjusted original callback function (callback): wrapper for conditional GVL acquire * ext/fiddle/depend: add dependencies * ext/fiddle/extconf.rb: include top_srcdir for internal.h * internal.h (ruby_thread_has_gvl_p): expose for fiddle * vm_core.h (ruby_thread_has_gvl_p): moved to internal.h * test/fiddle/test_function.rb (test_nogvl_poll): new test git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52723 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ext/fiddle/closure.c | 79 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 25 deletions(-) (limited to 'ext/fiddle/closure.c') diff --git a/ext/fiddle/closure.c b/ext/fiddle/closure.c index 5d8a3d200f..2263b410e8 100644 --- a/ext/fiddle/closure.c +++ b/ext/fiddle/closure.c @@ -1,4 +1,6 @@ #include +#include +#include "internal.h" /* rb_thread_has_gvl_p */ VALUE cFiddleClosure; @@ -55,10 +57,19 @@ const rb_data_type_t closure_data_type = { {0, dealloc, closure_memsize,}, }; -static void -callback(ffi_cif *cif, void *resp, void **args, void *ctx) +struct callback_args { + ffi_cif *cif; + void *resp; + void **args; + void *ctx; +}; + +static void * +with_gvl_callback(void *ptr) { - VALUE self = (VALUE)ctx; + struct callback_args *x = ptr; + + VALUE self = (VALUE)x->ctx; VALUE rbargs = rb_iv_get(self, "@args"); VALUE ctype = rb_iv_get(self, "@ctype"); int argc = RARRAY_LENINT(rbargs); @@ -76,46 +87,46 @@ callback(ffi_cif *cif, void *resp, void **args, void *ctx) argc = 0; break; case TYPE_INT: - rb_ary_push(params, INT2NUM(*(int *)args[i])); + rb_ary_push(params, INT2NUM(*(int *)x->args[i])); break; case -TYPE_INT: - rb_ary_push(params, UINT2NUM(*(unsigned int *)args[i])); + rb_ary_push(params, UINT2NUM(*(unsigned int *)x->args[i])); break; case TYPE_VOIDP: rb_ary_push(params, rb_funcall(cPointer, rb_intern("[]"), 1, - PTR2NUM(*(void **)args[i]))); + PTR2NUM(*(void **)x->args[i]))); break; case TYPE_LONG: - rb_ary_push(params, LONG2NUM(*(long *)args[i])); + rb_ary_push(params, LONG2NUM(*(long *)x->args[i])); break; case -TYPE_LONG: - rb_ary_push(params, ULONG2NUM(*(unsigned long *)args[i])); + rb_ary_push(params, ULONG2NUM(*(unsigned long *)x->args[i])); break; case TYPE_CHAR: - rb_ary_push(params, INT2NUM(*(signed char *)args[i])); + rb_ary_push(params, INT2NUM(*(signed char *)x->args[i])); break; case -TYPE_CHAR: - rb_ary_push(params, UINT2NUM(*(unsigned char *)args[i])); + rb_ary_push(params, UINT2NUM(*(unsigned char *)x->args[i])); break; case TYPE_SHORT: - rb_ary_push(params, INT2NUM(*(signed short *)args[i])); + rb_ary_push(params, INT2NUM(*(signed short *)x->args[i])); break; case -TYPE_SHORT: - rb_ary_push(params, UINT2NUM(*(unsigned short *)args[i])); + rb_ary_push(params, UINT2NUM(*(unsigned short *)x->args[i])); break; case TYPE_DOUBLE: - rb_ary_push(params, rb_float_new(*(double *)args[i])); + rb_ary_push(params, rb_float_new(*(double *)x->args[i])); break; case TYPE_FLOAT: - rb_ary_push(params, rb_float_new(*(float *)args[i])); + rb_ary_push(params, rb_float_new(*(float *)x->args[i])); break; #if HAVE_LONG_LONG case TYPE_LONG_LONG: - rb_ary_push(params, LL2NUM(*(LONG_LONG *)args[i])); + rb_ary_push(params, LL2NUM(*(LONG_LONG *)x->args[i])); break; case -TYPE_LONG_LONG: - rb_ary_push(params, ULL2NUM(*(unsigned LONG_LONG *)args[i])); + rb_ary_push(params, ULL2NUM(*(unsigned LONG_LONG *)x->args[i])); break; #endif default: @@ -131,41 +142,59 @@ callback(ffi_cif *cif, void *resp, void **args, void *ctx) case TYPE_VOID: break; case TYPE_LONG: - *(long *)resp = NUM2LONG(ret); + *(long *)x->resp = NUM2LONG(ret); break; case -TYPE_LONG: - *(unsigned long *)resp = NUM2ULONG(ret); + *(unsigned long *)x->resp = NUM2ULONG(ret); break; case TYPE_CHAR: case TYPE_SHORT: case TYPE_INT: - *(ffi_sarg *)resp = NUM2INT(ret); + *(ffi_sarg *)x->resp = NUM2INT(ret); break; case -TYPE_CHAR: case -TYPE_SHORT: case -TYPE_INT: - *(ffi_arg *)resp = NUM2UINT(ret); + *(ffi_arg *)x->resp = NUM2UINT(ret); break; case TYPE_VOIDP: - *(void **)resp = NUM2PTR(ret); + *(void **)x->resp = NUM2PTR(ret); break; case TYPE_DOUBLE: - *(double *)resp = NUM2DBL(ret); + *(double *)x->resp = NUM2DBL(ret); break; case TYPE_FLOAT: - *(float *)resp = (float)NUM2DBL(ret); + *(float *)x->resp = (float)NUM2DBL(ret); break; #if HAVE_LONG_LONG case TYPE_LONG_LONG: - *(LONG_LONG *)resp = NUM2LL(ret); + *(LONG_LONG *)x->resp = NUM2LL(ret); break; case -TYPE_LONG_LONG: - *(unsigned LONG_LONG *)resp = NUM2ULL(ret); + *(unsigned LONG_LONG *)x->resp = NUM2ULL(ret); break; #endif default: rb_raise(rb_eRuntimeError, "closure retval: %d", type); } + return 0; +} + +static void +callback(ffi_cif *cif, void *resp, void **args, void *ctx) +{ + struct callback_args x; + + x.cif = cif; + x.resp = resp; + x.args = args; + x.ctx = ctx; + + if (ruby_thread_has_gvl_p()) { + (void)with_gvl_callback(&x); + } else { + (void)rb_thread_call_with_gvl(with_gvl_callback, &x); + } } static VALUE -- cgit v1.2.3