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