diff options
Diffstat (limited to 'ext/fiddle/closure.c')
-rw-r--r-- | ext/fiddle/closure.c | 189 |
1 files changed, 150 insertions, 39 deletions
diff --git a/ext/fiddle/closure.c b/ext/fiddle/closure.c index 40cee55e9a..7aa9407619 100644 --- a/ext/fiddle/closure.c +++ b/ext/fiddle/closure.c @@ -1,4 +1,5 @@ #include <fiddle.h> +#include <stdbool.h> #include <ruby/thread.h> int ruby_thread_has_gvl_p(void); /* from internal.h */ @@ -54,8 +55,13 @@ closure_memsize(const void * ptr) } const rb_data_type_t closure_data_type = { - "fiddle/closure", - {0, dealloc, closure_memsize,}, + .wrap_struct_name = "fiddle/closure", + .function = { + .dmark = 0, + .dfree = dealloc, + .dsize = closure_memsize + }, + .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED, }; struct callback_args { @@ -90,7 +96,7 @@ with_gvl_callback(void *ptr) case TYPE_INT: rb_ary_push(params, INT2NUM(*(int *)x->args[i])); break; - case -TYPE_INT: + case TYPE_UINT: rb_ary_push(params, UINT2NUM(*(unsigned int *)x->args[i])); break; case TYPE_VOIDP: @@ -101,19 +107,19 @@ with_gvl_callback(void *ptr) case TYPE_LONG: rb_ary_push(params, LONG2NUM(*(long *)x->args[i])); break; - case -TYPE_LONG: + case TYPE_ULONG: 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: + case TYPE_UCHAR: 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: + case TYPE_USHORT: rb_ary_push(params, UINT2NUM(*(unsigned short *)x->args[i])); break; case TYPE_DOUBLE: @@ -126,10 +132,28 @@ with_gvl_callback(void *ptr) case TYPE_LONG_LONG: rb_ary_push(params, LL2NUM(*(LONG_LONG *)x->args[i])); break; - case -TYPE_LONG_LONG: + case TYPE_ULONG_LONG: rb_ary_push(params, ULL2NUM(*(unsigned LONG_LONG *)x->args[i])); break; #endif + case TYPE_CONST_STRING: + rb_ary_push(params, + rb_str_new_cstr(*((const char **)(x->args[i])))); + break; + case TYPE_BOOL: + if (sizeof(bool) == sizeof(char)) { + rb_ary_push(params, CBOOL2RBBOOL(*(unsigned char *)x->args[i])); + } else if (sizeof(bool) == sizeof(short)) { + rb_ary_push(params, CBOOL2RBBOOL(*(unsigned short *)x->args[i])); + } else if (sizeof(bool) == sizeof(int)) { + rb_ary_push(params, CBOOL2RBBOOL(*(unsigned int *)x->args[i])); + } else if (sizeof(bool) == sizeof(long)) { + rb_ary_push(params, CBOOL2RBBOOL(*(unsigned long *)x->args[i])); + } else { + rb_raise(rb_eNotImpError, "bool isn't supported: %u", + (unsigned int)sizeof(bool)); + } + break; default: rb_raise(rb_eRuntimeError, "closure args: %d", type); } @@ -145,7 +169,7 @@ with_gvl_callback(void *ptr) case TYPE_LONG: *(long *)x->resp = NUM2LONG(ret); break; - case -TYPE_LONG: + case TYPE_ULONG: *(unsigned long *)x->resp = NUM2ULONG(ret); break; case TYPE_CHAR: @@ -153,9 +177,9 @@ with_gvl_callback(void *ptr) case TYPE_INT: *(ffi_sarg *)x->resp = NUM2INT(ret); break; - case -TYPE_CHAR: - case -TYPE_SHORT: - case -TYPE_INT: + case TYPE_UCHAR: + case TYPE_USHORT: + case TYPE_UINT: *(ffi_arg *)x->resp = NUM2UINT(ret); break; case TYPE_VOIDP: @@ -171,10 +195,21 @@ with_gvl_callback(void *ptr) case TYPE_LONG_LONG: *(LONG_LONG *)x->resp = NUM2LL(ret); break; - case -TYPE_LONG_LONG: + case TYPE_ULONG_LONG: *(unsigned LONG_LONG *)x->resp = NUM2ULL(ret); break; #endif + case TYPE_CONST_STRING: + /* Dangerous. Callback must keep reference of the String. */ + *((const char **)(x->resp)) = StringValueCStr(ret); + break; + case TYPE_BOOL: + if (sizeof(bool) == sizeof(long)) { + *(unsigned long *)x->resp = RB_TEST(ret); + } else { + *(ffi_arg *)x->resp = RB_TEST(ret); + } + break; default: rb_raise(rb_eRuntimeError, "closure retval: %d", type); } @@ -216,11 +251,30 @@ allocate(VALUE klass) return i; } +static fiddle_closure * +get_raw(VALUE self) +{ + fiddle_closure *closure; + TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, closure); + if (!closure) { + rb_raise(rb_eArgError, "already freed: %+"PRIsVALUE, self); + } + return closure; +} + +typedef struct { + VALUE self; + int argc; + VALUE *argv; +} initialize_data; + static VALUE -initialize(int rbargc, VALUE argv[], VALUE self) +initialize_body(VALUE user_data) { + initialize_data *data = (initialize_data *)user_data; VALUE ret; VALUE args; + VALUE normalized_args; VALUE abi; fiddle_closure * cl; ffi_cif * cif; @@ -228,65 +282,107 @@ initialize(int rbargc, VALUE argv[], VALUE self) ffi_status result; int i, argc; - if (2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi)) - abi = INT2NUM(FFI_DEFAULT_ABI); + if (2 == rb_scan_args(data->argc, data->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); + TypedData_Get_Struct(data->self, fiddle_closure, &closure_data_type, cl); cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *)); + normalized_args = rb_ary_new_capa(argc); for (i = 0; i < argc; i++) { - int type = NUM2INT(RARRAY_AREF(args, i)); - cl->argv[i] = INT2FFI_TYPE(type); + VALUE arg = rb_fiddle_type_ensure(RARRAY_AREF(args, i)); + rb_ary_push(normalized_args, arg); + cl->argv[i] = rb_fiddle_int_to_ffi_type(NUM2INT(arg)); } cl->argv[argc] = NULL; - rb_iv_set(self, "@ctype", ret); - rb_iv_set(self, "@args", args); + ret = rb_fiddle_type_ensure(ret); + rb_iv_set(data->self, "@ctype", ret); + rb_iv_set(data->self, "@args", normalized_args); cif = &cl->cif; pcl = cl->pcl; - result = ffi_prep_cif(cif, NUM2INT(abi), argc, - INT2FFI_TYPE(NUM2INT(ret)), - cl->argv); + result = ffi_prep_cif(cif, + NUM2INT(abi), + argc, + rb_fiddle_int_to_ffi_type(NUM2INT(ret)), + cl->argv); - if (FFI_OK != result) - rb_raise(rb_eRuntimeError, "error prepping CIF %d", result); + 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); + (void *)(data->self), cl->code); #else - result = ffi_prep_closure(pcl, cif, callback, (void *)self); + result = ffi_prep_closure(pcl, cif, callback, (void *)(data->self)); cl->code = (void *)pcl; i = mprotect(pcl, sizeof(*pcl), PROT_READ | PROT_EXEC); if (i) { - rb_sys_fail("mprotect"); + rb_sys_fail("mprotect"); } #endif - if (FFI_OK != result) - rb_raise(rb_eRuntimeError, "error prepping closure %d", result); + if (FFI_OK != result) { + rb_raise(rb_eRuntimeError, "error prepping closure %d", result); + } - return self; + return data->self; } static VALUE -to_i(VALUE self) +initialize_rescue(VALUE user_data, VALUE exception) { - fiddle_closure * cl; - void *code; + initialize_data *data = (initialize_data *)user_data; + dealloc(RTYPEDDATA_DATA(data->self)); + RTYPEDDATA_DATA(data->self) = NULL; + rb_exc_raise(exception); + return data->self; +} - TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl); +static VALUE +initialize(int argc, VALUE *argv, VALUE self) +{ + initialize_data data; + data.self = self; + data.argc = argc; + data.argv = argv; + return rb_rescue(initialize_body, (VALUE)&data, + initialize_rescue, (VALUE)&data); +} - code = cl->code; +static VALUE +to_i(VALUE self) +{ + fiddle_closure *closure = get_raw(self); + return PTR2NUM(closure->code); +} + +static VALUE +closure_free(VALUE self) +{ + fiddle_closure *closure; + TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, closure); + if (closure) { + dealloc(closure); + RTYPEDDATA_DATA(self) = NULL; + } + return RUBY_Qnil; +} - return PTR2NUM(code); +static VALUE +closure_freed_p(VALUE self) +{ + fiddle_closure *closure; + TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, closure); + return closure ? RUBY_Qfalse : RUBY_Qtrue; } void @@ -339,8 +435,23 @@ Init_fiddle_closure(void) /* * Document-method: to_i * - * Returns the memory address for this closure + * Returns the memory address for this closure. */ rb_define_method(cFiddleClosure, "to_i", to_i, 0); + + /* + * Document-method: free + * + * Free this closure explicitly. You can't use this closure anymore. + * + * If this closure is already freed, this does nothing. + */ + rb_define_method(cFiddleClosure, "free", closure_free, 0); + + /* + * Document-method: freed? + * + * Whether this closure was freed explicitly. + */ + rb_define_method(cFiddleClosure, "freed?", closure_freed_p, 0); } -/* vim: set noet sw=4 sts=4 */ |