Ruby 2.7.0p0(2019-12-25revision647ee6f091eafcce70ffb75ddf7e121e192ab217)

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
6 VALUE cFiddleClosure;
7
8 typedef struct {
9 void * code;
10 ffi_closure *pcl;
11 ffi_cif cif;
12 int argc;
13 ffi_type **argv;
14 } fiddle_closure;
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
55 const rb_data_type_t closure_data_type = {
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
205 VALUE i = TypedData_Make_Struct(klass, fiddle_closure,
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
292 Init_fiddle_closure(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 */
318 cFiddleClosure = rb_define_class_under(mFiddle, "Closure", rb_cObject);
319
320 rb_define_alloc_func(cFiddleClosure, allocate);
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 */
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:64
void
void
Definition: rb_mjit_min_header-2.7.0.h:13273
Init_fiddle_closure
void Init_fiddle_closure(void)
Definition: closure.c:292
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
NUM2ULL
#define NUM2ULL(x)
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:62
LL2NUM
#define LL2NUM(v)
Definition: rb_mjit_min_header-2.7.0.h:4240
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:61
rb_scan_args
#define rb_scan_args(argc, argvp, fmt,...)
Definition: rb_mjit_min_header-2.7.0.h:6372
NUM2LONG
#define NUM2LONG(x)
Definition: ruby.h:679
LONG_LONG
#define LONG_LONG
Definition: rb_mjit_min_header-2.7.0.h:3939
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
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:60
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:74
NUM2LL
#define NUM2LL(x)
NULL
#define NULL
Definition: _sdbm.c:101
ULL2NUM
#define ULL2NUM(v)
Definition: rb_mjit_min_header-2.7.0.h:4242
RARRAY_LENINT
#define RARRAY_LENINT(ary)
Definition: ruby.h:1071
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2669
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:55
rb_iv_get
VALUE rb_iv_get(VALUE, const char *)
Definition: variable.c:3294
rb_ary_tmp_new
VALUE rb_ary_tmp_new(long capa)
Definition: array.c:768
i
uint32_t i
Definition: rb_mjit_min_header-2.7.0.h:5464
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
rb_sys_fail
void rb_sys_fail(const char *mesg)
Definition: error.c:2793
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:920
RARRAY_AREF
#define RARRAY_AREF(a, i)
Definition: ruby.h:1101
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
RARRAY_CONST_PTR
#define RARRAY_CONST_PTR(a)
Definition: ruby.h:1072
cFiddleClosure
VALUE cFiddleClosure
Definition: closure.c:6
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
rb_funcall
#define rb_funcall(recv, mid, argc,...)
Definition: rb_mjit_min_header-2.7.0.h:6585
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
klass
VALUE klass
Definition: rb_mjit_min_header-2.7.0.h:13254
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
callback_args::args
void ** args
Definition: closure.c:63
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:1960
RB_GC_GUARD
#define RB_GC_GUARD(v)
Definition: ruby.h:585
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:3307
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:2387