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