Ruby  3.1.0dev(2021-09-10revisionb76ad15ed0da636161de0243c547ee1e6fc95681)
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(__OpenBSD__)
17 # define USE_FFI_CLOSURE_ALLOC 0
18 #endif
19 
20 #if defined(USE_FFI_CLOSURE_ALLOC)
21 #elif !defined(HAVE_FFI_CLOSURE_ALLOC)
22 # define USE_FFI_CLOSURE_ALLOC 0
23 #else
24 # define USE_FFI_CLOSURE_ALLOC 1
25 #endif
26 
27 static void
28 dealloc(void * ptr)
29 {
31 #if USE_FFI_CLOSURE_ALLOC
32  ffi_closure_free(cls->pcl);
33 #else
34  munmap(cls->pcl, sizeof(*cls->pcl));
35 #endif
36  if (cls->argv) xfree(cls->argv);
37  xfree(cls);
38 }
39 
40 static size_t
41 closure_memsize(const void * ptr)
42 {
44  size_t size = 0;
45 
46  size += sizeof(*cls);
47 #if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
48  size += ffi_raw_size(&cls->cif);
49 #endif
50  size += sizeof(*cls->argv);
51  size += sizeof(ffi_closure);
52 
53  return size;
54 }
55 
57  "fiddle/closure",
58  {0, dealloc, closure_memsize,},
59 };
60 
61 struct callback_args {
62  ffi_cif *cif;
63  void *resp;
64  void **args;
65  void *ctx;
66 };
67 
68 static void *
69 with_gvl_callback(void *ptr)
70 {
71  struct callback_args *x = ptr;
72 
73  VALUE self = (VALUE)x->ctx;
74  VALUE rbargs = rb_iv_get(self, "@args");
75  VALUE ctype = rb_iv_get(self, "@ctype");
76  int argc = RARRAY_LENINT(rbargs);
77  VALUE params = rb_ary_tmp_new(argc);
78  VALUE ret;
79  VALUE cPointer;
80  int i, type;
81 
82  cPointer = rb_const_get(mFiddle, rb_intern("Pointer"));
83 
84  for (i = 0; i < argc; i++) {
85  type = NUM2INT(RARRAY_AREF(rbargs, i));
86  switch (type) {
87  case TYPE_VOID:
88  argc = 0;
89  break;
90  case TYPE_INT:
91  rb_ary_push(params, INT2NUM(*(int *)x->args[i]));
92  break;
93  case -TYPE_INT:
94  rb_ary_push(params, UINT2NUM(*(unsigned int *)x->args[i]));
95  break;
96  case TYPE_VOIDP:
97  rb_ary_push(params,
98  rb_funcall(cPointer, rb_intern("[]"), 1,
99  PTR2NUM(*(void **)x->args[i])));
100  break;
101  case TYPE_LONG:
102  rb_ary_push(params, LONG2NUM(*(long *)x->args[i]));
103  break;
104  case -TYPE_LONG:
105  rb_ary_push(params, ULONG2NUM(*(unsigned long *)x->args[i]));
106  break;
107  case TYPE_CHAR:
108  rb_ary_push(params, INT2NUM(*(signed char *)x->args[i]));
109  break;
110  case -TYPE_CHAR:
111  rb_ary_push(params, UINT2NUM(*(unsigned char *)x->args[i]));
112  break;
113  case TYPE_SHORT:
114  rb_ary_push(params, INT2NUM(*(signed short *)x->args[i]));
115  break;
116  case -TYPE_SHORT:
117  rb_ary_push(params, UINT2NUM(*(unsigned short *)x->args[i]));
118  break;
119  case TYPE_DOUBLE:
120  rb_ary_push(params, rb_float_new(*(double *)x->args[i]));
121  break;
122  case TYPE_FLOAT:
123  rb_ary_push(params, rb_float_new(*(float *)x->args[i]));
124  break;
125 #if HAVE_LONG_LONG
126  case TYPE_LONG_LONG:
127  rb_ary_push(params, LL2NUM(*(LONG_LONG *)x->args[i]));
128  break;
129  case -TYPE_LONG_LONG:
130  rb_ary_push(params, ULL2NUM(*(unsigned LONG_LONG *)x->args[i]));
131  break;
132 #endif
133  case TYPE_CONST_STRING:
134  rb_ary_push(params,
135  rb_str_new_cstr(*((const char **)(x->args[i]))));
136  break;
137  default:
138  rb_raise(rb_eRuntimeError, "closure args: %d", type);
139  }
140  }
141 
142  ret = rb_funcall2(self, rb_intern("call"), argc, RARRAY_CONST_PTR(params));
143  RB_GC_GUARD(params);
144 
145  type = NUM2INT(ctype);
146  switch (type) {
147  case TYPE_VOID:
148  break;
149  case TYPE_LONG:
150  *(long *)x->resp = NUM2LONG(ret);
151  break;
152  case -TYPE_LONG:
153  *(unsigned long *)x->resp = NUM2ULONG(ret);
154  break;
155  case TYPE_CHAR:
156  case TYPE_SHORT:
157  case TYPE_INT:
158  *(ffi_sarg *)x->resp = NUM2INT(ret);
159  break;
160  case -TYPE_CHAR:
161  case -TYPE_SHORT:
162  case -TYPE_INT:
163  *(ffi_arg *)x->resp = NUM2UINT(ret);
164  break;
165  case TYPE_VOIDP:
166  *(void **)x->resp = NUM2PTR(ret);
167  break;
168  case TYPE_DOUBLE:
169  *(double *)x->resp = NUM2DBL(ret);
170  break;
171  case TYPE_FLOAT:
172  *(float *)x->resp = (float)NUM2DBL(ret);
173  break;
174 #if HAVE_LONG_LONG
175  case TYPE_LONG_LONG:
176  *(LONG_LONG *)x->resp = NUM2LL(ret);
177  break;
178  case -TYPE_LONG_LONG:
179  *(unsigned LONG_LONG *)x->resp = NUM2ULL(ret);
180  break;
181 #endif
182  case TYPE_CONST_STRING:
183  /* Dangerous. Callback must keep reference of the String. */
184  *((const char **)(x->resp)) = StringValueCStr(ret);
185  break;
186  default:
187  rb_raise(rb_eRuntimeError, "closure retval: %d", type);
188  }
189  return 0;
190 }
191 
192 static void
193 callback(ffi_cif *cif, void *resp, void **args, void *ctx)
194 {
195  struct callback_args x;
196 
197  x.cif = cif;
198  x.resp = resp;
199  x.args = args;
200  x.ctx = ctx;
201 
202  if (ruby_thread_has_gvl_p()) {
203  (void)with_gvl_callback(&x);
204  } else {
205  (void)rb_thread_call_with_gvl(with_gvl_callback, &x);
206  }
207 }
208 
209 static VALUE
210 allocate(VALUE klass)
211 {
212  fiddle_closure * closure;
213 
215  &closure_data_type, closure);
216 
217 #if USE_FFI_CLOSURE_ALLOC
218  closure->pcl = ffi_closure_alloc(sizeof(ffi_closure), &closure->code);
219 #else
220  closure->pcl = mmap(NULL, sizeof(ffi_closure), PROT_READ | PROT_WRITE,
221  MAP_ANON | MAP_PRIVATE, -1, 0);
222 #endif
223 
224  return i;
225 }
226 
227 static VALUE
228 initialize(int rbargc, VALUE argv[], VALUE self)
229 {
230  VALUE ret;
231  VALUE args;
232  VALUE normalized_args;
233  VALUE abi;
234  fiddle_closure * cl;
235  ffi_cif * cif;
236  ffi_closure *pcl;
237  ffi_status result;
238  int i, argc;
239 
240  if (2 == rb_scan_args(rbargc, argv, "21", &ret, &args, &abi))
241  abi = INT2NUM(FFI_DEFAULT_ABI);
242 
243  Check_Type(args, T_ARRAY);
244 
245  argc = RARRAY_LENINT(args);
246 
248 
249  cl->argv = (ffi_type **)xcalloc(argc + 1, sizeof(ffi_type *));
250 
251  normalized_args = rb_ary_new_capa(argc);
252  for (i = 0; i < argc; i++) {
254  rb_ary_push(normalized_args, arg);
255  cl->argv[i] = rb_fiddle_int_to_ffi_type(NUM2INT(arg));
256  }
257  cl->argv[argc] = NULL;
258 
260  rb_iv_set(self, "@ctype", ret);
261  rb_iv_set(self, "@args", normalized_args);
262 
263  cif = &cl->cif;
264  pcl = cl->pcl;
265 
266  result = ffi_prep_cif(cif,
267  NUM2INT(abi),
268  argc,
270  cl->argv);
271 
272  if (FFI_OK != result)
273  rb_raise(rb_eRuntimeError, "error prepping CIF %d", result);
274 
275 #if USE_FFI_CLOSURE_ALLOC
276  result = ffi_prep_closure_loc(pcl, cif, callback,
277  (void *)self, cl->code);
278 #else
279  result = ffi_prep_closure(pcl, cif, callback, (void *)self);
280  cl->code = (void *)pcl;
281  i = mprotect(pcl, sizeof(*pcl), PROT_READ | PROT_EXEC);
282  if (i) {
283  rb_sys_fail("mprotect");
284  }
285 #endif
286 
287  if (FFI_OK != result)
288  rb_raise(rb_eRuntimeError, "error prepping closure %d", result);
289 
290  return self;
291 }
292 
293 static VALUE
294 to_i(VALUE self)
295 {
296  fiddle_closure * cl;
297  void *code;
298 
300 
301  code = cl->code;
302 
303  return PTR2NUM(code);
304 }
305 
306 void
308 {
309 #if 0
310  mFiddle = rb_define_module("Fiddle"); /* let rdoc know about mFiddle */
311 #endif
312 
313  /*
314  * Document-class: Fiddle::Closure
315  *
316  * == Description
317  *
318  * An FFI closure wrapper, for handling callbacks.
319  *
320  * == Example
321  *
322  * closure = Class.new(Fiddle::Closure) {
323  * def call
324  * 10
325  * end
326  * }.new(Fiddle::TYPE_INT, [])
327  * #=> #<#<Class:0x0000000150d308>:0x0000000150d240>
328  * func = Fiddle::Function.new(closure, [], Fiddle::TYPE_INT)
329  * #=> #<Fiddle::Function:0x00000001516e58>
330  * func.call
331  * #=> 10
332  */
334 
336 
337  /*
338  * Document-method: new
339  *
340  * call-seq: new(ret, args, abi = Fiddle::DEFAULT)
341  *
342  * Construct a new Closure object.
343  *
344  * * +ret+ is the C type to be returned
345  * * +args+ is an Array of arguments, passed to the callback function
346  * * +abi+ is the abi of the closure
347  *
348  * If there is an error in preparing the ffi_cif or ffi_prep_closure,
349  * then a RuntimeError will be raised.
350  */
351  rb_define_method(cFiddleClosure, "initialize", initialize, -1);
352 
353  /*
354  * Document-method: to_i
355  *
356  * Returns the memory address for this closure
357  */
358  rb_define_method(cFiddleClosure, "to_i", to_i, 0);
359 }
360 /* vim: set noet sw=4 sts=4 */
rb_const_get
VALUE rb_const_get(VALUE, ID)
Definition: variable.c:2645
rb_ary_new_capa
VALUE rb_ary_new_capa(long capa)
Definition: array.c:748
TYPE_VOIDP
#define TYPE_VOIDP
Definition: fiddle.h:112
TYPE_DOUBLE
#define TYPE_DOUBLE
Definition: fiddle.h:121
callback_args::ctx
void * ctx
Definition: closure.c:65
Init_fiddle_closure
void Init_fiddle_closure(void)
Definition: closure.c:307
ULL2NUM
#define ULL2NUM
Definition: long_long.h:31
TYPE_LONG
#define TYPE_LONG
Definition: fiddle.h:116
callback_args::resp
void * resp
Definition: closure.c:63
mFiddle
VALUE mFiddle
Definition: fiddle.c:3
fiddle_closure::cif
ffi_cif cif
Definition: closure.c:11
callback_args::cif
ffi_cif * cif
Definition: closure.c:62
rb_intern
ID rb_intern(const char *)
Definition: symbol.c:784
fiddle.h
xcalloc
#define xcalloc
Definition: xmalloc.h:46
fiddle_closure::argv
ffi_type ** argv
Definition: closure.c:13
xfree
#define xfree
Definition: xmalloc.h:49
NUM2DBL
#define NUM2DBL
Definition: double.h:27
rb_define_module
VALUE rb_define_module(const char *name)
Definition: class.c:887
LONG2NUM
#define LONG2NUM
Definition: long.h:50
TYPE_SHORT
#define TYPE_SHORT
Definition: fiddle.h:114
LONG_LONG
#define LONG_LONG
Definition: long_long.h:34
argv
char ** argv
Definition: ruby.c:243
NUM2ULL
#define NUM2ULL
Definition: long_long.h:35
TYPE_INT
#define TYPE_INT
Definition: fiddle.h:115
callback_args
Definition: closure.c:61
ptr
struct RIMemo * ptr
Definition: debug.c:87
NUM2INT
#define NUM2INT
Definition: int.h:44
thread.h
TypedData_Make_Struct
#define TypedData_Make_Struct(klass, type, data_type, sval)
Definition: rtypeddata.h:122
NUM2UINT
#define NUM2UINT
Definition: int.h:45
rb_iv_get
VALUE rb_iv_get(VALUE, const char *)
Definition: variable.c:3659
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:3022
rb_iv_set
VALUE rb_iv_set(VALUE, const char *, VALUE)
Definition: variable.c:3670
closure_data_type
const rb_data_type_t closure_data_type
Definition: closure.c:56
INT2NUM
#define INT2NUM
Definition: int.h:43
rb_define_alloc_func
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
rb_cObject
VALUE rb_cObject
Object class.
Definition: object.c:50
ULONG2NUM
#define ULONG2NUM
Definition: long.h:60
rb_ary_tmp_new
VALUE rb_ary_tmp_new(long capa)
Definition: array.c:851
TYPE_CHAR
#define TYPE_CHAR
Definition: fiddle.h:113
LL2NUM
#define LL2NUM
Definition: long_long.h:30
rb_ary_push
VALUE rb_ary_push(VALUE ary, VALUE item)
Definition: array.c:1312
rb_sys_fail
void rb_sys_fail(const char *mesg)
Definition: error.c:3146
fiddle_closure::code
void * code
Definition: closure.c:9
NUM2PTR
#define NUM2PTR(x)
Definition: conversions.h:46
rb_eRuntimeError
VALUE rb_eRuntimeError
Definition: error.c:1091
rb_fiddle_int_to_ffi_type
ffi_type * rb_fiddle_int_to_ffi_type(int type)
Definition: conversions.c:156
NULL
#define NULL
Definition: regenc.h:69
rb_scan_args
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:2347
TYPE_VOID
#define TYPE_VOID
Definition: fiddle.h:111
cFiddleClosure
VALUE cFiddleClosure
Definition: closure.c:6
VALUE
unsigned long VALUE
Definition: value.h:38
rb_funcall
VALUE rb_funcall(VALUE, ID, int,...)
Calls a method.
Definition: vm_eval.c:1113
ruby_thread_has_gvl_p
int ruby_thread_has_gvl_p(void)
Definition: thread.c:1938
RARRAY_CONST_PTR
#define RARRAY_CONST_PTR(a)
Definition: missing.h:197
rb_thread_call_with_gvl
void * rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
Definition: thread.c:1895
NUM2LONG
#define NUM2LONG
Definition: long.h:51
rb_funcall2
#define rb_funcall2
Definition: eval.h:36
ret
return ret
Definition: memory.h:232
rb_float_new
#define rb_float_new
Definition: numeric.h:96
argc
int argc
Definition: ruby.c:242
PTR2NUM
#define PTR2NUM(x)
Definition: conversions.h:45
T_ARRAY
#define T_ARRAY
Definition: value_type.h:56
TypedData_Get_Struct
#define TypedData_Get_Struct(obj, type, data_type, sval)
Definition: rtypeddata.h:130
rb_data_type_struct
Definition: rtypeddata.h:70
UINT2NUM
#define UINT2NUM
Definition: int.h:46
RARRAY_AREF
#define RARRAY_AREF(a, i)
Definition: missing.h:201
TYPE_FLOAT
#define TYPE_FLOAT
Definition: fiddle.h:120
rb_str_new_cstr
#define rb_str_new_cstr(str)
Definition: string.h:219
fiddle_closure::pcl
ffi_closure * pcl
Definition: closure.c:10
fiddle_closure
Definition: closure.c:8
NUM2ULONG
#define NUM2ULONG
Definition: long.h:52
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:809
callback_args::args
void ** args
Definition: closure.c:64
rb_fiddle_type_ensure
VALUE rb_fiddle_type_ensure(VALUE type)
Definition: conversions.c:4
StringValueCStr
#define StringValueCStr(v)
Definition: rstring.h:52
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:655
size
rb_atomic_t size
Definition: signal.c:509
NUM2LL
#define NUM2LL
Definition: long_long.h:34
TYPE_CONST_STRING
#define TYPE_CONST_STRING
Definition: fiddle.h:123
fiddle_closure::argc
int argc
Definition: closure.c:12
ruby::backward::cxxanyargs::type
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:56
RB_GC_GUARD
#define RB_GC_GUARD(v)
Definition: memory.h:91