Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 981449d

Browse files
committed
Merge pull request #504 from andrewdotn/master
Support blocking: true for varargs calls
2 parents 5122498 + b81176f commit 981449d

File tree

5 files changed

+111
-25
lines changed

5 files changed

+111
-25
lines changed

ext/ffi_c/Call.c

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -338,40 +338,27 @@ rbffi_SetupCallParams(int argc, VALUE* argv, int paramCount, Type** paramTypes,
338338
}
339339
}
340340

341-
342-
typedef struct BlockingCall_ {
343-
rbffi_frame_t* frame;
344-
void* function;
345-
FunctionType* info;
346-
void **ffiValues;
347-
void* retval;
348-
void* params;
349-
#if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
350-
void* stkretval;
351-
#endif
352-
} BlockingCall;
353-
354341
static VALUE
355342
call_blocking_function(void* data)
356343
{
357-
BlockingCall* b = (BlockingCall *) data;
344+
rbffi_blocking_call_t* b = (rbffi_blocking_call_t *) data;
358345
b->frame->has_gvl = false;
359-
ffi_call(&b->info->ffi_cif, FFI_FN(b->function), b->retval, b->ffiValues);
346+
ffi_call(&b->cif, FFI_FN(b->function), b->retval, b->ffiValues);
360347
b->frame->has_gvl = true;
361348

362349
return Qnil;
363350
}
364351

365-
static VALUE
366-
do_blocking_call(void *data)
352+
VALUE
353+
rbffi_do_blocking_call(void *data)
367354
{
368355
rbffi_thread_blocking_region(call_blocking_function, data, (void *) -1, NULL);
369356

370357
return Qnil;
371358
}
372359

373-
static VALUE
374-
save_frame_exception(void *data, VALUE exc)
360+
VALUE
361+
rbffi_save_frame_exception(void *data, VALUE exc)
375362
{
376363
rbffi_frame_t* frame = (rbffi_frame_t *) data;
377364
frame->exc = exc;
@@ -390,7 +377,7 @@ rbffi_CallFunction(int argc, VALUE* argv, void* function, FunctionType* fnInfo)
390377
retval = alloca(MAX(fnInfo->ffi_cif.rtype->size, FFI_SIZEOF_ARG));
391378

392379
if (unlikely(fnInfo->blocking)) {
393-
BlockingCall* bc;
380+
rbffi_blocking_call_t* bc;
394381

395382
/*
396383
* due to the way thread switching works on older ruby variants, we
@@ -399,16 +386,16 @@ rbffi_CallFunction(int argc, VALUE* argv, void* function, FunctionType* fnInfo)
399386
#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
400387
ffiValues = ALLOCA_N(void *, fnInfo->parameterCount);
401388
params = ALLOCA_N(FFIStorage, fnInfo->parameterCount);
402-
bc = ALLOCA_N(BlockingCall, 1);
389+
bc = ALLOCA_N(rbffi_blocking_call_t, 1);
403390
bc->retval = retval;
404391
#else
405392
ffiValues = ALLOC_N(void *, fnInfo->parameterCount);
406393
params = ALLOC_N(FFIStorage, fnInfo->parameterCount);
407-
bc = ALLOC_N(BlockingCall, 1);
394+
bc = ALLOC_N(rbffi_blocking_call_t, 1);
408395
bc->retval = xmalloc(MAX(fnInfo->ffi_cif.rtype->size, FFI_SIZEOF_ARG));
409396
bc->stkretval = retval;
410397
#endif
411-
bc->info = fnInfo;
398+
bc->cif = fnInfo->ffi_cif;
412399
bc->function = function;
413400
bc->ffiValues = ffiValues;
414401
bc->params = params;
@@ -419,11 +406,11 @@ rbffi_CallFunction(int argc, VALUE* argv, void* function, FunctionType* fnInfo)
419406
fnInfo->callbackParameters, fnInfo->callbackCount, fnInfo->rbEnums);
420407

421408
rbffi_frame_push(&frame);
422-
rb_rescue2(do_blocking_call, (VALUE) bc, save_frame_exception, (VALUE) &frame, rb_eException, (VALUE) 0);
409+
rb_rescue2(rbffi_do_blocking_call, (VALUE) bc, rbffi_save_frame_exception, (VALUE) &frame, rb_eException, (VALUE) 0);
423410
rbffi_frame_pop(&frame);
424411

425412
#if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
426-
memcpy(bc->stkretval, bc->retval, MAX(bc->info->ffi_cif.rtype->size, FFI_SIZEOF_ARG));
413+
memcpy(bc->stkretval, bc->retval, MAX(bc->cif.rtype->size, FFI_SIZEOF_ARG));
427414
xfree(bc->params);
428415
xfree(bc->ffiValues);
429416
xfree(bc->retval);

ext/ffi_c/Call.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
#ifndef RBFFI_CALL_H
3434
#define RBFFI_CALL_H
3535

36+
#include "Thread.h"
37+
3638
#ifdef __cplusplus
3739
extern "C" {
3840
#endif
@@ -85,6 +87,21 @@ Invoker rbffi_GetInvoker(struct FunctionType_* fnInfo);
8587
extern VALUE rbffi_GetEnumValue(VALUE enums, VALUE value);
8688
extern int rbffi_GetSignedIntValue(VALUE value, int type, int minValue, int maxValue, const char* typeName, VALUE enums);
8789

90+
typedef struct rbffi_blocking_call {
91+
rbffi_frame_t* frame;
92+
void* function;
93+
ffi_cif cif;
94+
void **ffiValues;
95+
void* retval;
96+
void* params;
97+
#if !(defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL))
98+
void* stkretval;
99+
#endif
100+
} rbffi_blocking_call_t;
101+
102+
VALUE rbffi_do_blocking_call(void* data);
103+
VALUE rbffi_save_frame_exception(void *data, VALUE exc);
104+
88105
#ifdef __cplusplus
89106
}
90107
#endif

ext/ffi_c/Variadic.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ typedef struct VariadicInvoker_ {
6464
ffi_abi abi;
6565
void* function;
6666
int paramCount;
67+
bool blocking;
6768
} VariadicInvoker;
6869

6970

@@ -84,6 +85,7 @@ variadic_allocate(VALUE klass)
8485
invoker->rbAddress = Qnil;
8586
invoker->rbEnums = Qnil;
8687
invoker->rbReturnType = Qnil;
88+
invoker->blocking = false;
8789

8890
return obj;
8991
}
@@ -115,6 +117,7 @@ variadic_initialize(VALUE self, VALUE rbFunction, VALUE rbParameterTypes, VALUE
115117
invoker->rbEnums = rb_hash_aref(options, ID2SYM(rb_intern("enums")));
116118
invoker->rbAddress = rbFunction;
117119
invoker->function = rbffi_AbstractMemory_Cast(rbFunction, rbffi_PointerClass)->address;
120+
invoker->blocking = RTEST(rb_hash_aref(options, ID2SYM(rb_intern("blocking"))));
118121

119122
#if defined(X86_WIN32)
120123
rbConventionStr = rb_funcall2(convention, rb_intern("to_s"), 0, NULL);
@@ -253,7 +256,28 @@ variadic_invoke(VALUE self, VALUE parameterTypes, VALUE parameterValues)
253256
ffiValues, NULL, 0, invoker->rbEnums);
254257

255258
rbffi_frame_push(&frame);
259+
#ifdef HAVE_RB_THREAD_CALL_WITHOUT_GVL
260+
/* In Call.c, blocking: true is supported on older ruby variants
261+
* without rb_thread_call_without_gvl by allocating on the heap instead
262+
* of the stack. Since this functionality is being added later,
263+
* we’re skipping support for old rubies here. */
264+
if(unlikely(invoker->blocking)) {
265+
rbffi_blocking_call_t* bc;
266+
bc = ALLOCA_N(rbffi_blocking_call_t, 1);
267+
bc->retval = retval;
268+
bc->function = invoker->function;
269+
bc->ffiValues = ffiValues;
270+
bc->params = params;
271+
bc->frame = &frame;
272+
bc->cif = cif;
273+
274+
rb_rescue2(rbffi_do_blocking_call, (VALUE) bc, rbffi_save_frame_exception, (VALUE) &frame, rb_eException, (VALUE) 0);
275+
} else {
276+
ffi_call(&cif, FFI_FN(invoker->function), retval, ffiValues);
277+
}
278+
#else
256279
ffi_call(&cif, FFI_FN(invoker->function), retval, ffiValues);
280+
#endif
257281
rbffi_frame_pop(&frame);
258282

259283
rbffi_save_errno();

spec/ffi/fixtures/FunctionTest.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#ifndef _WIN32
1212
#include <unistd.h>
1313
#include <pthread.h>
14+
#include <stdarg.h>
1415
#include <stdlib.h>
1516
#endif
1617

@@ -61,6 +62,44 @@ void testBlockingClose(struct testBlockingData *self) {
6162
free(self);
6263
}
6364

65+
static int sum_varargs(va_list args) {
66+
char sum = 0;
67+
int arg;
68+
while ((arg = va_arg(args, int)) != 0) {
69+
sum += arg;
70+
}
71+
va_end(args);
72+
return sum;
73+
}
74+
75+
/* Write c to pipe1 and return the value read from pipe2, or 0 if there’s
76+
* an error such as a timeout, or if c does not equal the sum of the
77+
* zero-terminated list of char arguments. */
78+
char testBlockingWRva(struct testBlockingData *self, char c, ...) {
79+
va_list args;
80+
va_start(args, c);
81+
if (sum_varargs(args) != c) {
82+
return 0;
83+
}
84+
85+
if( pipeHelperWriteChar(self->pipe1[1], c) != 1)
86+
return 0;
87+
return pipeHelperReadChar(self->pipe2[0], 10);
88+
}
89+
90+
char testBlockingRWva(struct testBlockingData *self, char c, ...) {
91+
va_list args;
92+
va_start(args, c);
93+
if (sum_varargs(args) != c) {
94+
return 0;
95+
}
96+
97+
char d = pipeHelperReadChar(self->pipe1[0], 10);
98+
if( pipeHelperWriteChar(self->pipe2[1], c) != 1)
99+
return 0;
100+
return d;
101+
}
102+
64103
struct async_data {
65104
void (*fn)(int);
66105
int value;

spec/ffi/variadic_spec.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ module LibTest
1313
enum :enum_type2, [:c3, 42, :c4]
1414
attach_function :pack_varargs, [ :buffer_out, :string, :varargs ], :void
1515
attach_function :pack_varargs2, [ :buffer_out, :enum_type1, :string, :varargs ], :enum_type1
16+
17+
attach_function :testBlockingOpen, [ ], :pointer
18+
attach_function :testBlockingRWva, [ :pointer, :char, :varargs ], :char, :blocking => true
19+
attach_function :testBlockingWRva, [ :pointer, :char, :varargs ], :char, :blocking => true
20+
attach_function :testBlockingClose, [ :pointer ], :void
1621
end
1722

1823
it "takes enum arguments" do
@@ -27,6 +32,20 @@ module LibTest
2732
expect(LibTest.pack_varargs2(buf, :c1, "ii", :int, :c3, :int, :c4)).to eq(:c2)
2833
end
2934

35+
it 'can wrap a blocking function with varargs' do
36+
pending("not supported in 1.8") if RUBY_VERSION =~ /^1\.8\..*/
37+
handle = LibTest.testBlockingOpen
38+
expect(handle).not_to be_null
39+
begin
40+
thWR = Thread.new { LibTest.testBlockingWRva(handle, 63, :int, 40, :int, 23, :int, 0) }
41+
thRW = Thread.new { LibTest.testBlockingRWva(handle, 64, :int, 40, :int, 24, :int, 0) }
42+
expect(thWR.value).to eq(64)
43+
expect(thRW.value).to eq(63)
44+
ensure
45+
LibTest.testBlockingClose(handle)
46+
end
47+
end
48+
3049
[ 0, 127, -128, -1 ].each do |i|
3150
it "call variadic with (:char (#{i})) argument" do
3251
buf = FFI::Buffer.new :long_long

0 commit comments

Comments
 (0)