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     xfree(cls->cif);
00027     if (cls->argv) xfree(cls->argv);
00028     xfree(cls);
00029 }
00030 
00031 static size_t
00032 closure_memsize(const void * ptr)
00033 {
00034     fiddle_closure * cls = (fiddle_closure *)ptr;
00035     size_t size = 0;
00036 
00037     if (ptr) {
00038         size += sizeof(*cls);
00039 #if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
00040         size += ffi_raw_size(cls->cif);
00041 #endif
00042         size += sizeof(*cls->argv);
00043         size += sizeof(ffi_closure);
00044     }
00045     return size;
00046 }
00047 
00048 const rb_data_type_t closure_data_type = {
00049     "fiddle/closure",
00050     0, dealloc, closure_memsize,
00051 };
00052 
00053 void
00054 callback(ffi_cif *cif, void *resp, void **args, void *ctx)
00055 {
00056     VALUE self      = (VALUE)ctx;
00057     VALUE rbargs    = rb_iv_get(self, "@args");
00058     VALUE ctype     = rb_iv_get(self, "@ctype");
00059     int argc        = RARRAY_LENINT(rbargs);
00060     VALUE *params   = xcalloc(argc, sizeof(VALUE *));
00061     VALUE ret;
00062     VALUE cPointer;
00063     int i, type;
00064 
00065     cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
00066 
00067     for (i = 0; i < argc; i++) {
00068         type = NUM2INT(RARRAY_PTR(rbargs)[i]);
00069         switch (type) {
00070           case TYPE_VOID:
00071             argc = 0;
00072             break;
00073           case TYPE_INT:
00074             params[i] = INT2NUM(*(int *)args[i]);
00075             break;
00076           case TYPE_VOIDP:
00077             params[i] = rb_funcall(cPointer, rb_intern("[]"), 1,
00078               PTR2NUM(*(void **)args[i]));
00079             break;
00080           case TYPE_LONG:
00081             params[i] = LONG2NUM(*(long *)args[i]);
00082             break;
00083           case TYPE_CHAR:
00084             params[i] = INT2NUM(*(char *)args[i]);
00085             break;
00086           case TYPE_DOUBLE:
00087             params[i] = rb_float_new(*(double *)args[i]);
00088             break;
00089           case TYPE_FLOAT:
00090             params[i] = rb_float_new(*(float *)args[i]);
00091             break;
00092 #if HAVE_LONG_LONG
00093           case TYPE_LONG_LONG:
00094             params[i] = rb_ull2inum(*(unsigned LONG_LONG *)args[i]);
00095             break;
00096 #endif
00097           default:
00098             rb_raise(rb_eRuntimeError, "closure args: %d", type);
00099         }
00100     }
00101 
00102     ret = rb_funcall2(self, rb_intern("call"), argc, params);
00103 
00104     type = NUM2INT(ctype);
00105     switch (type) {
00106       case TYPE_VOID:
00107         break;
00108       case TYPE_LONG:
00109         *(long *)resp = NUM2LONG(ret);
00110         break;
00111       case TYPE_CHAR:
00112         *(char *)resp = NUM2INT(ret);
00113         break;
00114       case TYPE_VOIDP:
00115         *(void **)resp = NUM2PTR(ret);
00116         break;
00117       case TYPE_INT:
00118         *(int *)resp = NUM2INT(ret);
00119         break;
00120       case TYPE_DOUBLE:
00121         *(double *)resp = NUM2DBL(ret);
00122         break;
00123       case TYPE_FLOAT:
00124         *(float *)resp = (float)NUM2DBL(ret);
00125         break;
00126 #if HAVE_LONG_LONG
00127       case TYPE_LONG_LONG:
00128         *(unsigned LONG_LONG *)resp = rb_big2ull(ret);
00129         break;
00130 #endif
00131       default:
00132         rb_raise(rb_eRuntimeError, "closure retval: %d", type);
00133     }
00134     xfree(params);
00135 }
00136 
00137 static VALUE
00138 allocate(VALUE klass)
00139 {
00140     fiddle_closure * closure;
00141 
00142     VALUE i = TypedData_Make_Struct(klass, fiddle_closure,
00143             &closure_data_type, closure);
00144 
00145 #ifndef DONT_USE_FFI_CLOSURE_ALLOC
00146     closure->pcl = ffi_closure_alloc(sizeof(ffi_closure), &closure->code);
00147 #else
00148     closure->pcl = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE,
00149         MAP_ANON | MAP_PRIVATE, -1, 0);
00150 #endif
00151     closure->cif = xmalloc(sizeof(ffi_cif));
00152 
00153     return i;
00154 }
00155 
00156 static VALUE
00157 initialize(int rbargc, VALUE argv[], VALUE self)
00158 {
00159     VALUE ret;
00160     VALUE args;
00161     VALUE abi;
00162     fiddle_closure * cl;
00163     ffi_cif * cif;
00164     ffi_closure *pcl;
00165     ffi_status result;
00166     int i, argc;
00167 
00168     if (2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi))
00169         abi = INT2NUM(FFI_DEFAULT_ABI);
00170 
00171     Check_Type(args, T_ARRAY);
00172 
00173     argc = RARRAY_LENINT(args);
00174 
00175     TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
00176 
00177     cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *));
00178 
00179     for (i = 0; i < argc; i++) {
00180         int type = NUM2INT(RARRAY_PTR(args)[i]);
00181         cl->argv[i] = INT2FFI_TYPE(type);
00182     }
00183     cl->argv[argc] = NULL;
00184 
00185     rb_iv_set(self, "@ctype", ret);
00186     rb_iv_set(self, "@args", args);
00187 
00188     cif = cl->cif;
00189     pcl = cl->pcl;
00190 
00191     result = ffi_prep_cif(cif, NUM2INT(abi), argc,
00192                 INT2FFI_TYPE(NUM2INT(ret)),
00193                 cl->argv);
00194 
00195     if (FFI_OK != result)
00196         rb_raise(rb_eRuntimeError, "error prepping CIF %d", result);
00197 
00198 #ifndef DONT_USE_FFI_CLOSURE_ALLOC
00199     result = ffi_prep_closure_loc(pcl, cif, callback,
00200                 (void *)self, cl->code);
00201 #else
00202     result = ffi_prep_closure(pcl, cif, callback, (void *)self);
00203     cl->code = (void *)pcl;
00204     mprotect(pcl, sizeof(pcl), PROT_READ | PROT_EXEC);
00205 #endif
00206 
00207     if (FFI_OK != result)
00208         rb_raise(rb_eRuntimeError, "error prepping closure %d", result);
00209 
00210     return self;
00211 }
00212 
00213 static VALUE
00214 to_i(VALUE self)
00215 {
00216     fiddle_closure * cl;
00217     void *code;
00218 
00219     TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
00220 
00221     code = cl->code;
00222 
00223     return PTR2NUM(code);
00224 }
00225 
00226 void
00227 Init_fiddle_closure()
00228 {
00229     cFiddleClosure = rb_define_class_under(mFiddle, "Closure", rb_cObject);
00230 
00231     rb_define_alloc_func(cFiddleClosure, allocate);
00232 
00233     rb_define_method(cFiddleClosure, "initialize", initialize, -1);
00234     rb_define_method(cFiddleClosure, "to_i", to_i, 0);
00235 }
00236 /* vim: set noet sw=4 sts=4 */
00237 

Generated on Wed Aug 10 09:16:59 2011 for Ruby by  doxygen 1.4.7