Files
nagachika 229c041f05 merge revision(s) d732bc51bdbfe7d66038731d42e01a511d13b5f8,633a1f15d8228236094ddee12e4e169d655ec49e,95f387f61a4a4ea92635da760b7de5b1e09bb84e,528a3a17977aa1843a26630c96635c3cb161e729,261569d4aac440f25de588cca365163ecf1124a2,e4a9e926f0fe0acf2fbe61da6e075a95d34be066,318be1cb2f9c6f04403c063a5618c6267012ee51,e1855100e46040e73630b378974c17764e0cccee,9cfa811b0f209d714f89fe0de6778c71f77556c7,b68dab866724aacc1cbc6b7d5e1f555dec092346: [Backport #17202]
Revert "Revert "Revert "[ruby/fiddle] Use ffi_closure_free by
	 default. (#20)"""

	This reverts commit 87f6154bb4.

	It turned out that the change fails to build on macOS

	https://rubyci.org/logs/rubyci.s3.amazonaws.com/osx1014/ruby-master/log/20200304T074503Z.fail.html.gz
	```
	+ make 'TESTS=--hide-skip -v fiddle' RUBYOPT=-w test-all
	dyld: lazy symbol binding failed: Symbol not found: _ffi_closure_alloc
	  Referenced from: /Users/hsbt/Documents/cb/tmp/build/20200304T074503Z/ruby/.ext/x86_64-darwin18/fiddle.bundle
	  Expected in: flat namespace

	dyld: Symbol not found: _ffi_closure_alloc
	  Referenced from: /Users/hsbt/Documents/cb/tmp/build/20200304T074503Z/ruby/.ext/x86_64-darwin18/fiddle.bundle
	  Expected in: flat namespace

	make: *** [yes-test-all] Abort trap: 6
	```

	[ruby/fiddle] Use ffi_closure_free if available

	[ruby/fiddle] ffi_closure_free is available in the bundled libffi

	[ruby/fiddle] use ffi_closure_alloc only with 3.2 or later

	[ruby/fiddle] always use ffi_closure_alloc on Windows

	Fixed a typo

	Show libffi version only if set

	ext/fiddle/extconf.rb: check if ffi_closure_alloc is available

	to define HAVE_FFI_CLOSURE_ALLOC.
	The macro is used in closure.c, so have_func check is needed.

	If pkg-config is not installed, extconf.rb fails to detect the version
	of libffi, and does not add "-DUSE_FFI_CLOSURE_ALLOC=1" even when system
	libffi version is >= 3.2.

	If USE_FFI_CLOSURE_ALLOC is not defined, closure.c attempts to check if
	HAVE_FFI_CLOSURE_ALLOC is defined or not, but have_func was removed with
	528a3a1797, so the macro is always not
	defined.

	This resulted in this deprecation warning:

	https://rubyci.org/logs/rubyci.s3.amazonaws.com/ubuntu2004/ruby-master/log/20200512T123003Z.log.html.gz
	```
	compiling closure.c
	closure.c: In function 'initialize':
	closure.c:265:5: warning: 'ffi_prep_closure' is deprecated: use ffi_prep_closure_loc instead [-Wdeprecated-declarations]
	  265 |     result = ffi_prep_closure(pcl, cif, callback, (void *)self);
	      |     ^~~~~~
	In file included from ./fiddle.h:42,
	                 from closure.c:1:
	/usr/include/x86_64-linux-gnu/ffi.h:334:1: note: declared here
	  334 | ffi_prep_closure (ffi_closure*,
	      | ^~~~~~~~~~~~~~~~
	```

	Do not try ffi_closure_alloc if libffi is <= 3.1

	Maybe due to e1855100e4, CentOS, RHEL, and
	Fedora CIs have started failing with SEGV.  Try to avoid
	ffi_closure_alloc on those environments.

	https://rubyci.org/logs/rubyci.s3.amazonaws.com/centos8/ruby-master/log/20200512T183004Z.fail.html.gz
	https://rubyci.org/logs/rubyci.s3.amazonaws.com/fedora32/ruby-master/log/20200512T183004Z.fail.html.gz
	https://rubyci.org/logs/rubyci.s3.amazonaws.com/rhel8/ruby-master/log/20200512T183003Z.fail.html.gz

	ext/fiddle/extconf.rb: Fix the condition of libffi <= 3.1

	ver is [3, 1, 0] which is not less then or equal to [3, 1]
2020-10-01 07:53:55 +09:00

343 lines
7.8 KiB
C

#include <fiddle.h>
#include <ruby/thread.h>
int ruby_thread_has_gvl_p(void); /* from internal.h */
VALUE cFiddleClosure;
typedef struct {
void * code;
ffi_closure *pcl;
ffi_cif cif;
int argc;
ffi_type **argv;
} fiddle_closure;
#if defined(USE_FFI_CLOSURE_ALLOC)
#elif !defined(HAVE_FFI_CLOSURE_ALLOC)
# define USE_FFI_CLOSURE_ALLOC 0
#else
# define USE_FFI_CLOSURE_ALLOC 1
#endif
static void
dealloc(void * ptr)
{
fiddle_closure * cls = (fiddle_closure *)ptr;
#if USE_FFI_CLOSURE_ALLOC
ffi_closure_free(cls->pcl);
#else
munmap(cls->pcl, sizeof(*cls->pcl));
#endif
if (cls->argv) xfree(cls->argv);
xfree(cls);
}
static size_t
closure_memsize(const void * ptr)
{
fiddle_closure * cls = (fiddle_closure *)ptr;
size_t size = 0;
size += sizeof(*cls);
#if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
size += ffi_raw_size(&cls->cif);
#endif
size += sizeof(*cls->argv);
size += sizeof(ffi_closure);
return size;
}
const rb_data_type_t closure_data_type = {
"fiddle/closure",
{0, dealloc, closure_memsize,},
};
struct callback_args {
ffi_cif *cif;
void *resp;
void **args;
void *ctx;
};
static void *
with_gvl_callback(void *ptr)
{
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);
VALUE params = rb_ary_tmp_new(argc);
VALUE ret;
VALUE cPointer;
int i, type;
cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
for (i = 0; i < argc; i++) {
type = NUM2INT(RARRAY_AREF(rbargs, i));
switch (type) {
case TYPE_VOID:
argc = 0;
break;
case TYPE_INT:
rb_ary_push(params, INT2NUM(*(int *)x->args[i]));
break;
case -TYPE_INT:
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 **)x->args[i])));
break;
case TYPE_LONG:
rb_ary_push(params, LONG2NUM(*(long *)x->args[i]));
break;
case -TYPE_LONG:
rb_ary_push(params, ULONG2NUM(*(unsigned long *)x->args[i]));
break;
case TYPE_CHAR:
rb_ary_push(params, INT2NUM(*(signed char *)x->args[i]));
break;
case -TYPE_CHAR:
rb_ary_push(params, UINT2NUM(*(unsigned char *)x->args[i]));
break;
case TYPE_SHORT:
rb_ary_push(params, INT2NUM(*(signed short *)x->args[i]));
break;
case -TYPE_SHORT:
rb_ary_push(params, UINT2NUM(*(unsigned short *)x->args[i]));
break;
case TYPE_DOUBLE:
rb_ary_push(params, rb_float_new(*(double *)x->args[i]));
break;
case TYPE_FLOAT:
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 *)x->args[i]));
break;
case -TYPE_LONG_LONG:
rb_ary_push(params, ULL2NUM(*(unsigned LONG_LONG *)x->args[i]));
break;
#endif
default:
rb_raise(rb_eRuntimeError, "closure args: %d", type);
}
}
ret = rb_funcall2(self, rb_intern("call"), argc, RARRAY_CONST_PTR(params));
RB_GC_GUARD(params);
type = NUM2INT(ctype);
switch (type) {
case TYPE_VOID:
break;
case TYPE_LONG:
*(long *)x->resp = NUM2LONG(ret);
break;
case -TYPE_LONG:
*(unsigned long *)x->resp = NUM2ULONG(ret);
break;
case TYPE_CHAR:
case TYPE_SHORT:
case TYPE_INT:
*(ffi_sarg *)x->resp = NUM2INT(ret);
break;
case -TYPE_CHAR:
case -TYPE_SHORT:
case -TYPE_INT:
*(ffi_arg *)x->resp = NUM2UINT(ret);
break;
case TYPE_VOIDP:
*(void **)x->resp = NUM2PTR(ret);
break;
case TYPE_DOUBLE:
*(double *)x->resp = NUM2DBL(ret);
break;
case TYPE_FLOAT:
*(float *)x->resp = (float)NUM2DBL(ret);
break;
#if HAVE_LONG_LONG
case TYPE_LONG_LONG:
*(LONG_LONG *)x->resp = NUM2LL(ret);
break;
case -TYPE_LONG_LONG:
*(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
allocate(VALUE klass)
{
fiddle_closure * closure;
VALUE i = TypedData_Make_Struct(klass, fiddle_closure,
&closure_data_type, closure);
#if USE_FFI_CLOSURE_ALLOC
closure->pcl = ffi_closure_alloc(sizeof(ffi_closure), &closure->code);
#else
closure->pcl = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, 0);
#endif
return i;
}
static VALUE
initialize(int rbargc, VALUE argv[], VALUE self)
{
VALUE ret;
VALUE args;
VALUE abi;
fiddle_closure * cl;
ffi_cif * cif;
ffi_closure *pcl;
ffi_status result;
int i, argc;
if (2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi))
abi = INT2NUM(FFI_DEFAULT_ABI);
Check_Type(args, T_ARRAY);
argc = RARRAY_LENINT(args);
TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *));
for (i = 0; i < argc; i++) {
int type = NUM2INT(RARRAY_AREF(args, i));
cl->argv[i] = INT2FFI_TYPE(type);
}
cl->argv[argc] = NULL;
rb_iv_set(self, "@ctype", ret);
rb_iv_set(self, "@args", args);
cif = &cl->cif;
pcl = cl->pcl;
result = ffi_prep_cif(cif, NUM2INT(abi), argc,
INT2FFI_TYPE(NUM2INT(ret)),
cl->argv);
if (FFI_OK != result)
rb_raise(rb_eRuntimeError, "error prepping CIF %d", result);
#if USE_FFI_CLOSURE_ALLOC
result = ffi_prep_closure_loc(pcl, cif, callback,
(void *)self, cl->code);
#else
result = ffi_prep_closure(pcl, cif, callback, (void *)self);
cl->code = (void *)pcl;
i = mprotect(pcl, sizeof(*pcl), PROT_READ | PROT_EXEC);
if (i) {
rb_sys_fail("mprotect");
}
#endif
if (FFI_OK != result)
rb_raise(rb_eRuntimeError, "error prepping closure %d", result);
return self;
}
static VALUE
to_i(VALUE self)
{
fiddle_closure * cl;
void *code;
TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
code = cl->code;
return PTR2NUM(code);
}
void
Init_fiddle_closure(void)
{
#if 0
mFiddle = rb_define_module("Fiddle"); /* let rdoc know about mFiddle */
#endif
/*
* Document-class: Fiddle::Closure
*
* == Description
*
* An FFI closure wrapper, for handling callbacks.
*
* == Example
*
* closure = Class.new(Fiddle::Closure) {
* def call
* 10
* end
* }.new(Fiddle::TYPE_INT, [])
* #=> #<#<Class:0x0000000150d308>:0x0000000150d240>
* func = Fiddle::Function.new(closure, [], Fiddle::TYPE_INT)
* #=> #<Fiddle::Function:0x00000001516e58>
* func.call
* #=> 10
*/
cFiddleClosure = rb_define_class_under(mFiddle, "Closure", rb_cObject);
rb_define_alloc_func(cFiddleClosure, allocate);
/*
* Document-method: new
*
* call-seq: new(ret, args, abi = Fiddle::DEFAULT)
*
* Construct a new Closure object.
*
* * +ret+ is the C type to be returned
* * +args+ is an Array of arguments, passed to the callback function
* * +abi+ is the abi of the closure
*
* If there is an error in preparing the ffi_cif or ffi_prep_closure,
* then a RuntimeError will be raised.
*/
rb_define_method(cFiddleClosure, "initialize", initialize, -1);
/*
* Document-method: to_i
*
* Returns the memory address for this closure
*/
rb_define_method(cFiddleClosure, "to_i", to_i, 0);
}
/* vim: set noet sw=4 sts=4 */