Ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5ad0e4688e963d9de019557c78feed9)
closure.c
Go to the documentation of this file.
1#include <fiddle.h>
2#include <ruby/thread.h>
3
4int ruby_thread_has_gvl_p(void); /* from internal.h */
5
7
8typedef 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
23static void
24dealloc(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
36static size_t
37closure_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
58 ffi_cif *cif;
59 void *resp;
60 void **args;
61 void *ctx;
62};
63
64static void *
65with_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
180static void
181callback(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
197static VALUE
198allocate(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
215static VALUE
216initialize(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))
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
275static VALUE
276to_i(VALUE self)
277{
278 fiddle_closure * cl;
279 void *code;
280
282
283 code = cl->code;
284
285 return PTR2NUM(code);
286}
287
288void
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 */
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
unsigned long ffi_arg
Definition: ffitarget.h:30
@ FFI_DEFAULT_ABI
Definition: ffitarget.h:38
signed long ffi_sarg
Definition: ffitarget.h:31
int ruby_thread_has_gvl_p(void)
Definition: thread.c:1704
const rb_data_type_t closure_data_type
Definition: closure.c:52
void Init_fiddle_closure(void)
Definition: closure.c:289
VALUE cFiddleClosure
Definition: closure.c:6
#define NUM2PTR(x)
Definition: conversions.h:37
#define INT2FFI_TYPE(_type)
Definition: conversions.h:32
#define PTR2NUM(x)
Definition: conversions.h:36
struct RIMemo * ptr
Definition: debug.c:65
VALUE mFiddle
Definition: fiddle.c:3
#define TYPE_FLOAT
Definition: fiddle.h:116
#define TYPE_CHAR
Definition: fiddle.h:109
#define TYPE_VOID
Definition: fiddle.h:107
#define TYPE_INT
Definition: fiddle.h:111
#define TYPE_LONG
Definition: fiddle.h:112
#define TYPE_VOIDP
Definition: fiddle.h:108
#define TYPE_DOUBLE
Definition: fiddle.h:117
#define TYPE_SHORT
Definition: fiddle.h:110
VALUE rb_define_class_under(VALUE, const char *, VALUE)
Defines a class under the namespace of outer.
Definition: class.c:711
VALUE rb_define_module(const char *)
Definition: class.c:785
VALUE rb_cObject
Object class.
Definition: ruby.h:2012
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2671
VALUE rb_eRuntimeError
Definition: error.c:922
void rb_sys_fail(const char *mesg)
Definition: error.c:2795
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:39
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
size_t ffi_raw_size(ffi_cif *cif)
Definition: raw_api.c:35
#define NULL
#define NUM2DBL(x)
#define NUM2ULL(x)
#define ULL2NUM(v)
VALUE rb_const_get(VALUE, ID)
Definition: variable.c:2391
#define NUM2ULONG(x)
#define RARRAY_LENINT(ary)
#define xfree
#define UINT2NUM(x)
#define rb_funcall2
const char const char *typedef unsigned long VALUE
VALUE rb_ary_push(VALUE, VALUE)
Definition: array.c:1195
() void(cc->call !=vm_call_general)
#define rb_float_new(d)
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
uint32_t i
#define NUM2UINT(x)
#define INT2NUM(x)
VALUE rb_iv_set(VALUE, const char *, VALUE)
Definition: variable.c:3318
#define LONG2NUM(x)
#define NUM2INT(x)
#define RB_GC_GUARD(v)
#define TypedData_Get_Struct(obj, type, data_type, sval)
VALUE rb_ary_tmp_new(long)
Definition: array.c:768
#define rb_funcall(recv, mid, argc,...)
#define rb_scan_args(argc, argvp, fmt,...)
#define rb_intern(str)
unsigned int size
#define T_ARRAY
#define ULONG2NUM(x)
#define NUM2LL(x)
#define TypedData_Make_Struct(klass, type, data_type, sval)
const VALUE * argv
#define LL2NUM(v)
VALUE rb_iv_get(VALUE, const char *)
Definition: variable.c:3305
#define Check_Type(v, t)
#define xcalloc
#define NUM2LONG(x)
void rb_define_method(VALUE, const char *, VALUE(*)(), int)
#define RARRAY_AREF(a, i)
#define RARRAY_CONST_PTR(a)
#define LONG_LONG
unsigned long VALUE
Definition: ruby.h:102
void * resp
Definition: closure.c:59
void * ctx
Definition: closure.c:61
void ** args
Definition: closure.c:60
ffi_cif * cif
Definition: closure.c:58
ffi_type ** argv
Definition: closure.c:13
void * code
Definition: closure.c:9
ffi_cif cif
Definition: closure.c:11
ffi_closure * pcl
Definition: closure.c:10
RUBY_SYMBOL_EXPORT_BEGIN void * rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
Definition: thread.c:1661