Ruby  2.4.2p198(2017-09-14revision59899)
closure.c
Go to the documentation of this file.
1 #include <fiddle.h>
2 #include <ruby/thread.h>
3 #include "internal.h" /* rb_thread_has_gvl_p */
4 
6 
7 typedef struct {
8  void * code;
9  ffi_closure *pcl;
10  ffi_cif cif;
11  int argc;
12  ffi_type **argv;
14 
15 #if defined(USE_FFI_CLOSURE_ALLOC)
16 #elif defined(__OpenBSD__) || defined(__APPLE__) || defined(__linux__)
17 # define USE_FFI_CLOSURE_ALLOC 0
18 #elif defined(RUBY_LIBFFI_MODVERSION) && RUBY_LIBFFI_MODVERSION < 3000005 && \
19  (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_AMD64))
20 # define USE_FFI_CLOSURE_ALLOC 0
21 #else
22 # define USE_FFI_CLOSURE_ALLOC 1
23 #endif
24 
25 static void
26 dealloc(void * ptr)
27 {
28  fiddle_closure * cls = (fiddle_closure *)ptr;
29 #if USE_FFI_CLOSURE_ALLOC
30  ffi_closure_free(cls->pcl);
31 #else
32  munmap(cls->pcl, sizeof(*cls->pcl));
33 #endif
34  if (cls->argv) xfree(cls->argv);
35  xfree(cls);
36 }
37 
38 static size_t
39 closure_memsize(const void * ptr)
40 {
41  fiddle_closure * cls = (fiddle_closure *)ptr;
42  size_t size = 0;
43 
44  size += sizeof(*cls);
45 #if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
46  size += ffi_raw_size(&cls->cif);
47 #endif
48  size += sizeof(*cls->argv);
49  size += sizeof(ffi_closure);
50 
51  return size;
52 }
53 
55  "fiddle/closure",
56  {0, dealloc, closure_memsize,},
57 };
58 
59 struct callback_args {
60  ffi_cif *cif;
61  void *resp;
62  void **args;
63  void *ctx;
64 };
65 
66 static void *
68 {
69  struct callback_args *x = ptr;
70 
71  VALUE self = (VALUE)x->ctx;
72  VALUE rbargs = rb_iv_get(self, "@args");
73  VALUE ctype = rb_iv_get(self, "@ctype");
74  int argc = RARRAY_LENINT(rbargs);
75  VALUE params = rb_ary_tmp_new(argc);
76  VALUE ret;
77  VALUE cPointer;
78  int i, type;
79 
80  cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
81 
82  for (i = 0; i < argc; i++) {
83  type = NUM2INT(RARRAY_AREF(rbargs, i));
84  switch (type) {
85  case TYPE_VOID:
86  argc = 0;
87  break;
88  case TYPE_INT:
89  rb_ary_push(params, INT2NUM(*(int *)x->args[i]));
90  break;
91  case -TYPE_INT:
92  rb_ary_push(params, UINT2NUM(*(unsigned int *)x->args[i]));
93  break;
94  case TYPE_VOIDP:
95  rb_ary_push(params,
96  rb_funcall(cPointer, rb_intern("[]"), 1,
97  PTR2NUM(*(void **)x->args[i])));
98  break;
99  case TYPE_LONG:
100  rb_ary_push(params, LONG2NUM(*(long *)x->args[i]));
101  break;
102  case -TYPE_LONG:
103  rb_ary_push(params, ULONG2NUM(*(unsigned long *)x->args[i]));
104  break;
105  case TYPE_CHAR:
106  rb_ary_push(params, INT2NUM(*(signed char *)x->args[i]));
107  break;
108  case -TYPE_CHAR:
109  rb_ary_push(params, UINT2NUM(*(unsigned char *)x->args[i]));
110  break;
111  case TYPE_SHORT:
112  rb_ary_push(params, INT2NUM(*(signed short *)x->args[i]));
113  break;
114  case -TYPE_SHORT:
115  rb_ary_push(params, UINT2NUM(*(unsigned short *)x->args[i]));
116  break;
117  case TYPE_DOUBLE:
118  rb_ary_push(params, rb_float_new(*(double *)x->args[i]));
119  break;
120  case TYPE_FLOAT:
121  rb_ary_push(params, rb_float_new(*(float *)x->args[i]));
122  break;
123 #if HAVE_LONG_LONG
124  case TYPE_LONG_LONG:
125  rb_ary_push(params, LL2NUM(*(LONG_LONG *)x->args[i]));
126  break;
127  case -TYPE_LONG_LONG:
128  rb_ary_push(params, ULL2NUM(*(unsigned LONG_LONG *)x->args[i]));
129  break;
130 #endif
131  default:
132  rb_raise(rb_eRuntimeError, "closure args: %d", type);
133  }
134  }
135 
136  ret = rb_funcall2(self, rb_intern("call"), argc, RARRAY_CONST_PTR(params));
137  RB_GC_GUARD(params);
138 
139  type = NUM2INT(ctype);
140  switch (type) {
141  case TYPE_VOID:
142  break;
143  case TYPE_LONG:
144  *(long *)x->resp = NUM2LONG(ret);
145  break;
146  case -TYPE_LONG:
147  *(unsigned long *)x->resp = NUM2ULONG(ret);
148  break;
149  case TYPE_CHAR:
150  case TYPE_SHORT:
151  case TYPE_INT:
152  *(ffi_sarg *)x->resp = NUM2INT(ret);
153  break;
154  case -TYPE_CHAR:
155  case -TYPE_SHORT:
156  case -TYPE_INT:
157  *(ffi_arg *)x->resp = NUM2UINT(ret);
158  break;
159  case TYPE_VOIDP:
160  *(void **)x->resp = NUM2PTR(ret);
161  break;
162  case TYPE_DOUBLE:
163  *(double *)x->resp = NUM2DBL(ret);
164  break;
165  case TYPE_FLOAT:
166  *(float *)x->resp = (float)NUM2DBL(ret);
167  break;
168 #if HAVE_LONG_LONG
169  case TYPE_LONG_LONG:
170  *(LONG_LONG *)x->resp = NUM2LL(ret);
171  break;
172  case -TYPE_LONG_LONG:
173  *(unsigned LONG_LONG *)x->resp = NUM2ULL(ret);
174  break;
175 #endif
176  default:
177  rb_raise(rb_eRuntimeError, "closure retval: %d", type);
178  }
179  return 0;
180 }
181 
182 static void
183 callback(ffi_cif *cif, void *resp, void **args, void *ctx)
184 {
185  struct callback_args x;
186 
187  x.cif = cif;
188  x.resp = resp;
189  x.args = args;
190  x.ctx = ctx;
191 
192  if (ruby_thread_has_gvl_p()) {
193  (void)with_gvl_callback(&x);
194  } else {
196  }
197 }
198 
199 static VALUE
201 {
202  fiddle_closure * closure;
203 
205  &closure_data_type, closure);
206 
207 #if USE_FFI_CLOSURE_ALLOC
208  closure->pcl = ffi_closure_alloc(sizeof(ffi_closure), &closure->code);
209 #else
210  closure->pcl = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE,
211  MAP_ANON | MAP_PRIVATE, -1, 0);
212 #endif
213 
214  return i;
215 }
216 
217 static VALUE
218 initialize(int rbargc, VALUE argv[], VALUE self)
219 {
220  VALUE ret;
221  VALUE args;
222  VALUE abi;
223  fiddle_closure * cl;
224  ffi_cif * cif;
225  ffi_closure *pcl;
226  ffi_status result;
227  int i, argc;
228 
229  if (2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi))
230  abi = INT2NUM(FFI_DEFAULT_ABI);
231 
232  Check_Type(args, T_ARRAY);
233 
234  argc = RARRAY_LENINT(args);
235 
236  TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
237 
238  cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *));
239 
240  for (i = 0; i < argc; i++) {
241  int type = NUM2INT(RARRAY_AREF(args, i));
242  cl->argv[i] = INT2FFI_TYPE(type);
243  }
244  cl->argv[argc] = NULL;
245 
246  rb_iv_set(self, "@ctype", ret);
247  rb_iv_set(self, "@args", args);
248 
249  cif = &cl->cif;
250  pcl = cl->pcl;
251 
252  result = ffi_prep_cif(cif, NUM2INT(abi), argc,
253  INT2FFI_TYPE(NUM2INT(ret)),
254  cl->argv);
255 
256  if (FFI_OK != result)
257  rb_raise(rb_eRuntimeError, "error prepping CIF %d", result);
258 
259 #if USE_FFI_CLOSURE_ALLOC
260  result = ffi_prep_closure_loc(pcl, cif, callback,
261  (void *)self, cl->code);
262 #else
263  result = ffi_prep_closure(pcl, cif, callback, (void *)self);
264  cl->code = (void *)pcl;
265  i = mprotect(pcl, sizeof(*pcl), PROT_READ | PROT_EXEC);
266  if (i) {
267  rb_sys_fail("mprotect");
268  }
269 #endif
270 
271  if (FFI_OK != result)
272  rb_raise(rb_eRuntimeError, "error prepping closure %d", result);
273 
274  return self;
275 }
276 
277 static VALUE
278 to_i(VALUE self)
279 {
280  fiddle_closure * cl;
281  void *code;
282 
283  TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
284 
285  code = cl->code;
286 
287  return PTR2NUM(code);
288 }
289 
290 void
292 {
293 #if 0
294  mFiddle = rb_define_module("Fiddle"); /* let rdoc know about mFiddle */
295 #endif
296 
297  /*
298  * Document-class: Fiddle::Closure
299  *
300  * == Description
301  *
302  * An FFI closure wrapper, for handling callbacks.
303  *
304  * == Example
305  *
306  * closure = Class.new(Fiddle::Closure) {
307  * def call
308  * 10
309  * end
310  * }.new(Fiddle::TYPE_INT, [])
311  * #=> #<#<Class:0x0000000150d308>:0x0000000150d240>
312  * func = Fiddle::Function.new(closure, [], Fiddle::TYPE_INT)
313  * #=> #<Fiddle::Function:0x00000001516e58>
314  * func.call
315  * #=> 10
316  */
318 
320 
321  /*
322  * Document-method: new
323  *
324  * call-seq: new(ret, args, abi = Fiddle::DEFAULT)
325  *
326  * Construct a new Closure object.
327  *
328  * * +ret+ is the C type to be returned
329  * * +args+ is an Array of arguments, passed to the callback function
330  * * +abi+ is the abi of the closure
331  *
332  * If there is an error in preparing the ffi_cif or ffi_prep_closure,
333  * then a RuntimeError will be raised.
334  */
335  rb_define_method(cFiddleClosure, "initialize", initialize, -1);
336 
337  /*
338  * Document-method: to_i
339  *
340  * Returns the memory address for this closure
341  */
342  rb_define_method(cFiddleClosure, "to_i", to_i, 0);
343 }
344 /* vim: set noet sw=4 sts=4 */
#define TYPE_VOIDP
Definition: fiddle.h:108
VALUE mFiddle
Definition: fiddle.c:3
#define INT2NUM(x)
Definition: ruby.h:1538
#define NUM2INT(x)
Definition: ruby.h:684
#define NUM2UINT(x)
Definition: ruby.h:685
void * resp
Definition: closure.c:61
#define TypedData_Get_Struct(obj, type, data_type, sval)
Definition: ruby.h:1190
#define ULONG2NUM(x)
Definition: ruby.h:1574
VALUE rb_ary_push(VALUE ary, VALUE item)
Definition: array.c:905
VALUE rb_ary_tmp_new(long capa)
Definition: array.c:532
VALUE rb_funcall(VALUE, ID, int,...)
Calls a method.
Definition: vm_eval.c:821
VALUE rb_iv_set(VALUE, const char *, VALUE)
Definition: variable.c:3138
VALUE rb_iv_get(VALUE, const char *)
Definition: variable.c:3130
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:693
ffi_cif * cif
Definition: closure.c:60
#define Check_Type(v, t)
Definition: ruby.h:562
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2207
#define TYPE_CHAR
Definition: fiddle.h:109
#define RB_GC_GUARD(v)
Definition: ruby.h:552
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
int ruby_thread_has_gvl_p(void)
Definition: thread.c:1541
#define T_ARRAY
Definition: ruby.h:498
#define TYPE_SHORT
Definition: fiddle.h:110
signed long ffi_sarg
Definition: ffitarget.h:31
void * ctx
Definition: closure.c:63
#define NUM2DBL(x)
Definition: ruby.h:743
unsigned long ffi_arg
Definition: ffitarget.h:30
#define TYPE_INT
Definition: fiddle.h:111
#define TYPE_DOUBLE
Definition: fiddle.h:117
#define TYPE_LONG
Definition: fiddle.h:112
static void * with_gvl_callback(void *ptr)
Definition: closure.c:67
RUBY_EXTERN VALUE rb_cObject
Definition: ruby.h:1872
VALUE rb_eRuntimeError
Definition: error.c:761
#define rb_float_new(d)
Definition: internal.h:1296
static VALUE to_i(VALUE self)
Definition: closure.c:278
#define UINT2NUM(x)
Definition: ruby.h:1539
const rb_data_type_t closure_data_type
Definition: closure.c:54
int argc
Definition: ruby.c:183
static void callback(ffi_cif *cif, void *resp, void **args, void *ctx)
Definition: closure.c:183
ffi_closure * pcl
Definition: closure.c:9
#define INT2FFI_TYPE(_type)
Definition: conversions.h:32
ffi_type ** argv
Definition: closure.c:12
void ** args
Definition: closure.c:62
ffi_status ffi_prep_closure_loc(ffi_closure *closure, ffi_cif *cif, void(*fun)(ffi_cif *, void *, void **, void *), void *user_data, void *codeloc)
Definition: ffi.c:928
static void dealloc(void *ptr)
Definition: closure.c:26
VALUE rb_const_get(VALUE, ID)
Definition: variable.c:2335
#define RARRAY_CONST_PTR(a)
Definition: ruby.h:1028
static VALUE initialize(int rbargc, VALUE argv[], VALUE self)
Definition: closure.c:218
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:1919
unsigned long VALUE
Definition: ruby.h:85
#define rb_funcall2
Definition: ruby.h:1770
static VALUE result
Definition: nkf.c:40
#define PTR2NUM(x)
Definition: conversions.h:36
ffi_cif cif
Definition: closure.c:10
#define RARRAY_LENINT(ary)
Definition: ruby.h:1027
#define NUM2PTR(x)
Definition: conversions.h:37
void rb_sys_fail(const char *mesg)
Definition: error.c:2326
#define LONG2NUM(x)
Definition: ruby.h:1573
#define TYPE_FLOAT
Definition: fiddle.h:116
static VALUE allocate(VALUE klass)
Definition: closure.c:200
VALUE cFiddleClosure
Definition: closure.c:5
int size
Definition: encoding.c:57
#define RARRAY_AREF(a, i)
Definition: ruby.h:1040
#define TYPE_VOID
Definition: fiddle.h:107
#define NUM2ULONG(x)
Definition: ruby.h:658
RUBY_SYMBOL_EXPORT_BEGIN void * rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
Definition: thread.c:1499
#define TypedData_Make_Struct(klass, type, data_type, sval)
Definition: ruby.h:1182
void Init_fiddle_closure(void)
Definition: closure.c:291
void void xfree(void *)
void * code
Definition: closure.c:8
VALUE rb_define_module(const char *name)
Definition: class.c:768
#define rb_intern(str)
size_t ffi_raw_size(ffi_cif *cif)
Definition: raw_api.c:35
#define NULL
Definition: _sdbm.c:102
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Definition: class.c:1515
static size_t closure_memsize(const void *ptr)
Definition: closure.c:39
#define NUM2LONG(x)
Definition: ruby.h:648
char ** argv
Definition: ruby.c:184
ffi_status ffi_prep_cif(ffi_cif *cif, ffi_abi abi, unsigned int nargs, ffi_type *rtype, ffi_type **atypes)
Definition: prep_cif.c:226
#define xcalloc
Definition: defines.h:185