Ruby: ext/fiddle/closure.c Source File

Ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c52d4d35cc6a173c89eda98ceffa2dcf)

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
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(__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
27static void
28dealloc(void * ptr)
29{
30 fiddle_closure * cls = (fiddle_closure *)ptr;
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
40static size_t
41closure_memsize(const void * ptr)
42{
43 fiddle_closure * cls = (fiddle_closure *)ptr;
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
56 const rb_data_type_t closure_data_type = {
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
68static void *
69with_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
192static void
193callback(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
209static VALUE
210allocate(VALUE klass)
211{
212 fiddle_closure * closure;
213
214 VALUE i = TypedData_Make_Struct(klass, fiddle_closure,
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
227static VALUE
228initialize(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
247 TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
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++) {
253 VALUE arg = rb_fiddle_type_ensure(RARRAY_AREF(args, 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
259 ret = rb_fiddle_type_ensure(ret);
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,
269 rb_fiddle_int_to_ffi_type(NUM2INT(ret)),
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
293static VALUE
294to_i(VALUE self)
295{
296 fiddle_closure * cl;
297 void *code;
298
299 TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
300
301 code = cl->code;
302
303 return PTR2NUM(code);
304}
305
306void
307Init_fiddle_closure(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 */
333 cFiddleClosure = rb_define_class_under(mFiddle, "Closure", rb_cObject);
334
335 rb_define_alloc_func(cFiddleClosure, allocate);
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 */
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
ffi_arg
unsigned long ffi_arg
Definition: ffitarget.h:30
FFI_DEFAULT_ABI
@ FFI_DEFAULT_ABI
Definition: ffitarget.h:38
ffi_sarg
signed long ffi_sarg
Definition: ffitarget.h:31
rb_ary_push
VALUE rb_ary_push(VALUE ary, VALUE item)
Definition: array.c:1301
rb_ary_new_capa
VALUE rb_ary_new_capa(long capa)
Definition: array.c:743
rb_ary_tmp_new
VALUE rb_ary_tmp_new(long capa)
Definition: array.c:846
ruby_thread_has_gvl_p
int ruby_thread_has_gvl_p(void)
Definition: thread.c:1935
closure_data_type
const rb_data_type_t closure_data_type
Definition: closure.c:56
Init_fiddle_closure
void Init_fiddle_closure(void)
Definition: closure.c:307
cFiddleClosure
VALUE cFiddleClosure
Definition: closure.c:6
rb_fiddle_int_to_ffi_type
ffi_type * rb_fiddle_int_to_ffi_type(int type)
Definition: conversions.c:156
rb_fiddle_type_ensure
VALUE rb_fiddle_type_ensure(VALUE type)
Definition: conversions.c:4
NUM2PTR
#define NUM2PTR(x)
Definition: conversions.h:46
PTR2NUM
#define PTR2NUM(x)
Definition: conversions.h:45
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:653
ptr
struct RIMemo * ptr
Definition: debug.c:88
NUM2DBL
#define NUM2DBL
Definition: double.h:27
mFiddle
VALUE mFiddle
Definition: fiddle.c:3
fiddle.h
TYPE_CONST_STRING
#define TYPE_CONST_STRING
Definition: fiddle.h:119
TYPE_FLOAT
#define TYPE_FLOAT
Definition: fiddle.h:116
TYPE_CHAR
#define TYPE_CHAR
Definition: fiddle.h:109
TYPE_VOID
#define TYPE_VOID
Definition: fiddle.h:107
TYPE_INT
#define TYPE_INT
Definition: fiddle.h:111
TYPE_LONG
#define TYPE_LONG
Definition: fiddle.h:112
TYPE_VOIDP
#define TYPE_VOIDP
Definition: fiddle.h:108
TYPE_DOUBLE
#define TYPE_DOUBLE
Definition: fiddle.h:117
TYPE_SHORT
#define TYPE_SHORT
Definition: fiddle.h:110
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:797
rb_define_module
VALUE rb_define_module(const char *name)
Definition: class.c:871
rb_scan_args
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Definition: class.c:2296
rb_raise
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2917
rb_eRuntimeError
VALUE rb_eRuntimeError
Definition: error.c:1055
rb_sys_fail
void rb_sys_fail(const char *mesg)
Definition: error.c:3041
rb_cObject
VALUE rb_cObject
Object class.
Definition: object.c:49
rb_funcall2
#define rb_funcall2
Definition: eval.h:36
rb_funcall
VALUE rb_funcall(VALUE, ID, int,...)
Calls a method.
Definition: vm_eval.c:1077
rb_str_new_cstr
#define rb_str_new_cstr(str)
Definition: string.h:219
rb_const_get
VALUE rb_const_get(VALUE, ID)
Definition: variable.c:2624
rb_define_alloc_func
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
rb_intern
ID rb_intern(const char *)
Definition: symbol.c:785
rb_iv_set
VALUE rb_iv_set(VALUE, const char *, VALUE)
Definition: variable.c:3580
rb_iv_get
VALUE rb_iv_get(VALUE, const char *)
Definition: variable.c:3569
thread.h
rb_thread_call_with_gvl
void * rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
Definition: thread.c:1892
NUM2UINT
#define NUM2UINT
Definition: int.h:45
NUM2INT
#define NUM2INT
Definition: int.h:44
INT2NUM
#define INT2NUM
Definition: int.h:43
UINT2NUM
#define UINT2NUM
Definition: int.h:46
NUM2LL
#define NUM2LL
Definition: long_long.h:34
LL2NUM
#define LL2NUM
Definition: long_long.h:30
ULL2NUM
#define ULL2NUM
Definition: long_long.h:31
NUM2ULL
#define NUM2ULL
Definition: long_long.h:35
rb_float_new
#define rb_float_new
Definition: numeric.h:95
size
voidpf void uLong size
Definition: ioapi.h:138
NUM2ULONG
#define NUM2ULONG
Definition: long.h:52
ULONG2NUM
#define ULONG2NUM
Definition: long.h:60
LONG2NUM
#define LONG2NUM
Definition: long.h:50
NUM2LONG
#define NUM2LONG
Definition: long.h:51
RB_GC_GUARD
#define RB_GC_GUARD(v)
Definition: memory.h:91
ruby::backward::cxxanyargs::type
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:56
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
RARRAY_CONST_PTR
#define RARRAY_CONST_PTR(s)
Definition: psych_emitter.c:4
RARRAY_AREF
#define RARRAY_AREF(a, i)
Definition: psych_emitter.c:7
ffi_raw_size
size_t ffi_raw_size(ffi_cif *cif)
Definition: raw_api.c:35
NULL
#define NULL
Definition: regenc.h:69
StringValueCStr
#define StringValueCStr(v)
Definition: rstring.h:52
TypedData_Get_Struct
#define TypedData_Get_Struct(obj, type, data_type, sval)
Definition: rtypeddata.h:130
TypedData_Make_Struct
#define TypedData_Make_Struct(klass, type, data_type, sval)
Definition: rtypeddata.h:122
argc
int argc
Definition: ruby.c:240
argv
char ** argv
Definition: ruby.c:241
callback_args
Definition: closure.c:61
callback_args::resp
void * resp
Definition: closure.c:63
callback_args::ctx
void * ctx
Definition: closure.c:65
callback_args::args
void ** args
Definition: closure.c:64
callback_args::cif
ffi_cif * cif
Definition: closure.c:62
code
Definition: inftree9.h:24
fiddle_closure
Definition: closure.c:8
fiddle_closure::argv
ffi_type ** argv
Definition: closure.c:13
fiddle_closure::code
void * code
Definition: closure.c:9
fiddle_closure::argc
int argc
Definition: closure.c:12
fiddle_closure::cif
ffi_cif cif
Definition: closure.c:11
fiddle_closure::pcl
ffi_closure * pcl
Definition: closure.c:10
rb_data_type_struct
Definition: rtypeddata.h:70
VALUE
unsigned long VALUE
Definition: value.h:38
T_ARRAY
#define T_ARRAY
Definition: value_type.h:55
xfree
#define xfree
Definition: xmalloc.h:49
xcalloc
#define xcalloc
Definition: xmalloc.h:46