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