ext/fiddle/closure.c

Go to the documentation of this file.
00001 #include <fiddle.h>
00002 
00003 VALUE cFiddleClosure;
00004 
00005 typedef struct {
00006     void * code;
00007     ffi_closure *pcl;
00008     ffi_cif cif;
00009     int argc;
00010     ffi_type **argv;
00011 } fiddle_closure;
00012 
00013 #if defined(USE_FFI_CLOSURE_ALLOC)
00014 #elif defined(__OpenBSD__) || defined(__APPLE__) || defined(__linux__)
00015 # define USE_FFI_CLOSURE_ALLOC 0
00016 #elif defined(RUBY_LIBFFI_MODVERSION) && RUBY_LIBFFI_MODVERSION < 3000005 && \
00017         (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_AMD64))
00018 # define USE_FFI_CLOSURE_ALLOC 0
00019 #else
00020 # define USE_FFI_CLOSURE_ALLOC 1
00021 #endif
00022 
00023 static void
00024 dealloc(void * ptr)
00025 {
00026     fiddle_closure * cls = (fiddle_closure *)ptr;
00027 #if USE_FFI_CLOSURE_ALLOC
00028     ffi_closure_free(cls->pcl);
00029 #else
00030     munmap(cls->pcl, sizeof(*cls->pcl));
00031 #endif
00032     if (cls->argv) xfree(cls->argv);
00033     xfree(cls);
00034 }
00035 
00036 static size_t
00037 closure_memsize(const void * ptr)
00038 {
00039     fiddle_closure * cls = (fiddle_closure *)ptr;
00040     size_t size = 0;
00041 
00042     if (ptr) {
00043         size += sizeof(*cls);
00044 #if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
00045         size += ffi_raw_size(&cls->cif);
00046 #endif
00047         size += sizeof(*cls->argv);
00048         size += sizeof(ffi_closure);
00049     }
00050     return size;
00051 }
00052 
00053 const rb_data_type_t closure_data_type = {
00054     "fiddle/closure",
00055     {0, dealloc, closure_memsize,},
00056 };
00057 
00058 void
00059 callback(ffi_cif *cif, void *resp, void **args, void *ctx)
00060 {
00061     VALUE self      = (VALUE)ctx;
00062     VALUE rbargs    = rb_iv_get(self, "@args");
00063     VALUE ctype     = rb_iv_get(self, "@ctype");
00064     int argc        = RARRAY_LENINT(rbargs);
00065     VALUE params    = rb_ary_tmp_new(argc);
00066     VALUE ret;
00067     VALUE cPointer;
00068     int i, type;
00069 
00070     cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
00071 
00072     for (i = 0; i < argc; i++) {
00073         type = NUM2INT(RARRAY_PTR(rbargs)[i]);
00074         switch (type) {
00075           case TYPE_VOID:
00076             argc = 0;
00077             break;
00078           case TYPE_INT:
00079             rb_ary_push(params, INT2NUM(*(int *)args[i]));
00080             break;
00081           case -TYPE_INT:
00082             rb_ary_push(params, UINT2NUM(*(unsigned int *)args[i]));
00083             break;
00084           case TYPE_VOIDP:
00085             rb_ary_push(params,
00086                         rb_funcall(cPointer, rb_intern("[]"), 1,
00087                                    PTR2NUM(*(void **)args[i])));
00088             break;
00089           case TYPE_LONG:
00090             rb_ary_push(params, LONG2NUM(*(long *)args[i]));
00091             break;
00092           case -TYPE_LONG:
00093             rb_ary_push(params, ULONG2NUM(*(unsigned long *)args[i]));
00094             break;
00095           case TYPE_CHAR:
00096             rb_ary_push(params, INT2NUM(*(signed char *)args[i]));
00097             break;
00098           case -TYPE_CHAR:
00099             rb_ary_push(params, UINT2NUM(*(unsigned char *)args[i]));
00100             break;
00101           case TYPE_SHORT:
00102             rb_ary_push(params, INT2NUM(*(signed short *)args[i]));
00103             break;
00104           case -TYPE_SHORT:
00105             rb_ary_push(params, UINT2NUM(*(unsigned short *)args[i]));
00106             break;
00107           case TYPE_DOUBLE:
00108             rb_ary_push(params, rb_float_new(*(double *)args[i]));
00109             break;
00110           case TYPE_FLOAT:
00111             rb_ary_push(params, rb_float_new(*(float *)args[i]));
00112             break;
00113 #if HAVE_LONG_LONG
00114           case TYPE_LONG_LONG:
00115             rb_ary_push(params, LL2NUM(*(LONG_LONG *)args[i]));
00116             break;
00117           case -TYPE_LONG_LONG:
00118             rb_ary_push(params, ULL2NUM(*(unsigned LONG_LONG *)args[i]));
00119             break;
00120 #endif
00121           default:
00122             rb_raise(rb_eRuntimeError, "closure args: %d", type);
00123         }
00124     }
00125 
00126     ret = rb_funcall2(self, rb_intern("call"), argc, RARRAY_PTR(params));
00127     RB_GC_GUARD(params);
00128 
00129     type = NUM2INT(ctype);
00130     switch (type) {
00131       case TYPE_VOID:
00132         break;
00133       case TYPE_LONG:
00134         *(long *)resp = NUM2LONG(ret);
00135         break;
00136       case -TYPE_LONG:
00137         *(unsigned long *)resp = NUM2ULONG(ret);
00138         break;
00139       case TYPE_CHAR:
00140       case TYPE_SHORT:
00141       case TYPE_INT:
00142         *(ffi_sarg *)resp = NUM2INT(ret);
00143         break;
00144       case -TYPE_CHAR:
00145       case -TYPE_SHORT:
00146       case -TYPE_INT:
00147         *(ffi_arg *)resp = NUM2UINT(ret);
00148         break;
00149       case TYPE_VOIDP:
00150         *(void **)resp = NUM2PTR(ret);
00151         break;
00152       case TYPE_DOUBLE:
00153         *(double *)resp = NUM2DBL(ret);
00154         break;
00155       case TYPE_FLOAT:
00156         *(float *)resp = (float)NUM2DBL(ret);
00157         break;
00158 #if HAVE_LONG_LONG
00159       case TYPE_LONG_LONG:
00160         *(LONG_LONG *)resp = NUM2LL(ret);
00161         break;
00162       case -TYPE_LONG_LONG:
00163         *(unsigned LONG_LONG *)resp = NUM2ULL(ret);
00164         break;
00165 #endif
00166       default:
00167         rb_raise(rb_eRuntimeError, "closure retval: %d", type);
00168     }
00169 }
00170 
00171 static VALUE
00172 allocate(VALUE klass)
00173 {
00174     fiddle_closure * closure;
00175 
00176     VALUE i = TypedData_Make_Struct(klass, fiddle_closure,
00177             &closure_data_type, closure);
00178 
00179 #if USE_FFI_CLOSURE_ALLOC
00180     closure->pcl = ffi_closure_alloc(sizeof(ffi_closure), &closure->code);
00181 #else
00182     closure->pcl = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE,
00183         MAP_ANON | MAP_PRIVATE, -1, 0);
00184 #endif
00185 
00186     return i;
00187 }
00188 
00189 static VALUE
00190 initialize(int rbargc, VALUE argv[], VALUE self)
00191 {
00192     VALUE ret;
00193     VALUE args;
00194     VALUE abi;
00195     fiddle_closure * cl;
00196     ffi_cif * cif;
00197     ffi_closure *pcl;
00198     ffi_status result;
00199     int i, argc;
00200 
00201     if (2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi))
00202         abi = INT2NUM(FFI_DEFAULT_ABI);
00203 
00204     Check_Type(args, T_ARRAY);
00205 
00206     argc = RARRAY_LENINT(args);
00207 
00208     TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
00209 
00210     cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *));
00211 
00212     for (i = 0; i < argc; i++) {
00213         int type = NUM2INT(RARRAY_PTR(args)[i]);
00214         cl->argv[i] = INT2FFI_TYPE(type);
00215     }
00216     cl->argv[argc] = NULL;
00217 
00218     rb_iv_set(self, "@ctype", ret);
00219     rb_iv_set(self, "@args", args);
00220 
00221     cif = &cl->cif;
00222     pcl = cl->pcl;
00223 
00224     result = ffi_prep_cif(cif, NUM2INT(abi), argc,
00225                 INT2FFI_TYPE(NUM2INT(ret)),
00226                 cl->argv);
00227 
00228     if (FFI_OK != result)
00229         rb_raise(rb_eRuntimeError, "error prepping CIF %d", result);
00230 
00231 #if USE_FFI_CLOSURE_ALLOC
00232     result = ffi_prep_closure_loc(pcl, cif, callback,
00233                 (void *)self, cl->code);
00234 #else
00235     result = ffi_prep_closure(pcl, cif, callback, (void *)self);
00236     cl->code = (void *)pcl;
00237     i = mprotect(pcl, sizeof(*pcl), PROT_READ | PROT_EXEC);
00238     if (i) {
00239         rb_sys_fail("mprotect");
00240     }
00241 #endif
00242 
00243     if (FFI_OK != result)
00244         rb_raise(rb_eRuntimeError, "error prepping closure %d", result);
00245 
00246     return self;
00247 }
00248 
00249 static VALUE
00250 to_i(VALUE self)
00251 {
00252     fiddle_closure * cl;
00253     void *code;
00254 
00255     TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
00256 
00257     code = cl->code;
00258 
00259     return PTR2NUM(code);
00260 }
00261 
00262 void
00263 Init_fiddle_closure()
00264 {
00265 #if 0
00266     mFiddle = rb_define_module("Fiddle"); /* let rdoc know about mFiddle */
00267 #endif
00268 
00269     /*
00270      * Document-class: Fiddle::Closure
00271      *
00272      * == Description
00273      *
00274      * An FFI closure wrapper, for handling callbacks.
00275      *
00276      * == Example
00277      *
00278      *   closure = Class.new(Fiddle::Closure) {
00279      *     def call
00280      *       10
00281      *     end
00282      *   }.new(Fiddle::TYPE_INT, [])
00283      *      #=> #<#<Class:0x0000000150d308>:0x0000000150d240>
00284      *   func = Fiddle::Function.new(closure, [], Fiddle::TYPE_INT)
00285      *      #=> #<Fiddle::Function:0x00000001516e58>
00286      *   func.call
00287      *      #=> 10
00288      */
00289     cFiddleClosure = rb_define_class_under(mFiddle, "Closure", rb_cObject);
00290 
00291     rb_define_alloc_func(cFiddleClosure, allocate);
00292 
00293     /*
00294      * Document-method: new
00295      *
00296      * call-seq: new(ret, args, abi = Fiddle::DEFAULT)
00297      *
00298      * Construct a new Closure object.
00299      *
00300      * * +ret+ is the C type to be returned
00301      * * +args+ is an Array of arguments, passed to the callback function
00302      * * +abi+ is the abi of the closure
00303      *
00304      * If there is an error in preparing the ffi_cif or ffi_prep_closure,
00305      * then a RuntimeError will be raised.
00306      */
00307     rb_define_method(cFiddleClosure, "initialize", initialize, -1);
00308 
00309     /*
00310      * Document-method: to_i
00311      *
00312      * Returns the memory address for this closure
00313      */
00314     rb_define_method(cFiddleClosure, "to_i", to_i, 0);
00315 }
00316 /* vim: set noet sw=4 sts=4 */
00317 

Generated on 19 Jul 2016 for Ruby by  doxygen 1.4.7