Skip to content

Commit 29efadc

Browse files
committed
Move ivars into struct
1 parent b44bec0 commit 29efadc

1 file changed

Lines changed: 105 additions & 86 deletions

File tree

‎ext/fiddle/function.c‎

Lines changed: 105 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -37,23 +37,38 @@ static ID id_win32_last_error, id_win32_last_socket_error;
3737
} \
3838
} while (0)
3939

40+
/* Call-path state, converted and validated once in #initialize.
41+
* Deliberately holds no VALUEs so the type needs no dmark/dcompact. */
42+
typedef struct fiddle_function {
43+
ffi_cif cif;
44+
void (*fn)(void);
45+
int *argument_types;
46+
int n_argument_types;
47+
int return_type;
48+
ffi_abi abi;
49+
bool is_variadic;
50+
bool need_gvl;
51+
} fiddle_function_t;
52+
4053
static void
4154
deallocate(void *p)
4255
{
43-
ffi_cif *cif = p;
44-
if (cif->arg_types) xfree(cif->arg_types);
45-
xfree(cif);
56+
fiddle_function_t *func = p;
57+
if (func->cif.arg_types) xfree(func->cif.arg_types);
58+
if (func->argument_types) xfree(func->argument_types);
59+
xfree(func);
4660
}
4761

4862
static size_t
4963
function_memsize(const void *p)
5064
{
51-
/* const */ffi_cif *ptr = (ffi_cif *)p;
65+
const fiddle_function_t *func = p;
5266
size_t size = 0;
5367

54-
size += sizeof(*ptr);
68+
size += sizeof(*func);
69+
size += func->n_argument_types * sizeof(int);
5570
#if !defined(FFI_NO_RAW_API) || !FFI_NO_RAW_API
56-
size += ffi_raw_size(ptr);
71+
size += ffi_raw_size((ffi_cif *)&func->cif);
5772
#endif
5873

5974
return size;
@@ -72,9 +87,9 @@ const rb_data_type_t function_data_type = {
7287
static VALUE
7388
allocate(VALUE klass)
7489
{
75-
ffi_cif * cif;
90+
fiddle_function_t *func;
7691

77-
return TypedData_Make_Struct(klass, ffi_cif, &function_data_type, cif);
92+
return TypedData_Make_Struct(klass, fiddle_function_t, &function_data_type, func);
7893
}
7994

8095
VALUE
@@ -133,14 +148,15 @@ normalize_argument_types(const char *name,
133148
static VALUE
134149
initialize(int argc, VALUE argv[], VALUE self)
135150
{
136-
ffi_cif * cif;
151+
fiddle_function_t *func;
137152
VALUE ptr, arg_types, ret_type, abi, kwargs;
138153
VALUE name = Qnil;
139154
VALUE need_gvl = Qfalse;
140155
int c_ret_type;
141156
bool is_variadic = false;
142157
ffi_abi c_ffi_abi;
143158
void *cfunc;
159+
int i;
144160

145161
rb_scan_args(argc, argv, "31:", &ptr, &arg_types, &ret_type, &abi, &kwargs);
146162
rb_ivar_set(self, id_closure, ptr);
@@ -200,8 +216,27 @@ initialize(int argc, VALUE argv[], VALUE self)
200216
rb_ivar_set(self, id_abi, abi);
201217
rb_ivar_set(self, id_is_variadic, is_variadic ? Qtrue : Qfalse);
202218

203-
TypedData_Get_Struct(self, ffi_cif, &function_data_type, cif);
204-
cif->arg_types = NULL;
219+
TypedData_Get_Struct(self, fiddle_function_t, &function_data_type, func);
220+
func->fn = (void (*)(void))(VALUE)cfunc;
221+
func->abi = c_ffi_abi;
222+
func->return_type = c_ret_type;
223+
func->is_variadic = is_variadic;
224+
func->need_gvl = RTEST(need_gvl);
225+
226+
/* allow re-initialization without leaking or double-freeing */
227+
if (func->argument_types) {
228+
xfree(func->argument_types);
229+
func->argument_types = NULL;
230+
}
231+
func->n_argument_types = RARRAY_LENINT(arg_types);
232+
func->argument_types = ALLOC_N(int, func->n_argument_types);
233+
for (i = 0; i < func->n_argument_types; i++) {
234+
func->argument_types[i] = FIX2INT(RARRAY_AREF(arg_types, i));
235+
}
236+
if (func->cif.arg_types) {
237+
xfree(func->cif.arg_types);
238+
func->cif.arg_types = NULL;
239+
}
205240

206241
return self;
207242
}
@@ -227,95 +262,93 @@ static VALUE
227262
function_call(int argc, VALUE argv[], VALUE self)
228263
{
229264
struct nogvl_ffi_call_args args = { 0 };
265+
fiddle_function_t *func;
230266
fiddle_generic *generic_args;
231-
VALUE cfunc;
232-
VALUE abi;
233-
VALUE arg_types;
234-
VALUE is_variadic;
235-
VALUE need_gvl;
236-
int n_arg_types;
237267
int n_fixed_args = 0;
238268
int n_call_args = 0;
239269
int i;
240270
int i_call;
271+
int *call_arg_types;
241272
VALUE *converted_args;
242273
int n_converted_args = 0;
243274
VALUE alloc_buffer = 0;
244275

245-
cfunc = rb_ivar_get(self, id_ptr);
246-
abi = rb_ivar_get(self, id_abi);
247-
arg_types = rb_ivar_get(self, id_argument_types);
248-
is_variadic = rb_ivar_get(self, id_is_variadic);
249-
need_gvl = rb_ivar_get(self, id_need_gvl);
276+
TypedData_Get_Struct(self, fiddle_function_t, &function_data_type, func);
277+
args.cif = &func->cif;
250278

251-
n_arg_types = RARRAY_LENINT(arg_types);
252-
n_fixed_args = n_arg_types;
253-
if (RTEST(is_variadic)) {
254-
if (argc < n_arg_types) {
255-
rb_error_arity(argc, n_arg_types, UNLIMITED_ARGUMENTS);
279+
n_fixed_args = func->n_argument_types;
280+
if (func->is_variadic) {
281+
if (argc < n_fixed_args) {
282+
rb_error_arity(argc, n_fixed_args, UNLIMITED_ARGUMENTS);
256283
}
257-
if (((argc - n_arg_types) % 2) != 0) {
284+
if (((argc - n_fixed_args) % 2) != 0) {
258285
rb_raise(rb_eArgError,
259286
"variadic arguments must be type and value pairs: "
260287
"%"PRIsVALUE,
261288
rb_ary_new_from_values(argc, argv));
262289
}
263-
n_call_args = n_arg_types + ((argc - n_arg_types) / 2);
290+
n_call_args = n_fixed_args + ((argc - n_fixed_args) / 2);
264291
}
265292
else {
266-
if (argc != n_arg_types) {
267-
rb_error_arity(argc, n_arg_types, n_arg_types);
293+
if (argc != n_fixed_args) {
294+
rb_error_arity(argc, n_fixed_args, n_fixed_args);
268295
}
269-
n_call_args = n_arg_types;
296+
n_call_args = n_fixed_args;
270297
}
271298
Check_Max_Args("the number of arguments", n_call_args);
272299

273-
TypedData_Get_Struct(self, ffi_cif, &function_data_type, args.cif);
300+
generic_args = ALLOCV(alloc_buffer,
301+
sizeof(fiddle_generic) * n_call_args +
302+
sizeof(void *) * (n_call_args + 1) +
303+
sizeof(VALUE) * n_call_args +
304+
(func->is_variadic ? sizeof(int) * n_call_args : 0));
305+
args.values = (void **)((char *)generic_args +
306+
sizeof(fiddle_generic) * n_call_args);
307+
/* GC-scanned (conservatively) as part of the ALLOCV buffer */
308+
converted_args = (VALUE *)((char *)args.values +
309+
sizeof(void *) * (n_call_args + 1));
310+
MEMZERO(converted_args, VALUE, n_call_args);
274311

275-
if (is_variadic && args.cif->arg_types) {
276-
xfree(args.cif->arg_types);
277-
args.cif->arg_types = NULL;
312+
if (func->is_variadic) {
313+
call_arg_types = (int *)(converted_args + n_call_args);
314+
MEMCPY(call_arg_types, func->argument_types, int, n_fixed_args);
315+
for (i = n_fixed_args, i_call = n_fixed_args;
316+
i < argc;
317+
i += 2, i_call++) {
318+
VALUE arg_type = rb_fiddle_type_ensure(argv[i]);
319+
int c_arg_type = NUM2INT(arg_type);
320+
(void)INT2FFI_TYPE(c_arg_type); /* raise */
321+
call_arg_types[i_call] = c_arg_type;
322+
}
323+
324+
/* Variadic calls may pass different types each call, so the
325+
* cif must always be re-prepared */
326+
if (func->cif.arg_types) {
327+
xfree(func->cif.arg_types);
328+
func->cif.arg_types = NULL;
329+
}
330+
}
331+
else {
332+
call_arg_types = func->argument_types;
278333
}
279334

280-
if (!args.cif->arg_types) {
281-
VALUE fixed_arg_types = arg_types;
282-
VALUE return_type;
283-
int c_return_type;
284-
ffi_type *ffi_return_type;
335+
if (!func->cif.arg_types) {
285336
ffi_type **ffi_arg_types;
286337
ffi_status result;
287338

288-
arg_types = rb_ary_dup(fixed_arg_types);
289-
for (i = n_fixed_args; i < argc; i += 2) {
290-
VALUE arg_type = argv[i];
291-
int c_arg_type;
292-
arg_type = rb_fiddle_type_ensure(arg_type);
293-
c_arg_type = NUM2INT(arg_type);
294-
(void)INT2FFI_TYPE(c_arg_type); /* raise */
295-
rb_ary_push(arg_types, INT2FIX(c_arg_type));
296-
}
297-
298-
return_type = rb_ivar_get(self, id_return_type);
299-
c_return_type = FIX2INT(return_type);
300-
ffi_return_type = INT2FFI_TYPE(c_return_type);
301-
302339
ffi_arg_types = xcalloc(n_call_args + 1, sizeof(ffi_type *));
303340
for (i_call = 0; i_call < n_call_args; i_call++) {
304-
VALUE arg_type;
305-
int c_arg_type;
306-
arg_type = RARRAY_AREF(arg_types, i_call);
307-
c_arg_type = FIX2INT(arg_type);
308-
ffi_arg_types[i_call] = INT2FFI_TYPE(c_arg_type);
341+
ffi_arg_types[i_call] = INT2FFI_TYPE(call_arg_types[i_call]);
309342
}
310343
ffi_arg_types[i_call] = NULL;
311344

312-
if (is_variadic) {
345+
if (func->is_variadic) {
313346
#ifdef HAVE_FFI_PREP_CIF_VAR
314-
result = ffi_prep_cif_var(args.cif,
315-
FIX2INT(abi),
347+
result = ffi_prep_cif_var(&func->cif,
348+
func->abi,
316349
n_fixed_args,
317350
n_call_args,
318-
ffi_return_type,
351+
INT2FFI_TYPE(func->return_type),
319352
ffi_arg_types);
320353
#else
321354
/* This code is never used because ffi_prep_cif_var()
@@ -324,39 +357,25 @@ function_call(int argc, VALUE argv[], VALUE self)
324357
#endif
325358
}
326359
else {
327-
result = ffi_prep_cif(args.cif,
328-
FIX2INT(abi),
360+
result = ffi_prep_cif(&func->cif,
361+
func->abi,
329362
n_call_args,
330-
ffi_return_type,
363+
INT2FFI_TYPE(func->return_type),
331364
ffi_arg_types);
332365
}
333366
if (result != FFI_OK) {
334367
xfree(ffi_arg_types);
335-
args.cif->arg_types = NULL;
368+
func->cif.arg_types = NULL;
336369
rb_raise(rb_eRuntimeError, "error creating CIF %d", result);
337370
}
338371
}
339372

340-
generic_args = ALLOCV(alloc_buffer,
341-
sizeof(fiddle_generic) * n_call_args +
342-
sizeof(void *) * (n_call_args + 1) +
343-
sizeof(VALUE) * n_call_args);
344-
args.values = (void **)((char *)generic_args +
345-
sizeof(fiddle_generic) * n_call_args);
346-
/* GC-scanned (conservatively) as part of the ALLOCV buffer */
347-
converted_args = (VALUE *)((char *)args.values +
348-
sizeof(void *) * (n_call_args + 1));
349-
MEMZERO(converted_args, VALUE, n_call_args);
350-
351373
for (i = 0, i_call = 0;
352374
i < argc && i_call < n_call_args;
353375
i++, i_call++) {
354-
VALUE arg_type;
355-
int c_arg_type;
376+
int c_arg_type = call_arg_types[i_call];
356377
VALUE original_src;
357378
VALUE src;
358-
arg_type = RARRAY_AREF(arg_types, i_call);
359-
c_arg_type = FIX2INT(arg_type);
360379
if (i >= n_fixed_args) {
361380
i++;
362381
}
@@ -389,9 +408,9 @@ function_call(int argc, VALUE argv[], VALUE self)
389408
args.values[i_call] = (void *)&generic_args[i_call];
390409
}
391410
args.values[i_call] = NULL;
392-
args.fn = (void(*)(void))(VALUE)NUM2PTR(cfunc);
411+
args.fn = func->fn;
393412

394-
if (RTEST(need_gvl)) {
413+
if (func->need_gvl) {
395414
ffi_call(args.cif, args.fn, &(args.retval), args.values);
396415
}
397416
else {
@@ -414,7 +433,7 @@ function_call(int argc, VALUE argv[], VALUE self)
414433

415434
ALLOCV_END(alloc_buffer);
416435

417-
return GENERIC2VALUE(rb_ivar_get(self, id_return_type), args.retval);
436+
return GENERIC2VALUE(INT2FIX(func->return_type), args.retval);
418437
}
419438

420439
void

0 commit comments

Comments
 (0)