summaryrefslogtreecommitdiff
path: root/ext/ffi_c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/ffi_c')
-rw-r--r--ext/ffi_c/AbstractMemory.c90
-rw-r--r--ext/ffi_c/AbstractMemory.h5
-rw-r--r--ext/ffi_c/ArrayType.c62
-rw-r--r--ext/ffi_c/ArrayType.h1
-rw-r--r--ext/ffi_c/Buffer.c115
-rw-r--r--ext/ffi_c/Call.c18
-rw-r--r--ext/ffi_c/DynamicLibrary.c115
-rw-r--r--ext/ffi_c/Function.c336
-rw-r--r--ext/ffi_c/Function.h1
-rw-r--r--ext/ffi_c/FunctionInfo.c102
-rw-r--r--ext/ffi_c/LastError.c32
-rw-r--r--ext/ffi_c/MappedType.c89
-rw-r--r--ext/ffi_c/MappedType.h2
-rw-r--r--ext/ffi_c/MemoryPointer.c41
-rw-r--r--ext/ffi_c/MethodHandle.c4
-rw-r--r--ext/ffi_c/Pointer.c97
-rw-r--r--ext/ffi_c/Pointer.h1
-rw-r--r--ext/ffi_c/Struct.c231
-rw-r--r--ext/ffi_c/Struct.h11
-rw-r--r--ext/ffi_c/StructByValue.c64
-rw-r--r--ext/ffi_c/StructLayout.c165
-rw-r--r--ext/ffi_c/Type.c140
-rw-r--r--ext/ffi_c/Type.h4
-rw-r--r--ext/ffi_c/Types.c2
-rw-r--r--ext/ffi_c/Variadic.c88
-rw-r--r--ext/ffi_c/compat.h44
-rwxr-xr-x[-rw-r--r--]ext/ffi_c/extconf.rb5
-rw-r--r--ext/ffi_c/ffi.c4
m---------ext/ffi_c/libffi0
-rw-r--r--ext/ffi_c/rbffi.h2
30 files changed, 1338 insertions, 533 deletions
diff --git a/ext/ffi_c/AbstractMemory.c b/ext/ffi_c/AbstractMemory.c
index 1a7fcde..49da32e 100644
--- a/ext/ffi_c/AbstractMemory.c
+++ b/ext/ffi_c/AbstractMemory.c
@@ -55,21 +55,30 @@
# define RB_OBJ_STRING(obj) StringValueCStr(obj)
#endif
+static size_t memsize(const void *data);
static inline char* memory_address(VALUE self);
VALUE rbffi_AbstractMemoryClass = Qnil;
static VALUE NullPointerErrorClass = Qnil;
static ID id_to_ptr = 0, id_plus = 0, id_call = 0;
-static VALUE
-memory_allocate(VALUE klass)
-{
- AbstractMemory* memory;
- VALUE obj;
- obj = Data_Make_Struct(klass, AbstractMemory, NULL, -1, memory);
- memory->flags = MEM_RD | MEM_WR;
+const rb_data_type_t rbffi_abstract_memory_data_type = { /* extern */
+ .wrap_struct_name = "FFI::AbstractMemory",
+ .function = {
+ .dmark = NULL,
+ .dfree = RUBY_TYPED_DEFAULT_FREE,
+ .dsize = memsize,
+ },
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
- return obj;
+static size_t
+memsize(const void *data)
+{
+ return sizeof(AbstractMemory);
}
+
#define VAL(x, swap) (unlikely(((memory->flags & MEM_SWAP) != 0)) ? swap((x)) : (x))
#define NUM_OP(name, type, toNative, fromNative, swap) \
@@ -87,7 +96,7 @@ static VALUE \
memory_put_##name(VALUE self, VALUE offset, VALUE value) \
{ \
AbstractMemory* memory; \
- Data_Get_Struct(self, AbstractMemory, memory); \
+ TypedData_Get_Struct(self, AbstractMemory, &rbffi_abstract_memory_data_type, memory); \
memory_op_put_##name(memory, NUM2LONG(offset), value); \
return self; \
} \
@@ -96,7 +105,7 @@ static VALUE \
memory_write_##name(VALUE self, VALUE value) \
{ \
AbstractMemory* memory; \
- Data_Get_Struct(self, AbstractMemory, memory); \
+ TypedData_Get_Struct(self, AbstractMemory, &rbffi_abstract_memory_data_type, memory); \
memory_op_put_##name(memory, 0, value); \
return self; \
} \
@@ -115,7 +124,7 @@ static VALUE \
memory_get_##name(VALUE self, VALUE offset) \
{ \
AbstractMemory* memory; \
- Data_Get_Struct(self, AbstractMemory, memory); \
+ TypedData_Get_Struct(self, AbstractMemory, &rbffi_abstract_memory_data_type, memory); \
return memory_op_get_##name(memory, NUM2LONG(offset)); \
} \
static VALUE memory_read_##name(VALUE self); \
@@ -123,7 +132,7 @@ static VALUE \
memory_read_##name(VALUE self) \
{ \
AbstractMemory* memory; \
- Data_Get_Struct(self, AbstractMemory, memory); \
+ TypedData_Get_Struct(self, AbstractMemory, &rbffi_abstract_memory_data_type, memory); \
return memory_op_get_##name(memory, 0); \
} \
static MemoryOp memory_op_##name = { memory_op_get_##name, memory_op_put_##name }; \
@@ -141,7 +150,7 @@ memory_put_array_of_##name(VALUE self, VALUE offset, VALUE ary) \
if (likely(count > 0)) checkWrite(memory); \
checkBounds(memory, off, count * sizeof(type)); \
for (i = 0; i < count; i++) { \
- type tmp = (type) VAL(toNative(RARRAY_PTR(ary)[i]), swap); \
+ type tmp = (type) VAL(toNative(RARRAY_AREF(ary, i)), swap); \
memcpy(memory->address + off + (i * sizeof(type)), &tmp, sizeof(tmp)); \
} \
return self; \
@@ -307,6 +316,7 @@ static VALUE
memory_clear(VALUE self)
{
AbstractMemory* ptr = MEMORY(self);
+ checkWrite(ptr);
memset(ptr->address, 0, ptr->size);
return self;
}
@@ -321,7 +331,7 @@ memory_size(VALUE self)
{
AbstractMemory* ptr;
- Data_Get_Struct(self, AbstractMemory, ptr);
+ TypedData_Get_Struct(self, AbstractMemory, &rbffi_abstract_memory_data_type, ptr);
return LONG2NUM(ptr->size);
}
@@ -344,8 +354,8 @@ memory_get(VALUE self, VALUE type_name, VALUE offset)
nType = rbffi_Type_Lookup(type_name);
if(NIL_P(nType)) goto undefined_type;
- Data_Get_Struct(self, AbstractMemory, ptr);
- Data_Get_Struct(nType, Type, type);
+ TypedData_Get_Struct(self, AbstractMemory, &rbffi_abstract_memory_data_type, ptr);
+ TypedData_Get_Struct(nType, Type, &rbffi_type_data_type, type);
MemoryOp *op = get_memory_op(type);
if(op == NULL) goto undefined_type;
@@ -376,8 +386,8 @@ memory_put(VALUE self, VALUE type_name, VALUE offset, VALUE value)
nType = rbffi_Type_Lookup(type_name);
if(NIL_P(nType)) goto undefined_type;
- Data_Get_Struct(self, AbstractMemory, ptr);
- Data_Get_Struct(nType, Type, type);
+ TypedData_Get_Struct(self, AbstractMemory, &rbffi_abstract_memory_data_type, ptr);
+ TypedData_Get_Struct(nType, Type, &rbffi_type_data_type, type);
MemoryOp *op = get_memory_op(type);
if(op == NULL) goto undefined_type;
@@ -442,7 +452,7 @@ memory_get_array_of_string(int argc, VALUE* argv, VALUE self)
count = (countnum == Qnil ? 0 : NUM2INT(countnum));
retVal = rb_ary_new2(count);
- Data_Get_Struct(self, AbstractMemory, ptr);
+ TypedData_Get_Struct(self, AbstractMemory, &rbffi_abstract_memory_data_type, ptr);
checkRead(ptr);
if (countnum != Qnil) {
@@ -633,7 +643,7 @@ memory_type_size(VALUE self)
{
AbstractMemory* ptr;
- Data_Get_Struct(self, AbstractMemory, ptr);
+ TypedData_Get_Struct(self, AbstractMemory, &rbffi_abstract_memory_data_type, ptr);
return INT2NUM(ptr->typeSize);
}
@@ -651,7 +661,7 @@ memory_aref(VALUE self, VALUE idx)
AbstractMemory* ptr;
VALUE rbOffset = Qnil;
- Data_Get_Struct(self, AbstractMemory, ptr);
+ TypedData_Get_Struct(self, AbstractMemory, &rbffi_abstract_memory_data_type, ptr);
rbOffset = ULONG2NUM(NUM2ULONG(idx) * ptr->typeSize);
@@ -661,7 +671,9 @@ memory_aref(VALUE self, VALUE idx)
static inline char*
memory_address(VALUE obj)
{
- return ((AbstractMemory *) DATA_PTR(obj))->address;
+ AbstractMemory *mem;
+ TypedData_Get_Struct(obj, AbstractMemory, &rbffi_abstract_memory_data_type, mem);
+ return mem->address;
}
static VALUE
@@ -669,24 +681,33 @@ memory_copy_from(VALUE self, VALUE rbsrc, VALUE rblen)
{
AbstractMemory* dst;
- Data_Get_Struct(self, AbstractMemory, dst);
+ TypedData_Get_Struct(self, AbstractMemory, &rbffi_abstract_memory_data_type, dst);
- memcpy(dst->address, rbffi_AbstractMemory_Cast(rbsrc, rbffi_AbstractMemoryClass)->address, NUM2INT(rblen));
+ memcpy(dst->address, rbffi_AbstractMemory_Cast(rbsrc, &rbffi_abstract_memory_data_type)->address, NUM2INT(rblen));
return self;
}
-AbstractMemory*
-rbffi_AbstractMemory_Cast(VALUE obj, VALUE klass)
+/*
+ * call-seq:
+ * res.freeze
+ *
+ * Freeze the AbstractMemory object and unset the writable flag.
+ */
+static VALUE
+memory_freeze(VALUE self)
{
- if (rb_obj_is_kind_of(obj, klass)) {
- AbstractMemory* memory;
- Data_Get_Struct(obj, AbstractMemory, memory);
- return memory;
- }
+ AbstractMemory* ptr = MEMORY(self);
+ ptr->flags &= ~MEM_WR;
+ return rb_call_super(0, NULL);
+}
- rb_raise(rb_eArgError, "Invalid Memory object");
- return NULL;
+AbstractMemory*
+rbffi_AbstractMemory_Cast(VALUE obj, const rb_data_type_t *data_type)
+{
+ AbstractMemory* memory;
+ TypedData_Get_Struct(obj, AbstractMemory, data_type, memory);
+ return memory;
}
void
@@ -781,7 +802,7 @@ rbffi_AbstractMemory_Init(VALUE moduleFFI)
* Document-variable: FFI::AbstractMemory
*/
rb_global_variable(&rbffi_AbstractMemoryClass);
- rb_define_alloc_func(classMemory, memory_allocate);
+ rb_undef_alloc_func(classMemory);
NullPointerErrorClass = rb_define_class_under(moduleFFI, "NullPointerError", rb_eRuntimeError);
/* Document-variable: NullPointerError */
@@ -1096,6 +1117,7 @@ rbffi_AbstractMemory_Init(VALUE moduleFFI)
rb_define_method(classMemory, "type_size", memory_type_size, 0);
rb_define_method(classMemory, "[]", memory_aref, 1);
rb_define_method(classMemory, "__copy_from__", memory_copy_from, 2);
+ rb_define_method(classMemory, "freeze", memory_freeze, 0 );
id_to_ptr = rb_intern("to_ptr");
id_call = rb_intern("call");
diff --git a/ext/ffi_c/AbstractMemory.h b/ext/ffi_c/AbstractMemory.h
index 1119288..5973bac 100644
--- a/ext/ffi_c/AbstractMemory.h
+++ b/ext/ffi_c/AbstractMemory.h
@@ -86,12 +86,13 @@ struct AbstractMemory_ {
};
+extern const rb_data_type_t rbffi_abstract_memory_data_type;
extern VALUE rbffi_AbstractMemoryClass;
extern MemoryOps rbffi_AbstractMemoryOps;
extern void rbffi_AbstractMemory_Init(VALUE ffiModule);
-extern AbstractMemory* rbffi_AbstractMemory_Cast(VALUE obj, VALUE klass);
+extern AbstractMemory* rbffi_AbstractMemory_Cast(VALUE obj, const rb_data_type_t *data_type);
extern void rbffi_AbstractMemory_Error(AbstractMemory *, int op);
@@ -161,7 +162,7 @@ get_memory_op(Type* type)
}
}
-#define MEMORY(obj) rbffi_AbstractMemory_Cast((obj), rbffi_AbstractMemoryClass)
+#define MEMORY(obj) rbffi_AbstractMemory_Cast((obj), &rbffi_abstract_memory_data_type)
#define MEMORY_PTR(obj) MEMORY((obj))->address
#define MEMORY_LEN(obj) MEMORY((obj))->size
diff --git a/ext/ffi_c/ArrayType.c b/ext/ffi_c/ArrayType.c
index bfd666a..b1cbcea 100644
--- a/ext/ffi_c/ArrayType.c
+++ b/ext/ffi_c/ArrayType.c
@@ -29,12 +29,30 @@
#include <ruby.h>
#include <ffi.h>
+#include "compat.h"
#include "ArrayType.h"
static VALUE array_type_s_allocate(VALUE klass);
static VALUE array_type_initialize(VALUE self, VALUE rbComponentType, VALUE rbLength);
-static void array_type_mark(ArrayType *);
-static void array_type_free(ArrayType *);
+static void array_type_mark(void *);
+static void array_type_compact(void *);
+static void array_type_free(void *);
+static size_t array_type_memsize(const void *);
+
+const rb_data_type_t rbffi_array_type_data_type = { /* extern */
+ .wrap_struct_name = "FFI::ArrayType",
+ .function = {
+ .dmark = array_type_mark,
+ .dfree = array_type_free,
+ .dsize = array_type_memsize,
+ ffi_compact_callback( array_type_compact )
+ },
+ .parent = &rbffi_type_data_type,
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
+
VALUE rbffi_ArrayTypeClass = Qnil;
@@ -44,32 +62,50 @@ array_type_s_allocate(VALUE klass)
ArrayType* array;
VALUE obj;
- obj = Data_Make_Struct(klass, ArrayType, array_type_mark, array_type_free, array);
+ obj = TypedData_Make_Struct(klass, ArrayType, &rbffi_array_type_data_type, array);
array->base.nativeType = NATIVE_ARRAY;
array->base.ffiType = xcalloc(1, sizeof(*array->base.ffiType));
array->base.ffiType->type = FFI_TYPE_STRUCT;
array->base.ffiType->size = 0;
array->base.ffiType->alignment = 0;
- array->rbComponentType = Qnil;
+ RB_OBJ_WRITE(obj, &array->rbComponentType, Qnil);
return obj;
}
static void
-array_type_mark(ArrayType *array)
+array_type_mark(void *data)
+{
+ ArrayType *array = (ArrayType *)data;
+ rb_gc_mark_movable(array->rbComponentType);
+}
+
+static void
+array_type_compact(void *data)
{
- rb_gc_mark(array->rbComponentType);
+ ArrayType *array = (ArrayType *)data;
+ ffi_gc_location(array->rbComponentType);
}
static void
-array_type_free(ArrayType *array)
+array_type_free(void *data)
{
+ ArrayType *array = (ArrayType *)data;
xfree(array->base.ffiType);
xfree(array->ffiTypes);
xfree(array);
}
+static size_t
+array_type_memsize(const void *data)
+{
+ const ArrayType *array = (const ArrayType *)data;
+ size_t memsize = sizeof(ArrayType);
+ memsize += array->length * sizeof(*array->ffiTypes);
+ memsize += sizeof(*array->base.ffiType);
+ return memsize;
+}
/*
* call-seq: initialize(component_type, length)
@@ -84,12 +120,12 @@ array_type_initialize(VALUE self, VALUE rbComponentType, VALUE rbLength)
ArrayType* array;
int i;
- Data_Get_Struct(self, ArrayType, array);
+ TypedData_Get_Struct(self, ArrayType, &rbffi_array_type_data_type, array);
array->length = NUM2UINT(rbLength);
- array->rbComponentType = rbComponentType;
- Data_Get_Struct(rbComponentType, Type, array->componentType);
-
+ RB_OBJ_WRITE(self, &array->rbComponentType, rbComponentType);
+ TypedData_Get_Struct(rbComponentType, Type, &rbffi_type_data_type, array->componentType);
+
array->ffiTypes = xcalloc(array->length + 1, sizeof(*array->ffiTypes));
array->base.ffiType->elements = array->ffiTypes;
array->base.ffiType->size = array->componentType->ffiType->size * array->length;
@@ -112,7 +148,7 @@ array_type_length(VALUE self)
{
ArrayType* array;
- Data_Get_Struct(self, ArrayType, array);
+ TypedData_Get_Struct(self, ArrayType, &rbffi_array_type_data_type, array);
return UINT2NUM(array->length);
}
@@ -127,7 +163,7 @@ array_type_element_type(VALUE self)
{
ArrayType* array;
- Data_Get_Struct(self, ArrayType, array);
+ TypedData_Get_Struct(self, ArrayType, &rbffi_array_type_data_type, array);
return array->rbComponentType;
}
diff --git a/ext/ffi_c/ArrayType.h b/ext/ffi_c/ArrayType.h
index 356ffb1..9b1eba0 100644
--- a/ext/ffi_c/ArrayType.h
+++ b/ext/ffi_c/ArrayType.h
@@ -48,6 +48,7 @@ typedef struct ArrayType_ {
} ArrayType;
extern void rbffi_ArrayType_Init(VALUE moduleFFI);
+extern const rb_data_type_t rbffi_array_type_data_type;
extern VALUE rbffi_ArrayTypeClass;
diff --git a/ext/ffi_c/Buffer.c b/ext/ffi_c/Buffer.c
index b5f39a4..78339f3 100644
--- a/ext/ffi_c/Buffer.c
+++ b/ext/ffi_c/Buffer.c
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2008-2010 Wayne Meissner
* Copyright (C) 2009 Aman Gupta <aman@tmm1.net>
- *
+ *
* Copyright (c) 2008-2013, Ruby FFI project contributors
* All rights reserved.
*
@@ -39,7 +39,7 @@
#define BUFFER_EMBED_MAXLEN (8)
typedef struct Buffer {
AbstractMemory memory;
-
+
union {
VALUE rbParent; /* link to parent buffer */
char* storage; /* start of malloc area */
@@ -49,9 +49,40 @@ typedef struct Buffer {
static VALUE buffer_allocate(VALUE klass);
static VALUE buffer_initialize(int argc, VALUE* argv, VALUE self);
-static void buffer_release(Buffer* ptr);
-static void buffer_mark(Buffer* ptr);
+static void buffer_release(void *data);
+static void buffer_mark(void *data);
+static void buffer_compact(void *data);
static VALUE buffer_free(VALUE self);
+static size_t allocated_buffer_memsize(const void *data);
+static size_t buffer_memsize(const void *data);
+
+static const rb_data_type_t buffer_data_type = {
+ .wrap_struct_name = "FFI::Buffer",
+ .function = {
+ .dmark = buffer_mark,
+ .dfree = RUBY_TYPED_DEFAULT_FREE,
+ .dsize = buffer_memsize,
+ ffi_compact_callback( buffer_compact )
+ },
+ .parent = &rbffi_abstract_memory_data_type,
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
+
+static const rb_data_type_t allocated_buffer_data_type = {
+ .wrap_struct_name = "FFI::Buffer(allocated)",
+ .function = {
+ .dmark = NULL,
+ .dfree = buffer_release,
+ .dsize = allocated_buffer_memsize,
+ },
+ .parent = &buffer_data_type,
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
+
static VALUE BufferClass = Qnil;
@@ -61,21 +92,22 @@ buffer_allocate(VALUE klass)
Buffer* buffer;
VALUE obj;
- obj = Data_Make_Struct(klass, Buffer, NULL, buffer_release, buffer);
- buffer->data.rbParent = Qnil;
+ obj = TypedData_Make_Struct(klass, Buffer, &allocated_buffer_data_type, buffer);
+ RB_OBJ_WRITE(obj, &buffer->data.rbParent, Qnil);
buffer->memory.flags = MEM_RD | MEM_WR;
return obj;
}
static void
-buffer_release(Buffer* ptr)
+buffer_release(void *data)
{
+ Buffer *ptr = (Buffer *)data;
if ((ptr->memory.flags & MEM_EMBED) == 0 && ptr->data.storage != NULL) {
xfree(ptr->data.storage);
ptr->data.storage = NULL;
}
-
+
xfree(ptr);
}
@@ -95,7 +127,7 @@ buffer_initialize(int argc, VALUE* argv, VALUE self)
Buffer* p;
int nargs;
- Data_Get_Struct(self, Buffer, p);
+ TypedData_Get_Struct(self, Buffer, &buffer_data_type, p);
nargs = rb_scan_args(argc, argv, "12", &rbSize, &rbCount, &rbClear);
p->memory.typeSize = rbffi_type_size(rbSize);
@@ -110,11 +142,11 @@ buffer_initialize(int argc, VALUE* argv, VALUE self)
/* ensure the memory is aligned on at least a 8 byte boundary */
p->memory.address = (void *) (((uintptr_t) p->data.storage + 0x7) & (uintptr_t) ~0x7ULL);
-
+
if (p->memory.size > 0 && (nargs < 3 || RTEST(rbClear))) {
memset(p->memory.address, 0, p->memory.size);
}
-
+
} else {
p->memory.flags |= MEM_EMBED;
p->memory.address = (void *) &p->data.embed[0];
@@ -137,9 +169,9 @@ buffer_initialize_copy(VALUE self, VALUE other)
{
AbstractMemory* src;
Buffer* dst;
-
- Data_Get_Struct(self, Buffer, dst);
- src = rbffi_AbstractMemory_Cast(other, BufferClass);
+
+ TypedData_Get_Struct(self, Buffer, &buffer_data_type, dst);
+ src = rbffi_AbstractMemory_Cast(other, &buffer_data_type);
if ((dst->memory.flags & MEM_EMBED) == 0 && dst->data.storage != NULL) {
xfree(dst->data.storage);
}
@@ -148,11 +180,11 @@ buffer_initialize_copy(VALUE self, VALUE other)
rb_raise(rb_eNoMemError, "failed to allocate memory size=%lu bytes", src->size);
return Qnil;
}
-
+
dst->memory.address = (void *) (((uintptr_t) dst->data.storage + 0x7) & (uintptr_t) ~0x7ULL);
dst->memory.size = src->size;
dst->memory.typeSize = src->typeSize;
-
+
/* finally, copy the actual buffer contents */
memcpy(dst->memory.address, src->address, src->size);
@@ -171,16 +203,16 @@ slice(VALUE self, long offset, long len)
Buffer* ptr;
Buffer* result;
VALUE obj = Qnil;
-
- Data_Get_Struct(self, Buffer, ptr);
+
+ TypedData_Get_Struct(self, Buffer, &buffer_data_type, ptr);
checkBounds(&ptr->memory, offset, len);
- obj = Data_Make_Struct(BufferClass, Buffer, buffer_mark, -1, result);
+ obj = TypedData_Make_Struct(BufferClass, Buffer, &buffer_data_type, result);
result->memory.address = ptr->memory.address + offset;
result->memory.size = len;
result->memory.flags = ptr->memory.flags;
result->memory.typeSize = ptr->memory.typeSize;
- result->data.rbParent = self;
+ RB_OBJ_WRITE(obj, &result->data.rbParent, self);
return obj;
}
@@ -197,7 +229,7 @@ buffer_plus(VALUE self, VALUE rbOffset)
Buffer* ptr;
long offset = NUM2LONG(rbOffset);
- Data_Get_Struct(self, Buffer, ptr);
+ TypedData_Get_Struct(self, Buffer, &buffer_data_type, ptr);
return slice(self, offset, ptr->memory.size - offset);
}
@@ -226,10 +258,10 @@ buffer_inspect(VALUE self)
char tmp[100];
Buffer* ptr;
- Data_Get_Struct(self, Buffer, ptr);
+ TypedData_Get_Struct(self, Buffer, &buffer_data_type, ptr);
snprintf(tmp, sizeof(tmp), "#<FFI:Buffer:%p address=%p size=%ld>", ptr, ptr->memory.address, ptr->memory.size);
-
+
return rb_str_new2(tmp);
}
@@ -255,7 +287,7 @@ buffer_order(int argc, VALUE* argv, VALUE self)
{
Buffer* ptr;
- Data_Get_Struct(self, Buffer, ptr);
+ TypedData_Get_Struct(self, Buffer, &buffer_data_type, ptr);
if (argc == 0) {
int order = (ptr->memory.flags & MEM_SWAP) == 0 ? BYTE_ORDER : SWAPPED_ORDER;
return order == BIG_ENDIAN ? ID2SYM(rb_intern("big")) : ID2SYM(rb_intern("little"));
@@ -279,7 +311,7 @@ buffer_order(int argc, VALUE* argv, VALUE self)
Buffer* p2;
VALUE retval = slice(self, 0, ptr->memory.size);
- Data_Get_Struct(retval, Buffer, p2);
+ TypedData_Get_Struct(retval, Buffer, &buffer_data_type, p2);
p2->memory.flags |= MEM_SWAP;
return retval;
}
@@ -294,7 +326,7 @@ buffer_free(VALUE self)
{
Buffer* ptr;
- Data_Get_Struct(self, Buffer, ptr);
+ TypedData_Get_Struct(self, Buffer, &buffer_data_type, ptr);
if ((ptr->memory.flags & MEM_EMBED) == 0 && ptr->data.storage != NULL) {
xfree(ptr->data.storage);
ptr->data.storage = NULL;
@@ -304,9 +336,34 @@ buffer_free(VALUE self)
}
static void
-buffer_mark(Buffer* ptr)
+buffer_mark(void *data)
+{
+ Buffer *ptr = (Buffer *)data;
+ rb_gc_mark_movable(ptr->data.rbParent);
+}
+
+static void
+buffer_compact(void *data)
+{
+ Buffer *ptr = (Buffer *)data;
+ ffi_gc_location(ptr->data.rbParent);
+}
+
+static size_t
+buffer_memsize(const void *data)
{
- rb_gc_mark(ptr->data.rbParent);
+ return sizeof(Buffer);
+}
+
+static size_t
+allocated_buffer_memsize(const void *data)
+{
+ const Buffer *ptr = (const Buffer *)data;
+ size_t memsize = sizeof(Buffer);
+ if ((ptr->memory.flags & MEM_EMBED) == 0 && ptr->data.storage != NULL) {
+ memsize += ptr->memory.size;
+ }
+ return memsize;
}
void
@@ -348,7 +405,7 @@ rbffi_Buffer_Init(VALUE moduleFFI)
rb_define_alias(rb_singleton_class(BufferClass), "new_in", "alloc_in");
rb_define_alias(rb_singleton_class(BufferClass), "new_out", "alloc_out");
rb_define_alias(rb_singleton_class(BufferClass), "new_inout", "alloc_inout");
-
+
rb_define_method(BufferClass, "initialize", buffer_initialize, -1);
rb_define_method(BufferClass, "initialize_copy", buffer_initialize_copy, 1);
rb_define_method(BufferClass, "order", buffer_order, -1);
diff --git a/ext/ffi_c/Call.c b/ext/ffi_c/Call.c
index bd6c277..ccb711c 100644
--- a/ext/ffi_c/Call.c
+++ b/ext/ffi_c/Call.c
@@ -420,11 +420,15 @@ getPointer(VALUE value, int type)
{
if (likely(type == T_DATA && rb_obj_is_kind_of(value, rbffi_AbstractMemoryClass))) {
- return ((AbstractMemory *) DATA_PTR(value))->address;
+ AbstractMemory *mem;
+ TypedData_Get_Struct(value, AbstractMemory, &rbffi_abstract_memory_data_type, mem);
+ return mem->address;
} else if (type == T_DATA && rb_obj_is_kind_of(value, rbffi_StructClass)) {
- AbstractMemory* memory = ((Struct *) DATA_PTR(value))->pointer;
+ Struct* s;
+ TypedData_Get_Struct(value, Struct, &rbffi_struct_data_type, s);
+ AbstractMemory* memory = s->pointer;
return memory != NULL ? memory->address : NULL;
} else if (type == T_STRING) {
@@ -439,7 +443,9 @@ getPointer(VALUE value, int type)
VALUE ptr = rb_funcall2(value, id_to_ptr, 0, NULL);
if (rb_obj_is_kind_of(ptr, rbffi_AbstractMemoryClass) && TYPE(ptr) == T_DATA) {
- return ((AbstractMemory *) DATA_PTR(ptr))->address;
+ AbstractMemory *mem;
+ TypedData_Get_Struct(ptr, AbstractMemory, &rbffi_abstract_memory_data_type, mem);
+ return mem->address;
}
rb_raise(rb_eArgError, "to_ptr returned an invalid pointer");
}
@@ -466,14 +472,16 @@ callback_param(VALUE proc, VALUE cbInfo)
/* Handle Function pointers here */
if (rb_obj_is_kind_of(proc, rbffi_FunctionClass)) {
AbstractMemory* ptr;
- Data_Get_Struct(proc, AbstractMemory, ptr);
+ TypedData_Get_Struct(proc, AbstractMemory, &rbffi_abstract_memory_data_type, ptr);
return ptr->address;
}
callback = rbffi_Function_ForProc(cbInfo, proc);
RB_GC_GUARD(callback);
- return ((AbstractMemory *) DATA_PTR(callback))->address;
+ AbstractMemory *mem;
+ TypedData_Get_Struct(callback, AbstractMemory, &rbffi_abstract_memory_data_type, mem);
+ return mem->address;
}
diff --git a/ext/ffi_c/DynamicLibrary.c b/ext/ffi_c/DynamicLibrary.c
index 78b3de6..9096e74 100644
--- a/ext/ffi_c/DynamicLibrary.c
+++ b/ext/ffi_c/DynamicLibrary.c
@@ -50,17 +50,45 @@
typedef struct LibrarySymbol_ {
Pointer base;
- VALUE library;
VALUE name;
} LibrarySymbol;
-static VALUE library_initialize(VALUE self, VALUE libname, VALUE libflags);
-static void library_free(Library* lib);
+static VALUE library_initialize(VALUE self, VALUE libname, VALUE libflags);
+static void library_free(void *);
+static size_t library_memsize(const void *);
static VALUE symbol_allocate(VALUE klass);
static VALUE symbol_new(VALUE library, void* address, VALUE name);
-static void symbol_mark(LibrarySymbol* sym);
+static void symbol_mark(void *data);
+static void symbol_compact(void *data);
+static size_t symbol_memsize(const void *data);
+
+static const rb_data_type_t rbffi_library_data_type = {
+ .wrap_struct_name = "FFI::DynamicLibrary",
+ .function = {
+ .dmark = NULL,
+ .dfree = library_free,
+ .dsize = library_memsize,
+ },
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
+
+static const rb_data_type_t library_symbol_data_type = {
+ .wrap_struct_name = "FFI::DynamicLibrary::Symbol",
+ .function = {
+ .dmark = symbol_mark,
+ .dfree = RUBY_TYPED_DEFAULT_FREE,
+ .dsize = symbol_memsize,
+ ffi_compact_callback( symbol_compact )
+ },
+ .parent = &rbffi_pointer_data_type,
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
static VALUE LibraryClass = Qnil, SymbolClass = Qnil;
@@ -80,7 +108,7 @@ static VALUE
library_allocate(VALUE klass)
{
Library* library;
- return Data_Make_Struct(klass, Library, NULL, library_free, library);
+ return TypedData_Make_Struct(klass, Library, &rbffi_library_data_type, library);
}
/*
@@ -113,9 +141,9 @@ library_initialize(VALUE self, VALUE libname, VALUE libflags)
Check_Type(libflags, T_FIXNUM);
- Data_Get_Struct(self, Library, library);
+ TypedData_Get_Struct(self, Library, &rbffi_library_data_type, library);
flags = libflags != Qnil ? NUM2UINT(libflags) : 0;
-
+
library->handle = dl_open(libname != Qnil ? StringValueCStr(libname) : NULL, flags);
if (library->handle == NULL) {
char errmsg[1024];
@@ -133,7 +161,9 @@ library_initialize(VALUE self, VALUE libname, VALUE libflags)
library->handle = RTLD_DEFAULT;
}
#endif
- rb_iv_set(self, "@name", libname != Qnil ? libname : rb_str_new2("[current process]"));
+ rb_iv_set(self, "@name", libname != Qnil ? rb_str_new_frozen(libname) : rb_str_new2("[current process]"));
+
+ rb_obj_freeze(self);
return self;
}
@@ -144,9 +174,9 @@ library_dlsym(VALUE self, VALUE name)
void* address = NULL;
Check_Type(name, T_STRING);
- Data_Get_Struct(self, Library, library);
+ TypedData_Get_Struct(self, Library, &rbffi_library_data_type, library);
address = dl_sym(library->handle, StringValueCStr(name));
-
+
return address != NULL ? symbol_new(self, address, name) : Qnil;
}
@@ -163,8 +193,10 @@ library_dlerror(VALUE self)
}
static void
-library_free(Library* library)
+library_free(void *data)
{
+ Library *library = (Library*)data;
+
/* dlclose() on MacOS tends to segfault - avoid it */
#ifndef __APPLE__
if (library->handle != NULL) {
@@ -174,6 +206,12 @@ library_free(Library* library)
xfree(library);
}
+static size_t
+library_memsize(const void *data)
+{
+ return sizeof(Library);
+}
+
#if (defined(_WIN32) || defined(__WIN32__)) && !defined(__CYGWIN__)
static void*
dl_open(const char* name, int flags)
@@ -189,8 +227,19 @@ dl_open(const char* name, int flags)
static void
dl_error(char* buf, int size)
{
- FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(),
- 0, buf, size, NULL);
+ // Get the last error code
+ DWORD error = GetLastError();
+
+ // Get the associated message
+ LPSTR message = NULL;
+ FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL, error, 0, (LPSTR)&message, 0, NULL);
+
+ // Update the passed in buffer
+ snprintf(buf, size, "Failed with error %d: %s", error, message);
+
+ // Free the allocated message
+ LocalFree(message);
}
#endif
@@ -198,10 +247,9 @@ static VALUE
symbol_allocate(VALUE klass)
{
LibrarySymbol* sym;
- VALUE obj = Data_Make_Struct(klass, LibrarySymbol, NULL, -1, sym);
- sym->name = Qnil;
- sym->library = Qnil;
- sym->base.rbParent = Qnil;
+ VALUE obj = TypedData_Make_Struct(klass, LibrarySymbol, &library_symbol_data_type, sym);
+ RB_OBJ_WRITE(obj, &sym->base.rbParent, Qnil);
+ RB_OBJ_WRITE(obj, &sym->name, Qnil);
return obj;
}
@@ -224,23 +272,39 @@ static VALUE
symbol_new(VALUE library, void* address, VALUE name)
{
LibrarySymbol* sym;
- VALUE obj = Data_Make_Struct(SymbolClass, LibrarySymbol, symbol_mark, -1, sym);
+ VALUE obj = TypedData_Make_Struct(SymbolClass, LibrarySymbol, &library_symbol_data_type, sym);
sym->base.memory.address = address;
sym->base.memory.size = LONG_MAX;
sym->base.memory.typeSize = 1;
sym->base.memory.flags = MEM_RD | MEM_WR;
- sym->library = library;
- sym->name = name;
+ RB_OBJ_WRITE(obj, &sym->base.rbParent, library);
+ RB_OBJ_WRITE(obj, &sym->name, rb_str_new_frozen(name));
+ rb_obj_freeze(obj);
return obj;
}
static void
-symbol_mark(LibrarySymbol* sym)
+symbol_mark(void *data)
+{
+ LibrarySymbol *sym = (LibrarySymbol *)data;
+ rb_gc_mark_movable(sym->base.rbParent);
+ rb_gc_mark_movable(sym->name);
+}
+
+static void
+symbol_compact(void *data)
{
- rb_gc_mark(sym->library);
- rb_gc_mark(sym->name);
+ LibrarySymbol *sym = (LibrarySymbol *)data;
+ ffi_gc_location(sym->base.rbParent);
+ ffi_gc_location(sym->name);
+}
+
+static size_t
+symbol_memsize(const void *data)
+{
+ return sizeof(LibrarySymbol);
}
/*
@@ -254,8 +318,8 @@ symbol_inspect(VALUE self)
LibrarySymbol* sym;
char buf[256];
- Data_Get_Struct(self, LibrarySymbol, sym);
- snprintf(buf, sizeof(buf), "#<FFI::Library::Symbol name=%s address=%p>",
+ TypedData_Get_Struct(self, LibrarySymbol, &library_symbol_data_type, sym);
+ snprintf(buf, sizeof(buf), "#<FFI::DynamicLibrary::Symbol name=%s address=%p>",
StringValueCStr(sym->name), sym->base.memory.address);
return rb_str_new2(buf);
}
@@ -331,4 +395,3 @@ rbffi_DynamicLibrary_Init(VALUE moduleFFI)
#undef DEF
}
-
diff --git a/ext/ffi_c/Function.c b/ext/ffi_c/Function.c
index 1a57591..7810056 100644
--- a/ext/ffi_c/Function.c
+++ b/ext/ffi_c/Function.c
@@ -42,6 +42,10 @@
#include <ruby.h>
#include <ruby/thread.h>
+#if HAVE_RB_EXT_RACTOR_SAFE
+#include <ruby/ractor.h>
+#endif
+
#include <ffi.h>
#if defined(HAVE_NATIVETHREAD) && !defined(_WIN32)
#include <pthread.h>
@@ -65,6 +69,9 @@
#include "MethodHandle.h"
#include "Function.h"
+#define DEFER_ASYNC_CALLBACK 1
+
+struct async_cb_dispatcher;
typedef struct Function_ {
Pointer base;
FunctionType* info;
@@ -73,10 +80,15 @@ typedef struct Function_ {
Closure* closure;
VALUE rbProc;
VALUE rbFunctionInfo;
+#if defined(DEFER_ASYNC_CALLBACK)
+ struct async_cb_dispatcher *dispatcher;
+#endif
} Function;
-static void function_mark(Function *);
-static void function_free(Function *);
+static void function_mark(void *data);
+static void function_compact(void *data);
+static void function_free(void *data);
+static size_t function_memsize(const void *data);
static VALUE function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc);
static void callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data);
static bool callback_prep(void* ctx, void* code, Closure* closure, char* errmsg, size_t errmsgsize);
@@ -84,9 +96,6 @@ static void* callback_with_gvl(void* data);
static VALUE invoke_callback(VALUE data);
static VALUE save_callback_exception(VALUE data, VALUE exc);
-#define DEFER_ASYNC_CALLBACK 1
-
-
#if defined(DEFER_ASYNC_CALLBACK)
static VALUE async_cb_event(void *);
static VALUE async_cb_call(void *);
@@ -95,11 +104,21 @@ static VALUE async_cb_call(void *);
extern int ruby_thread_has_gvl_p(void);
extern int ruby_native_thread_p(void);
-VALUE rbffi_FunctionClass = Qnil;
+static const rb_data_type_t function_data_type = {
+ .wrap_struct_name = "FFI::Function",
+ .function = {
+ .dmark = function_mark,
+ .dfree = function_free,
+ .dsize = function_memsize,
+ ffi_compact_callback( function_compact )
+ },
+ .parent = &rbffi_pointer_data_type,
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
-#if defined(DEFER_ASYNC_CALLBACK)
-static VALUE async_cb_thread = Qnil;
-#endif
+VALUE rbffi_FunctionClass = Qnil;
static ID id_call = 0, id_to_native = 0, id_from_native = 0, id_cbtable = 0, id_cb_ref = 0;
@@ -110,7 +129,10 @@ struct gvl_callback {
bool done;
rbffi_frame_t *frame;
#if defined(DEFER_ASYNC_CALLBACK)
+ struct async_cb_dispatcher *dispatcher;
struct gvl_callback* next;
+
+ /* Signal when the callback has finished and retval is set */
# ifndef _WIN32
pthread_cond_t async_cond;
pthread_mutex_t async_mutex;
@@ -122,16 +144,74 @@ struct gvl_callback {
#if defined(DEFER_ASYNC_CALLBACK)
-static struct gvl_callback* async_cb_list = NULL;
+struct async_cb_dispatcher {
+ /* the Ractor-local dispatcher thread */
+ VALUE thread;
+
+ /* single linked list of pending callbacks */
+ struct gvl_callback* async_cb_list;
+
+ /* Signal new entries in async_cb_list */
# ifndef _WIN32
- static pthread_mutex_t async_cb_mutex = PTHREAD_MUTEX_INITIALIZER;
- static pthread_cond_t async_cb_cond = PTHREAD_COND_INITIALIZER;
+ pthread_mutex_t async_cb_mutex;
+ pthread_cond_t async_cb_cond;
# else
- static HANDLE async_cb_cond;
- static CRITICAL_SECTION async_cb_lock;
+ HANDLE async_cb_cond;
+ CRITICAL_SECTION async_cb_lock;
# endif
-#endif
+};
+
+#if HAVE_RB_EXT_RACTOR_SAFE
+static void
+async_cb_dispatcher_mark(void *ptr)
+{
+ struct async_cb_dispatcher *ctx = (struct async_cb_dispatcher *)ptr;
+ rb_gc_mark(ctx->thread);
+}
+
+static void
+async_cb_dispatcher_free(void *ptr)
+{
+ struct async_cb_dispatcher *ctx = (struct async_cb_dispatcher *)ptr;
+ xfree(ctx);
+}
+
+struct rb_ractor_local_storage_type async_cb_dispatcher_key_type = {
+ async_cb_dispatcher_mark,
+ async_cb_dispatcher_free,
+};
+
+static rb_ractor_local_key_t async_cb_dispatcher_key;
+
+static struct async_cb_dispatcher *
+async_cb_dispatcher_get(void)
+{
+ struct async_cb_dispatcher *ctx = (struct async_cb_dispatcher *)rb_ractor_local_storage_ptr(async_cb_dispatcher_key);
+ return ctx;
+}
+
+static void
+async_cb_dispatcher_set(struct async_cb_dispatcher *ctx)
+{
+ rb_ractor_local_storage_ptr_set(async_cb_dispatcher_key, ctx);
+}
+#else
+// for ruby 2.x
+static struct async_cb_dispatcher *async_cb_dispatcher = NULL;
+static struct async_cb_dispatcher *
+async_cb_dispatcher_get(void)
+{
+ return async_cb_dispatcher;
+}
+
+static void
+async_cb_dispatcher_set(struct async_cb_dispatcher *ctx)
+{
+ async_cb_dispatcher = ctx;
+}
+#endif
+#endif
static VALUE
function_allocate(VALUE klass)
@@ -139,28 +219,39 @@ function_allocate(VALUE klass)
Function *fn;
VALUE obj;
- obj = Data_Make_Struct(klass, Function, function_mark, function_free, fn);
+ obj = TypedData_Make_Struct(klass, Function, &function_data_type, fn);
fn->base.memory.flags = MEM_RD;
- fn->base.rbParent = Qnil;
- fn->rbProc = Qnil;
- fn->rbFunctionInfo = Qnil;
+ RB_OBJ_WRITE(obj, &fn->base.rbParent, Qnil);
+ RB_OBJ_WRITE(obj, &fn->rbProc, Qnil);
+ RB_OBJ_WRITE(obj, &fn->rbFunctionInfo, Qnil);
fn->autorelease = true;
return obj;
}
static void
-function_mark(Function *fn)
+function_mark(void *data)
{
- rb_gc_mark(fn->base.rbParent);
- rb_gc_mark(fn->rbProc);
- rb_gc_mark(fn->rbFunctionInfo);
+ Function *fn = (Function *)data;
+ rb_gc_mark_movable(fn->base.rbParent);
+ rb_gc_mark_movable(fn->rbProc);
+ rb_gc_mark_movable(fn->rbFunctionInfo);
}
static void
-function_free(Function *fn)
+function_compact(void *data)
{
+ Function *fn = (Function *)data;
+ ffi_gc_location(fn->base.rbParent);
+ ffi_gc_location(fn->rbProc);
+ ffi_gc_location(fn->rbFunctionInfo);
+}
+
+static void
+function_free(void *data)
+{
+ Function *fn = (Function *)data;
if (fn->methodHandle != NULL) {
rbffi_MethodHandle_Free(fn->methodHandle);
}
@@ -172,6 +263,20 @@ function_free(Function *fn)
xfree(fn);
}
+static size_t
+function_memsize(const void *data)
+{
+ const Function *fn = (const Function *)data;
+ size_t memsize = sizeof(Function);
+
+ // Would be nice to better account for MethodHandle and Closure too.
+ if (fn->closure) {
+ memsize += sizeof(Closure);
+ }
+
+ return memsize;
+}
+
/*
* @param [Type, Symbol] return_type return type for the function
* @param [Array<Type, Symbol>] param_types array of parameters types
@@ -254,7 +359,7 @@ rbffi_Function_ForProc(VALUE rbFunctionInfo, VALUE proc)
/* If the first callback reference has the same function function signature, use it */
if (cbref != Qnil && CLASS_OF(cbref) == rbffi_FunctionClass) {
Function* fp;
- Data_Get_Struct(cbref, Function, fp);
+ TypedData_Get_Struct(cbref, Function, &function_data_type, fp);
if (fp->rbFunctionInfo == rbFunctionInfo) {
return cbref;
}
@@ -287,9 +392,7 @@ static void
after_fork_callback(void)
{
/* Ensure that a new dispatcher thread is started in a forked process */
- async_cb_thread = Qnil;
- pthread_mutex_init(&async_cb_mutex, NULL);
- pthread_cond_init(&async_cb_cond, NULL);
+ async_cb_dispatcher_set(NULL);
}
#endif
@@ -298,17 +401,17 @@ function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc)
{
Function* fn = NULL;
- Data_Get_Struct(self, Function, fn);
+ TypedData_Get_Struct(self, Function, &function_data_type, fn);
- fn->rbFunctionInfo = rbFunctionInfo;
+ RB_OBJ_WRITE(self, &fn->rbFunctionInfo, rbFunctionInfo);
- Data_Get_Struct(fn->rbFunctionInfo, FunctionType, fn->info);
+ TypedData_Get_Struct(fn->rbFunctionInfo, FunctionType, &rbffi_fntype_data_type, fn->info);
if (rb_obj_is_kind_of(rbProc, rbffi_PointerClass)) {
Pointer* orig;
- Data_Get_Struct(rbProc, Pointer, orig);
+ TypedData_Get_Struct(rbProc, Pointer, &rbffi_pointer_data_type, orig);
fn->base.memory = orig->memory;
- fn->base.rbParent = rbProc;
+ RB_OBJ_WRITE(self, &fn->base.rbParent, rbProc);
} else if (rb_obj_is_kind_of(rbProc, rb_cProc) || rb_respond_to(rbProc, id_call)) {
if (fn->info->closurePool == NULL) {
@@ -319,17 +422,30 @@ function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc)
}
#if defined(DEFER_ASYNC_CALLBACK)
- if (async_cb_thread == Qnil) {
+ {
+ struct async_cb_dispatcher *ctx = async_cb_dispatcher_get();
+ if (ctx == NULL) {
+ ctx = (struct async_cb_dispatcher*)ALLOC(struct async_cb_dispatcher);
+ ctx->async_cb_list = NULL;
#if !defined(_WIN32)
- if( pthread_atfork(NULL, NULL, after_fork_callback) ){
- rb_warn("FFI: unable to register fork callback");
- }
+ pthread_mutex_init(&ctx->async_cb_mutex, NULL);
+ pthread_cond_init(&ctx->async_cb_cond, NULL);
+ if( pthread_atfork(NULL, NULL, after_fork_callback) ){
+ rb_warn("FFI: unable to register fork callback");
+ }
+#else
+ InitializeCriticalSection(&ctx->async_cb_lock);
+ ctx->async_cb_cond = CreateEvent(NULL, FALSE, FALSE, NULL);
#endif
+ ctx->thread = rb_thread_create(async_cb_event, ctx);
- async_cb_thread = rb_thread_create(async_cb_event, NULL);
- /* Name thread, for better debugging */
- rb_funcall(async_cb_thread, rb_intern("name="), 1, rb_str_new2("FFI Callback Dispatcher"));
+ /* Name thread, for better debugging */
+ rb_funcall(ctx->thread, rb_intern("name="), 1, rb_str_new2("FFI Callback Dispatcher"));
+
+ async_cb_dispatcher_set(ctx);
+ }
+ fn->dispatcher = ctx;
}
#endif
@@ -344,7 +460,7 @@ function_init(VALUE self, VALUE rbFunctionInfo, VALUE rbProc)
rb_obj_classname(rbProc));
}
- fn->rbProc = rbProc;
+ RB_OBJ_WRITE(self, &fn->rbProc, rbProc);
return self;
}
@@ -360,7 +476,7 @@ function_call(int argc, VALUE* argv, VALUE self)
{
Function* fn;
- Data_Get_Struct(self, Function, fn);
+ TypedData_Get_Struct(self, Function, &function_data_type, fn);
return (*fn->info->invoke)(argc, argv, fn->base.memory.address, fn->info);
}
@@ -376,9 +492,9 @@ static VALUE
function_attach(VALUE self, VALUE module, VALUE name)
{
Function* fn;
- char var[1024];
- Data_Get_Struct(self, Function, fn);
+ StringValue(name);
+ TypedData_Get_Struct(self, Function, &function_data_type, fn);
if (fn->info->parameterCount == -1) {
rb_raise(rb_eRuntimeError, "cannot attach variadic functions");
@@ -394,12 +510,6 @@ function_attach(VALUE self, VALUE module, VALUE name)
fn->methodHandle = rbffi_MethodHandle_Alloc(fn->info, fn->base.memory.address);
}
- /*
- * Stash the Function in a module variable so it does not get garbage collected
- */
- snprintf(var, sizeof(var), "@@%s", StringValueCStr(name));
- rb_cv_set(module, var, self);
-
rb_define_singleton_method(module, StringValueCStr(name),
rbffi_MethodHandle_CodeAddress(fn->methodHandle), -1);
@@ -421,7 +531,8 @@ function_set_autorelease(VALUE self, VALUE autorelease)
{
Function* fn;
- Data_Get_Struct(self, Function, fn);
+ rb_check_frozen(self);
+ TypedData_Get_Struct(self, Function, &function_data_type, fn);
fn->autorelease = RTEST(autorelease);
@@ -433,11 +544,21 @@ function_autorelease_p(VALUE self)
{
Function* fn;
- Data_Get_Struct(self, Function, fn);
+ TypedData_Get_Struct(self, Function, &function_data_type, fn);
return fn->autorelease ? Qtrue : Qfalse;
}
+static VALUE
+function_type(VALUE self)
+{
+ Function* fn;
+
+ TypedData_Get_Struct(self, Function, &function_data_type, fn);
+
+ return fn->rbFunctionInfo;
+}
+
/*
* call-seq: free
* @return [self]
@@ -448,7 +569,7 @@ function_release(VALUE self)
{
Function* fn;
- Data_Get_Struct(self, Function, fn);
+ TypedData_Get_Struct(self, Function, &function_data_type, fn);
if (fn->closure == NULL) {
rb_raise(rb_eRuntimeError, "cannot free function which was not allocated");
@@ -463,6 +584,7 @@ function_release(VALUE self)
static void
callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data)
{
+ Function* fn;
struct gvl_callback cb = { 0 };
cb.closure = (Closure *) user_data;
@@ -470,6 +592,7 @@ callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data)
cb.parameters = parameters;
cb.done = false;
cb.frame = rbffi_frame_current();
+ fn = (Function *) cb.closure->info;
if (cb.frame != NULL) cb.frame->exc = Qnil;
@@ -482,18 +605,19 @@ callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data)
#if defined(DEFER_ASYNC_CALLBACK) && !defined(_WIN32)
} else {
bool empty = false;
+ struct async_cb_dispatcher *ctx = fn->dispatcher;
pthread_mutex_init(&cb.async_mutex, NULL);
pthread_cond_init(&cb.async_cond, NULL);
- /* Now signal the async callback thread */
- pthread_mutex_lock(&async_cb_mutex);
- empty = async_cb_list == NULL;
- cb.next = async_cb_list;
- async_cb_list = &cb;
+ /* Now signal the async callback dispatcher thread */
+ pthread_mutex_lock(&ctx->async_cb_mutex);
+ empty = ctx->async_cb_list == NULL;
+ cb.next = ctx->async_cb_list;
+ ctx->async_cb_list = &cb;
- pthread_cond_signal(&async_cb_cond);
- pthread_mutex_unlock(&async_cb_mutex);
+ pthread_cond_signal(&ctx->async_cb_cond);
+ pthread_mutex_unlock(&ctx->async_cb_mutex);
/* Wait for the thread executing the ruby callback to signal it is done */
pthread_mutex_lock(&cb.async_mutex);
@@ -507,17 +631,18 @@ callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data)
#elif defined(DEFER_ASYNC_CALLBACK) && defined(_WIN32)
} else {
bool empty = false;
+ struct async_cb_dispatcher *ctx = fn->dispatcher;
cb.async_event = CreateEvent(NULL, FALSE, FALSE, NULL);
- /* Now signal the async callback thread */
- EnterCriticalSection(&async_cb_lock);
- empty = async_cb_list == NULL;
- cb.next = async_cb_list;
- async_cb_list = &cb;
- LeaveCriticalSection(&async_cb_lock);
+ /* Now signal the async callback dispatcher thread */
+ EnterCriticalSection(&ctx->async_cb_lock);
+ empty = ctx->async_cb_list == NULL;
+ cb.next = ctx->async_cb_list;
+ ctx->async_cb_list = &cb;
+ LeaveCriticalSection(&ctx->async_cb_lock);
- SetEvent(async_cb_cond);
+ SetEvent(ctx->async_cb_cond);
/* Wait for the thread executing the ruby callback to signal it is done */
WaitForSingleObject(cb.async_event, INFINITE);
@@ -528,6 +653,7 @@ callback_invoke(ffi_cif* cif, void* retval, void** parameters, void* user_data)
#if defined(DEFER_ASYNC_CALLBACK)
struct async_wait {
+ struct async_cb_dispatcher *dispatcher;
void* cb;
bool stop;
};
@@ -536,9 +662,10 @@ static void * async_cb_wait(void *);
static void async_cb_stop(void *);
static VALUE
-async_cb_event(void* unused)
+async_cb_event(void* ptr)
{
- struct async_wait w = { 0 };
+ struct async_cb_dispatcher *ctx = (struct async_cb_dispatcher *)ptr;
+ struct async_wait w = { ctx };
w.stop = false;
while (!w.stop) {
@@ -559,23 +686,24 @@ static void *
async_cb_wait(void *data)
{
struct async_wait* w = (struct async_wait *) data;
+ struct async_cb_dispatcher *ctx = w->dispatcher;
w->cb = NULL;
- EnterCriticalSection(&async_cb_lock);
+ EnterCriticalSection(&ctx->async_cb_lock);
- while (!w->stop && async_cb_list == NULL) {
- LeaveCriticalSection(&async_cb_lock);
- WaitForSingleObject(async_cb_cond, INFINITE);
- EnterCriticalSection(&async_cb_lock);
+ while (!w->stop && ctx->async_cb_list == NULL) {
+ LeaveCriticalSection(&ctx->async_cb_lock);
+ WaitForSingleObject(ctx->async_cb_cond, INFINITE);
+ EnterCriticalSection(&ctx->async_cb_lock);
}
- if (async_cb_list != NULL) {
- w->cb = async_cb_list;
- async_cb_list = async_cb_list->next;
+ if (ctx->async_cb_list != NULL) {
+ w->cb = ctx->async_cb_list;
+ ctx->async_cb_list = ctx->async_cb_list->next;
}
- LeaveCriticalSection(&async_cb_lock);
+ LeaveCriticalSection(&ctx->async_cb_lock);
return NULL;
}
@@ -584,11 +712,12 @@ static void
async_cb_stop(void *data)
{
struct async_wait* w = (struct async_wait *) data;
+ struct async_cb_dispatcher *ctx = w->dispatcher;
- EnterCriticalSection(&async_cb_lock);
+ EnterCriticalSection(&ctx->async_cb_lock);
w->stop = true;
- LeaveCriticalSection(&async_cb_lock);
- SetEvent(async_cb_cond);
+ LeaveCriticalSection(&ctx->async_cb_lock);
+ SetEvent(ctx->async_cb_cond);
}
#else
@@ -596,21 +725,22 @@ static void *
async_cb_wait(void *data)
{
struct async_wait* w = (struct async_wait *) data;
+ struct async_cb_dispatcher *ctx = w->dispatcher;
w->cb = NULL;
- pthread_mutex_lock(&async_cb_mutex);
+ pthread_mutex_lock(&ctx->async_cb_mutex);
- while (!w->stop && async_cb_list == NULL) {
- pthread_cond_wait(&async_cb_cond, &async_cb_mutex);
+ while (!w->stop && ctx->async_cb_list == NULL) {
+ pthread_cond_wait(&ctx->async_cb_cond, &ctx->async_cb_mutex);
}
- if (async_cb_list != NULL) {
- w->cb = async_cb_list;
- async_cb_list = async_cb_list->next;
+ if (ctx->async_cb_list != NULL) {
+ w->cb = ctx->async_cb_list;
+ ctx->async_cb_list = ctx->async_cb_list->next;
}
- pthread_mutex_unlock(&async_cb_mutex);
+ pthread_mutex_unlock(&ctx->async_cb_mutex);
return NULL;
}
@@ -619,11 +749,12 @@ static void
async_cb_stop(void *data)
{
struct async_wait* w = (struct async_wait *) data;
+ struct async_cb_dispatcher *ctx = w->dispatcher;
- pthread_mutex_lock(&async_cb_mutex);
+ pthread_mutex_lock(&ctx->async_cb_mutex);
w->stop = true;
- pthread_cond_signal(&async_cb_cond);
- pthread_mutex_unlock(&async_cb_mutex);
+ pthread_cond_signal(&ctx->async_cb_cond);
+ pthread_mutex_unlock(&ctx->async_cb_mutex);
}
#endif
@@ -796,7 +927,9 @@ invoke_callback(VALUE data)
break;
case NATIVE_POINTER:
if (TYPE(rbReturnValue) == T_DATA && rb_obj_is_kind_of(rbReturnValue, rbffi_PointerClass)) {
- *((void **) retval) = ((AbstractMemory *) DATA_PTR(rbReturnValue))->address;
+ AbstractMemory* memory;
+ TypedData_Get_Struct(rbReturnValue, AbstractMemory, &rbffi_abstract_memory_data_type, memory);
+ *((void **) retval) = memory->address;
} else {
/* Default to returning NULL if not a value pointer object. handles nil case as well */
*((void **) retval) = NULL;
@@ -809,15 +942,20 @@ invoke_callback(VALUE data)
case NATIVE_FUNCTION:
if (TYPE(rbReturnValue) == T_DATA && rb_obj_is_kind_of(rbReturnValue, rbffi_PointerClass)) {
+ AbstractMemory* memory;
+ TypedData_Get_Struct(rbReturnValue, AbstractMemory, &rbffi_abstract_memory_data_type, memory);
- *((void **) retval) = ((AbstractMemory *) DATA_PTR(rbReturnValue))->address;
+ *((void **) retval) = memory->address;
} else if (rb_obj_is_kind_of(rbReturnValue, rb_cProc) || rb_respond_to(rbReturnValue, id_call)) {
VALUE function;
function = rbffi_Function_ForProc(rbReturnType, rbReturnValue);
- *((void **) retval) = ((AbstractMemory *) DATA_PTR(function))->address;
+ AbstractMemory* memory;
+ TypedData_Get_Struct(function, AbstractMemory, &rbffi_abstract_memory_data_type, memory);
+
+ *((void **) retval) = memory->address;
} else {
*((void **) retval) = NULL;
}
@@ -825,7 +963,9 @@ invoke_callback(VALUE data)
case NATIVE_STRUCT:
if (TYPE(rbReturnValue) == T_DATA && rb_obj_is_kind_of(rbReturnValue, rbffi_StructClass)) {
- AbstractMemory* memory = ((Struct *) DATA_PTR(rbReturnValue))->pointer;
+ Struct* s;
+ TypedData_Get_Struct(rbReturnValue, Struct, &rbffi_struct_data_type, s);
+ AbstractMemory* memory = s->pointer;
if (memory->address != NULL) {
memcpy(retval, memory->address, returnType->ffiType->size);
@@ -891,6 +1031,7 @@ rbffi_Function_Init(VALUE moduleFFI)
rb_define_method(rbffi_FunctionClass, "attach", function_attach, 2);
rb_define_method(rbffi_FunctionClass, "free", function_release, 0);
rb_define_method(rbffi_FunctionClass, "autorelease=", function_set_autorelease, 1);
+ rb_define_private_method(rbffi_FunctionClass, "type", function_type, 0);
/*
* call-seq: autorelease
* @return [Boolean]
@@ -910,8 +1051,7 @@ rbffi_Function_Init(VALUE moduleFFI)
id_cb_ref = rb_intern("@__ffi_callback__");
id_to_native = rb_intern("to_native");
id_from_native = rb_intern("from_native");
-#if defined(_WIN32)
- InitializeCriticalSection(&async_cb_lock);
- async_cb_cond = CreateEvent(NULL, FALSE, FALSE, NULL);
+#if defined(DEFER_ASYNC_CALLBACK) && defined(HAVE_RB_EXT_RACTOR_SAFE)
+ async_cb_dispatcher_key = rb_ractor_local_storage_ptr_newkey(&async_cb_dispatcher_key_type);
#endif
}
diff --git a/ext/ffi_c/Function.h b/ext/ffi_c/Function.h
index 406b4d8..89b22ec 100644
--- a/ext/ffi_c/Function.h
+++ b/ext/ffi_c/Function.h
@@ -68,6 +68,7 @@ struct FunctionType_ {
bool hasStruct;
};
+extern const rb_data_type_t rbffi_fntype_data_type;
extern VALUE rbffi_FunctionTypeClass, rbffi_FunctionClass;
void rbffi_Function_Init(VALUE moduleFFI);
diff --git a/ext/ffi_c/FunctionInfo.c b/ext/ffi_c/FunctionInfo.c
index 64e9874..b5150d8 100644
--- a/ext/ffi_c/FunctionInfo.c
+++ b/ext/ffi_c/FunctionInfo.c
@@ -51,8 +51,24 @@
static VALUE fntype_allocate(VALUE klass);
static VALUE fntype_initialize(int argc, VALUE* argv, VALUE self);
-static void fntype_mark(FunctionType*);
-static void fntype_free(FunctionType *);
+static void fntype_mark(void *);
+static void fntype_compact(void *);
+static void fntype_free(void *);
+static size_t fntype_memsize(const void *);
+
+const rb_data_type_t rbffi_fntype_data_type = { /* extern */
+ .wrap_struct_name = "FFI::FunctionType",
+ .function = {
+ .dmark = fntype_mark,
+ .dfree = fntype_free,
+ .dsize = fntype_memsize,
+ ffi_compact_callback( fntype_compact )
+ },
+ .parent = &rbffi_type_data_type,
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
VALUE rbffi_FunctionTypeClass = Qnil;
@@ -60,13 +76,13 @@ static VALUE
fntype_allocate(VALUE klass)
{
FunctionType* fnInfo;
- VALUE obj = Data_Make_Struct(klass, FunctionType, fntype_mark, fntype_free, fnInfo);
+ VALUE obj = TypedData_Make_Struct(klass, FunctionType, &rbffi_fntype_data_type, fnInfo);
fnInfo->type.ffiType = &ffi_type_pointer;
fnInfo->type.nativeType = NATIVE_FUNCTION;
- fnInfo->rbReturnType = Qnil;
- fnInfo->rbParameterTypes = Qnil;
- fnInfo->rbEnums = Qnil;
+ RB_OBJ_WRITE(obj, &fnInfo->rbReturnType, Qnil);
+ RB_OBJ_WRITE(obj, &fnInfo->rbParameterTypes, Qnil);
+ RB_OBJ_WRITE(obj, &fnInfo->rbEnums, Qnil);
fnInfo->invoke = rbffi_CallFunction;
fnInfo->closurePool = NULL;
@@ -74,19 +90,37 @@ fntype_allocate(VALUE klass)
}
static void
-fntype_mark(FunctionType* fnInfo)
+fntype_mark(void *data)
{
- rb_gc_mark(fnInfo->rbReturnType);
- rb_gc_mark(fnInfo->rbParameterTypes);
- rb_gc_mark(fnInfo->rbEnums);
+ FunctionType *fnInfo = (FunctionType *)data;
+ rb_gc_mark_movable(fnInfo->rbReturnType);
+ rb_gc_mark_movable(fnInfo->rbParameterTypes);
+ rb_gc_mark_movable(fnInfo->rbEnums);
if (fnInfo->callbackCount > 0 && fnInfo->callbackParameters != NULL) {
- rb_gc_mark_locations(&fnInfo->callbackParameters[0], &fnInfo->callbackParameters[fnInfo->callbackCount]);
+ for (size_t index = 0; index < fnInfo->callbackCount; index++) {
+ rb_gc_mark_movable(fnInfo->callbackParameters[index]);
+ }
+ }
+}
+
+static void
+fntype_compact(void *data)
+{
+ FunctionType *fnInfo = (FunctionType *)data;
+ ffi_gc_location(fnInfo->rbReturnType);
+ ffi_gc_location(fnInfo->rbParameterTypes);
+ ffi_gc_location(fnInfo->rbEnums);
+ if (fnInfo->callbackCount > 0 && fnInfo->callbackParameters != NULL) {
+ for (size_t index = 0; index < fnInfo->callbackCount; index++) {
+ ffi_gc_location(fnInfo->callbackParameters[index]);
+ }
}
}
static void
-fntype_free(FunctionType* fnInfo)
+fntype_free(void *data)
{
+ FunctionType *fnInfo = (FunctionType *)data;
xfree(fnInfo->parameterTypes);
xfree(fnInfo->ffiParameterTypes);
xfree(fnInfo->nativeParameterTypes);
@@ -97,6 +131,23 @@ fntype_free(FunctionType* fnInfo)
xfree(fnInfo);
}
+static size_t
+fntype_memsize(const void *data)
+{
+ const FunctionType *fnInfo = (const FunctionType *)data;
+
+ size_t memsize = sizeof(FunctionType);
+ memsize += fnInfo->callbackCount * sizeof(VALUE);
+
+ memsize += fnInfo->parameterCount * (
+ sizeof(*fnInfo->parameterTypes)
+ + sizeof(ffi_type *)
+ + sizeof(*fnInfo->nativeParameterTypes)
+ );
+
+ return memsize;
+}
+
/*
* call-seq: initialize(return_type, param_types, options={})
* @param [Type, Symbol] return_type return type for the function
@@ -129,13 +180,13 @@ fntype_initialize(int argc, VALUE* argv, VALUE self)
Check_Type(rbParamTypes, T_ARRAY);
- Data_Get_Struct(self, FunctionType, fnInfo);
+ TypedData_Get_Struct(self, FunctionType, &rbffi_fntype_data_type, fnInfo);
fnInfo->parameterCount = (int) RARRAY_LEN(rbParamTypes);
fnInfo->parameterTypes = xcalloc(fnInfo->parameterCount, sizeof(*fnInfo->parameterTypes));
fnInfo->ffiParameterTypes = xcalloc(fnInfo->parameterCount, sizeof(ffi_type *));
fnInfo->nativeParameterTypes = xcalloc(fnInfo->parameterCount, sizeof(*fnInfo->nativeParameterTypes));
- fnInfo->rbParameterTypes = rb_ary_new2(fnInfo->parameterCount);
- fnInfo->rbEnums = rbEnums;
+ RB_OBJ_WRITE(self, &fnInfo->rbParameterTypes, rb_ary_new2(fnInfo->parameterCount));
+ RB_OBJ_WRITE(self, &fnInfo->rbEnums, rbEnums);
fnInfo->blocking = RTEST(rbBlocking);
fnInfo->hasStruct = false;
@@ -150,7 +201,8 @@ fntype_initialize(int argc, VALUE* argv, VALUE self)
if (rb_obj_is_kind_of(type, rbffi_FunctionTypeClass)) {
REALLOC_N(fnInfo->callbackParameters, VALUE, fnInfo->callbackCount + 1);
- fnInfo->callbackParameters[fnInfo->callbackCount++] = type;
+ RB_OBJ_WRITE(self, &fnInfo->callbackParameters[fnInfo->callbackCount], type);
+ fnInfo->callbackCount++;
}
if (rb_obj_is_kind_of(type, rbffi_StructByValueClass)) {
@@ -158,12 +210,12 @@ fntype_initialize(int argc, VALUE* argv, VALUE self)
}
rb_ary_push(fnInfo->rbParameterTypes, type);
- Data_Get_Struct(type, Type, fnInfo->parameterTypes[i]);
+ TypedData_Get_Struct(type, Type, &rbffi_type_data_type, fnInfo->parameterTypes[i]);
fnInfo->ffiParameterTypes[i] = fnInfo->parameterTypes[i]->ffiType;
fnInfo->nativeParameterTypes[i] = fnInfo->parameterTypes[i]->nativeType;
}
- fnInfo->rbReturnType = rbffi_Type_Lookup(rbReturnType);
+ RB_OBJ_WRITE(self, &fnInfo->rbReturnType, rbffi_Type_Lookup(rbReturnType));
if (!RTEST(fnInfo->rbReturnType)) {
VALUE typeName = rb_funcall2(rbReturnType, rb_intern("inspect"), 0, NULL);
rb_raise(rb_eTypeError, "Invalid return type (%s)", RSTRING_PTR(typeName));
@@ -173,7 +225,7 @@ fntype_initialize(int argc, VALUE* argv, VALUE self)
fnInfo->hasStruct = true;
}
- Data_Get_Struct(fnInfo->rbReturnType, Type, fnInfo->returnType);
+ TypedData_Get_Struct(fnInfo->rbReturnType, Type, &rbffi_type_data_type, fnInfo->returnType);
fnInfo->ffiReturnType = fnInfo->returnType->ffiType;
#if defined(X86_WIN32)
@@ -199,20 +251,22 @@ fntype_initialize(int argc, VALUE* argv, VALUE self)
fnInfo->invoke = rbffi_GetInvoker(fnInfo);
+ rb_obj_freeze(fnInfo->rbParameterTypes);
+ rb_obj_freeze(self);
return self;
}
/*
- * call-seq: result_type
+ * call-seq: return_type
* @return [Type]
* Get the return type of the function type
*/
static VALUE
-fntype_result_type(VALUE self)
+fntype_return_type(VALUE self)
{
FunctionType* ft;
- Data_Get_Struct(self, FunctionType, ft);
+ TypedData_Get_Struct(self, FunctionType, &rbffi_fntype_data_type, ft);
return ft->rbReturnType;
}
@@ -227,7 +281,7 @@ fntype_param_types(VALUE self)
{
FunctionType* ft;
- Data_Get_Struct(self, FunctionType, ft);
+ TypedData_Get_Struct(self, FunctionType, &rbffi_fntype_data_type, ft);
return rb_ary_dup(ft->rbParameterTypes);
}
@@ -259,7 +313,7 @@ rbffi_FunctionInfo_Init(VALUE moduleFFI)
rb_define_alloc_func(rbffi_FunctionTypeClass, fntype_allocate);
rb_define_method(rbffi_FunctionTypeClass, "initialize", fntype_initialize, -1);
- rb_define_method(rbffi_FunctionTypeClass, "result_type", fntype_result_type, 0);
+ rb_define_method(rbffi_FunctionTypeClass, "return_type", fntype_return_type, 0);
rb_define_method(rbffi_FunctionTypeClass, "param_types", fntype_param_types, 0);
}
diff --git a/ext/ffi_c/LastError.c b/ext/ffi_c/LastError.c
index 6beecef..f4da301 100644
--- a/ext/ffi_c/LastError.c
+++ b/ext/ffi_c/LastError.c
@@ -91,30 +91,49 @@ thread_data_free(void *ptr)
}
#else
+static size_t
+thread_data_memsize(const void *data) {
+ return sizeof(ThreadData);
+}
+
+static const rb_data_type_t thread_data_data_type = {
+ .wrap_struct_name = "FFI::ThreadData",
+ .function = {
+ .dmark = NULL,
+ .dfree = RUBY_TYPED_DEFAULT_FREE,
+ .dsize = thread_data_memsize,
+ },
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
+};
+
static ID id_thread_data;
static ThreadData*
thread_data_init(void)
{
- ThreadData* td;
+ ThreadData *td;
VALUE obj;
- obj = Data_Make_Struct(rb_cObject, ThreadData, NULL, -1, td);
+ obj = TypedData_Make_Struct(rb_cObject, ThreadData, &thread_data_data_type, td);
rb_thread_local_aset(rb_thread_current(), id_thread_data, obj);
return td;
}
static inline ThreadData*
-thread_data_get()
+thread_data_get(void)
{
VALUE obj = rb_thread_local_aref(rb_thread_current(), id_thread_data);
- if (obj != Qnil && TYPE(obj) == T_DATA) {
- return (ThreadData *) DATA_PTR(obj);
+ if (NIL_P(obj)) {
+ return thread_data_init();
}
- return thread_data_init();
+ ThreadData *td;
+ TypedData_Get_Struct(obj, ThreadData, &thread_data_data_type, td);
+ return td;
}
#endif
@@ -154,7 +173,6 @@ get_last_winapi_error(VALUE self)
static VALUE
set_last_error(VALUE self, VALUE error)
{
-
#ifdef _WIN32
SetLastError(NUM2INT(error));
#else
diff --git a/ext/ffi_c/MappedType.c b/ext/ffi_c/MappedType.c
index d1a4189..2e506f2 100644
--- a/ext/ffi_c/MappedType.c
+++ b/ext/ffi_c/MappedType.c
@@ -31,6 +31,7 @@
#include <ffi.h>
#include "rbffi.h"
+#include "compat.h"
#include "Type.h"
#include "MappedType.h"
@@ -38,24 +39,41 @@
static VALUE mapped_allocate(VALUE);
static VALUE mapped_initialize(VALUE, VALUE);
-static void mapped_mark(MappedType *);
+static void mapped_mark(void *);
+static void mapped_compact(void *);
+static size_t mapped_memsize(const void *);
static ID id_native_type, id_to_native, id_from_native;
VALUE rbffi_MappedTypeClass = Qnil;
+static const rb_data_type_t mapped_type_data_type = {
+ .wrap_struct_name = "FFI::Type::Mapped",
+ .function = {
+ .dmark = mapped_mark,
+ .dfree = RUBY_TYPED_DEFAULT_FREE,
+ .dsize = mapped_memsize,
+ ffi_compact_callback( mapped_compact )
+ },
+ .parent = &rbffi_type_data_type,
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
+
+
static VALUE
mapped_allocate(VALUE klass)
{
MappedType* m;
- VALUE obj = Data_Make_Struct(klass, MappedType, mapped_mark, -1, m);
+ VALUE obj = TypedData_Make_Struct(klass, MappedType, &mapped_type_data_type, m);
- m->rbConverter = Qnil;
- m->rbType = Qnil;
+ RB_OBJ_WRITE(obj, &m->rbConverter, Qnil);
+ RB_OBJ_WRITE(obj, &m->rbType, Qnil);
m->type = NULL;
m->base.nativeType = NATIVE_MAPPED;
m->base.ffiType = &ffi_type_void;
-
+
return obj;
}
@@ -69,7 +87,7 @@ static VALUE
mapped_initialize(VALUE self, VALUE rbConverter)
{
MappedType* m = NULL;
-
+
if (!rb_respond_to(rbConverter, id_native_type)) {
rb_raise(rb_eNoMethodError, "native_type method not implemented");
}
@@ -81,25 +99,42 @@ mapped_initialize(VALUE self, VALUE rbConverter)
if (!rb_respond_to(rbConverter, id_from_native)) {
rb_raise(rb_eNoMethodError, "from_native method not implemented");
}
-
- Data_Get_Struct(self, MappedType, m);
- m->rbType = rb_funcall2(rbConverter, id_native_type, 0, NULL);
+
+ TypedData_Get_Struct(self, MappedType, &mapped_type_data_type, m);
+ RB_OBJ_WRITE(self, &m->rbType, rb_funcall2(rbConverter, id_native_type, 0, NULL));
if (!(rb_obj_is_kind_of(m->rbType, rbffi_TypeClass))) {
rb_raise(rb_eTypeError, "native_type did not return instance of FFI::Type");
}
- m->rbConverter = rbConverter;
- Data_Get_Struct(m->rbType, Type, m->type);
+ RB_OBJ_WRITE(self, &m->rbConverter, rbConverter);
+ TypedData_Get_Struct(m->rbType, Type, &rbffi_type_data_type, m->type);
m->base.ffiType = m->type->ffiType;
-
+
+ rb_obj_freeze(self);
+
return self;
}
static void
-mapped_mark(MappedType* m)
+mapped_mark(void* data)
+{
+ MappedType* m = (MappedType*)data;
+ rb_gc_mark_movable(m->rbType);
+ rb_gc_mark_movable(m->rbConverter);
+}
+
+static void
+mapped_compact(void* data)
{
- rb_gc_mark(m->rbType);
- rb_gc_mark(m->rbConverter);
+ MappedType* m = (MappedType*)data;
+ ffi_gc_location(m->rbType);
+ ffi_gc_location(m->rbConverter);
+}
+
+static size_t
+mapped_memsize(const void *data)
+{
+ return sizeof(MappedType);
}
/*
@@ -111,7 +146,7 @@ static VALUE
mapped_native_type(VALUE self)
{
MappedType*m = NULL;
- Data_Get_Struct(self, MappedType, m);
+ TypedData_Get_Struct(self, MappedType, &mapped_type_data_type, m);
return m->rbType;
}
@@ -124,9 +159,8 @@ static VALUE
mapped_to_native(int argc, VALUE* argv, VALUE self)
{
MappedType*m = NULL;
-
- Data_Get_Struct(self, MappedType, m);
-
+ TypedData_Get_Struct(self, MappedType, &mapped_type_data_type, m);
+
return rb_funcall2(m->rbConverter, id_to_native, argc, argv);
}
@@ -138,20 +172,28 @@ static VALUE
mapped_from_native(int argc, VALUE* argv, VALUE self)
{
MappedType*m = NULL;
-
- Data_Get_Struct(self, MappedType, m);
+ TypedData_Get_Struct(self, MappedType, &mapped_type_data_type, m);
return rb_funcall2(m->rbConverter, id_from_native, argc, argv);
}
+static VALUE
+mapped_converter(VALUE self)
+{
+ MappedType*m = NULL;
+ TypedData_Get_Struct(self, MappedType, &mapped_type_data_type, m);
+
+ return m->rbConverter;
+}
+
void
rbffi_MappedType_Init(VALUE moduleFFI)
{
- /*
+ /*
* Document-class: FFI::Type::Mapped < FFI::Type
*/
rbffi_MappedTypeClass = rb_define_class_under(rbffi_TypeClass, "Mapped", rbffi_TypeClass);
-
+
rb_global_variable(&rbffi_MappedTypeClass);
id_native_type = rb_intern("native_type");
@@ -164,5 +206,6 @@ rbffi_MappedType_Init(VALUE moduleFFI)
rb_define_method(rbffi_MappedTypeClass, "native_type", mapped_native_type, 0);
rb_define_method(rbffi_MappedTypeClass, "to_native", mapped_to_native, -1);
rb_define_method(rbffi_MappedTypeClass, "from_native", mapped_from_native, -1);
+ rb_define_method(rbffi_MappedTypeClass, "converter", mapped_converter, 0);
}
diff --git a/ext/ffi_c/MappedType.h b/ext/ffi_c/MappedType.h
index 4b26cc1..9f6f9ee 100644
--- a/ext/ffi_c/MappedType.h
+++ b/ext/ffi_c/MappedType.h
@@ -43,14 +43,12 @@ typedef struct MappedType_ {
Type* type;
VALUE rbConverter;
VALUE rbType;
-
} MappedType;
void rbffi_MappedType_Init(VALUE moduleFFI);
extern VALUE rbffi_MappedTypeClass;
-
#ifdef __cplusplus
}
#endif
diff --git a/ext/ffi_c/MemoryPointer.c b/ext/ffi_c/MemoryPointer.c
index 1a64f2e..a60168e 100644
--- a/ext/ffi_c/MemoryPointer.c
+++ b/ext/ffi_c/MemoryPointer.c
@@ -39,13 +39,14 @@
static VALUE memptr_allocate(VALUE klass);
-static void memptr_release(Pointer* ptr);
+static void memptr_release(void *data);
+static size_t memptr_memsize(const void *data);
static VALUE memptr_malloc(VALUE self, long size, long count, bool clear);
static VALUE memptr_free(VALUE self);
VALUE rbffi_MemoryPointerClass;
-#define MEMPTR(obj) ((MemoryPointer *) rbffi_AbstractMemory_Cast(obj, rbffi_MemoryPointerClass))
+#define MEMPTR(obj) ((MemoryPointer *) rbffi_AbstractMemory_Cast(obj, &memory_pointer_data_type))
VALUE
rbffi_MemoryPointer_NewInstance(long size, long count, bool clear)
@@ -53,12 +54,25 @@ rbffi_MemoryPointer_NewInstance(long size, long count, bool clear)
return memptr_malloc(memptr_allocate(rbffi_MemoryPointerClass), size, count, clear);
}
+static const rb_data_type_t memory_pointer_data_type = {
+ .wrap_struct_name = "FFI::MemoryPointer",
+ .function = {
+ .dmark = NULL,
+ .dfree = memptr_release,
+ .dsize = memptr_memsize,
+ },
+ .parent = &rbffi_pointer_data_type,
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
+
static VALUE
memptr_allocate(VALUE klass)
{
Pointer* p;
- VALUE obj = Data_Make_Struct(klass, Pointer, NULL, memptr_release, p);
- p->rbParent = Qnil;
+ VALUE obj = TypedData_Make_Struct(klass, Pointer, &memory_pointer_data_type, p);
+ RB_OBJ_WRITE(obj, &p->rbParent, Qnil);
p->memory.flags = MEM_RD | MEM_WR;
return obj;
@@ -94,7 +108,7 @@ memptr_malloc(VALUE self, long size, long count, bool clear)
Pointer* p;
unsigned long msize;
- Data_Get_Struct(self, Pointer, p);
+ TypedData_Get_Struct(self, Pointer, &memory_pointer_data_type, p);
msize = size * count;
@@ -122,7 +136,8 @@ memptr_free(VALUE self)
{
Pointer* ptr;
- Data_Get_Struct(self, Pointer, ptr);
+ rb_check_frozen(self);
+ TypedData_Get_Struct(self, Pointer, &memory_pointer_data_type, ptr);
if (ptr->allocated) {
if (ptr->storage != NULL) {
@@ -136,8 +151,9 @@ memptr_free(VALUE self)
}
static void
-memptr_release(Pointer* ptr)
+memptr_release(void *data)
{
+ Pointer *ptr = (Pointer *)data;
if (ptr->autorelease && ptr->allocated && ptr->storage != NULL) {
xfree(ptr->storage);
ptr->storage = NULL;
@@ -145,6 +161,17 @@ memptr_release(Pointer* ptr)
xfree(ptr);
}
+static size_t
+memptr_memsize(const void *data)
+{
+ const Pointer *ptr = (const Pointer *)data;
+ size_t memsize = sizeof(Pointer);
+ if (ptr->allocated) {
+ memsize += ptr->memory.size;
+ }
+ return memsize;
+}
+
/*
* call-seq: from_string(s)
* @param [String] s string
diff --git a/ext/ffi_c/MethodHandle.c b/ext/ffi_c/MethodHandle.c
index d047e10..83f7b77 100644
--- a/ext/ffi_c/MethodHandle.c
+++ b/ext/ffi_c/MethodHandle.c
@@ -77,7 +77,9 @@
static bool prep_trampoline(void* ctx, void* code, Closure* closure, char* errmsg, size_t errmsgsize);
static long trampoline_size(void);
-#if defined(__x86_64__) && (defined(__linux__) || defined(__APPLE__))
+#if defined(__x86_64__) && \
+ (defined(__linux__) || defined(__APPLE__)) && \
+ !USE_FFI_ALLOC
# define CUSTOM_TRAMPOLINE 1
#endif
diff --git a/ext/ffi_c/Pointer.c b/ext/ffi_c/Pointer.c
index 153fff1..dae853a 100644
--- a/ext/ffi_c/Pointer.c
+++ b/ext/ffi_c/Pointer.c
@@ -33,16 +33,33 @@
#include <ruby.h>
#include "rbffi.h"
#include "rbffi_endian.h"
+#include "compat.h"
#include "AbstractMemory.h"
#include "Pointer.h"
-#define POINTER(obj) rbffi_AbstractMemory_Cast((obj), rbffi_PointerClass)
+#define POINTER(obj) rbffi_AbstractMemory_Cast((obj), &rbffi_pointer_data_type)
VALUE rbffi_PointerClass = Qnil;
VALUE rbffi_NullPointerSingleton = Qnil;
-static void ptr_release(Pointer* ptr);
-static void ptr_mark(Pointer* ptr);
+static void ptr_release(void *data);
+static void ptr_mark(void *data);
+static void ptr_compact(void *data);
+static size_t ptr_memsize(const void *data);
+
+const rb_data_type_t rbffi_pointer_data_type = { /* extern */
+ .wrap_struct_name = "FFI::Pointer",
+ .function = {
+ .dmark = ptr_mark,
+ .dfree = ptr_release,
+ .dsize = ptr_memsize,
+ ffi_compact_callback( ptr_compact )
+ },
+ .parent = &rbffi_abstract_memory_data_type,
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
VALUE
rbffi_Pointer_NewInstance(void* addr)
@@ -54,12 +71,12 @@ rbffi_Pointer_NewInstance(void* addr)
return rbffi_NullPointerSingleton;
}
- obj = Data_Make_Struct(rbffi_PointerClass, Pointer, NULL, -1, p);
+ obj = TypedData_Make_Struct(rbffi_PointerClass, Pointer, &rbffi_pointer_data_type, p);
p->memory.address = addr;
p->memory.size = LONG_MAX;
p->memory.flags = (addr == NULL) ? 0 : (MEM_RD | MEM_WR);
p->memory.typeSize = 1;
- p->rbParent = Qnil;
+ RB_OBJ_WRITE(obj, &p->rbParent, Qnil);
return obj;
}
@@ -70,8 +87,8 @@ ptr_allocate(VALUE klass)
Pointer* p;
VALUE obj;
- obj = Data_Make_Struct(klass, Pointer, ptr_mark, ptr_release, p);
- p->rbParent = Qnil;
+ obj = TypedData_Make_Struct(klass, Pointer, &rbffi_pointer_data_type, p);
+ RB_OBJ_WRITE(obj, &p->rbParent, Qnil);
p->memory.flags = MEM_RD | MEM_WR;
return obj;
@@ -95,7 +112,7 @@ ptr_initialize(int argc, VALUE* argv, VALUE self)
VALUE rbType = Qnil, rbAddress = Qnil;
int typeSize = 1;
- Data_Get_Struct(self, Pointer, p);
+ TypedData_Get_Struct(self, Pointer, &rbffi_pointer_data_type, p);
switch (rb_scan_args(argc, argv, "11", &rbType, &rbAddress)) {
case 1:
@@ -112,7 +129,7 @@ ptr_initialize(int argc, VALUE* argv, VALUE self)
switch (TYPE(rbAddress)) {
case T_FIXNUM:
case T_BIGNUM:
- p->memory.address = (void*) (uintptr_t) NUM2LL(rbAddress);
+ p->memory.address = (void*) (uintptr_t) NUM2ULL(rbAddress);
p->memory.size = LONG_MAX;
if (p->memory.address == NULL) {
p->memory.flags = 0;
@@ -123,8 +140,8 @@ ptr_initialize(int argc, VALUE* argv, VALUE self)
if (rb_obj_is_kind_of(rbAddress, rbffi_PointerClass)) {
Pointer* orig;
- p->rbParent = rbAddress;
- Data_Get_Struct(rbAddress, Pointer, orig);
+ RB_OBJ_WRITE(self, &p->rbParent, rbAddress);
+ TypedData_Get_Struct(rbAddress, Pointer, &rbffi_pointer_data_type, orig);
p->memory = orig->memory;
} else {
rb_raise(rb_eTypeError, "wrong argument type, expected Integer or FFI::Pointer");
@@ -153,7 +170,7 @@ ptr_initialize_copy(VALUE self, VALUE other)
AbstractMemory* src;
Pointer* dst;
- Data_Get_Struct(self, Pointer, dst);
+ TypedData_Get_Struct(self, Pointer, &rbffi_pointer_data_type, dst);
src = POINTER(other);
if (src->size == LONG_MAX) {
rb_raise(rb_eRuntimeError, "cannot duplicate unbounded memory area");
@@ -195,16 +212,16 @@ slice(VALUE self, long offset, long size)
Pointer* p;
VALUE retval;
- Data_Get_Struct(self, AbstractMemory, ptr);
+ TypedData_Get_Struct(self, AbstractMemory, &rbffi_abstract_memory_data_type, ptr);
checkBounds(ptr, offset, size == LONG_MAX ? 1 : size);
- retval = Data_Make_Struct(rbffi_PointerClass, Pointer, ptr_mark, -1, p);
+ retval = TypedData_Make_Struct(rbffi_PointerClass, Pointer, &rbffi_pointer_data_type, p);
p->memory.address = ptr->address + offset;
p->memory.size = size;
p->memory.flags = ptr->flags;
p->memory.typeSize = ptr->typeSize;
- p->rbParent = self;
+ RB_OBJ_WRITE(retval, &p->rbParent, self);
return retval;
}
@@ -222,7 +239,7 @@ ptr_plus(VALUE self, VALUE offset)
AbstractMemory* ptr;
long off = NUM2LONG(offset);
- Data_Get_Struct(self, AbstractMemory, ptr);
+ TypedData_Get_Struct(self, AbstractMemory, &rbffi_abstract_memory_data_type, ptr);
return slice(self, off, ptr->size == LONG_MAX ? LONG_MAX : ptr->size - off);
}
@@ -252,7 +269,7 @@ ptr_inspect(VALUE self)
char buf[100];
Pointer* ptr;
- Data_Get_Struct(self, Pointer, ptr);
+ TypedData_Get_Struct(self, Pointer, &rbffi_pointer_data_type, ptr);
if (ptr->memory.size != LONG_MAX) {
snprintf(buf, sizeof(buf), "#<%s address=%p size=%lu>",
@@ -275,7 +292,7 @@ ptr_null_p(VALUE self)
{
Pointer* ptr;
- Data_Get_Struct(self, Pointer, ptr);
+ TypedData_Get_Struct(self, Pointer, &rbffi_pointer_data_type, ptr);
return ptr->memory.address == NULL ? Qtrue : Qfalse;
}
@@ -291,7 +308,7 @@ ptr_equals(VALUE self, VALUE other)
{
Pointer* ptr;
- Data_Get_Struct(self, Pointer, ptr);
+ TypedData_Get_Struct(self, Pointer, &rbffi_pointer_data_type, ptr);
if (NIL_P(other)) {
return ptr->memory.address == NULL ? Qtrue : Qfalse;
@@ -310,7 +327,7 @@ ptr_address(VALUE self)
{
Pointer* ptr;
- Data_Get_Struct(self, Pointer, ptr);
+ TypedData_Get_Struct(self, Pointer, &rbffi_pointer_data_type, ptr);
return ULL2NUM((uintptr_t) ptr->memory.address);
}
@@ -335,7 +352,7 @@ ptr_order(int argc, VALUE* argv, VALUE self)
{
Pointer* ptr;
- Data_Get_Struct(self, Pointer, ptr);
+ TypedData_Get_Struct(self, Pointer, &rbffi_pointer_data_type, ptr);
if (argc == 0) {
int order = (ptr->memory.flags & MEM_SWAP) == 0 ? BYTE_ORDER : SWAPPED_ORDER;
return order == BIG_ENDIAN ? ID2SYM(rb_intern("big")) : ID2SYM(rb_intern("little"));
@@ -361,7 +378,7 @@ ptr_order(int argc, VALUE* argv, VALUE self)
Pointer* p2;
VALUE retval = slice(self, 0, ptr->memory.size);
- Data_Get_Struct(retval, Pointer, p2);
+ TypedData_Get_Struct(retval, Pointer, &rbffi_pointer_data_type, p2);
p2->memory.flags |= MEM_SWAP;
return retval;
}
@@ -381,7 +398,8 @@ ptr_free(VALUE self)
{
Pointer* ptr;
- Data_Get_Struct(self, Pointer, ptr);
+ rb_check_frozen(self);
+ TypedData_Get_Struct(self, Pointer, &rbffi_pointer_data_type, ptr);
if (ptr->allocated) {
if (ptr->storage != NULL) {
@@ -404,7 +422,7 @@ ptr_type_size(VALUE self)
{
Pointer* ptr;
- Data_Get_Struct(self, Pointer, ptr);
+ TypedData_Get_Struct(self, Pointer, &rbffi_pointer_data_type, ptr);
return INT2NUM(ptr->memory.typeSize);
}
@@ -420,7 +438,8 @@ ptr_autorelease(VALUE self, VALUE autorelease)
{
Pointer* ptr;
- Data_Get_Struct(self, Pointer, ptr);
+ rb_check_frozen(self);
+ TypedData_Get_Struct(self, Pointer, &rbffi_pointer_data_type, ptr);
ptr->autorelease = autorelease == Qtrue;
return autorelease;
@@ -436,15 +455,16 @@ ptr_autorelease_p(VALUE self)
{
Pointer* ptr;
- Data_Get_Struct(self, Pointer, ptr);
+ TypedData_Get_Struct(self, Pointer, &rbffi_pointer_data_type, ptr);
return ptr->autorelease ? Qtrue : Qfalse;
}
static void
-ptr_release(Pointer* ptr)
+ptr_release(void *data)
{
+ Pointer *ptr = (Pointer *)data;
if (ptr->autorelease && ptr->allocated && ptr->storage != NULL) {
xfree(ptr->storage);
ptr->storage = NULL;
@@ -453,9 +473,28 @@ ptr_release(Pointer* ptr)
}
static void
-ptr_mark(Pointer* ptr)
+ptr_mark(void *data)
{
- rb_gc_mark(ptr->rbParent);
+ Pointer *ptr = (Pointer *)data;
+ rb_gc_mark_movable(ptr->rbParent);
+}
+
+static void
+ptr_compact(void *data)
+{
+ Pointer *ptr = (Pointer *)data;
+ ffi_gc_location(ptr->rbParent);
+}
+
+static size_t
+ptr_memsize(const void *data)
+{
+ const Pointer *ptr = (const Pointer *)data;
+ size_t memsize = sizeof(Pointer);
+ if (ptr->allocated) {
+ memsize += ptr->memory.size;
+ }
+ return memsize;
}
void
diff --git a/ext/ffi_c/Pointer.h b/ext/ffi_c/Pointer.h
index b3d6c85..0dfd8b2 100644
--- a/ext/ffi_c/Pointer.h
+++ b/ext/ffi_c/Pointer.h
@@ -40,6 +40,7 @@ extern "C" {
extern void rbffi_Pointer_Init(VALUE moduleFFI);
extern VALUE rbffi_Pointer_NewInstance(void* addr);
+extern const rb_data_type_t rbffi_pointer_data_type;
extern VALUE rbffi_PointerClass;
extern VALUE rbffi_NullPointerSingleton;
diff --git a/ext/ffi_c/Struct.c b/ext/ffi_c/Struct.c
index 92731c8..0fdba71 100644
--- a/ext/ffi_c/Struct.c
+++ b/ext/ffi_c/Struct.c
@@ -61,13 +61,29 @@ typedef struct InlineArray_ {
} InlineArray;
-static void struct_mark(Struct *);
-static void struct_free(Struct *);
+static void struct_mark(void *data);
+static void struct_compact(void *data);
+static void struct_free(void *data);
+static size_t struct_memsize(const void *);
static VALUE struct_class_layout(VALUE klass);
-static void struct_malloc(Struct* s);
-static void inline_array_mark(InlineArray *);
-static void store_reference_value(StructField* f, Struct* s, VALUE value);
-
+static void struct_malloc(VALUE self, Struct* s);
+static void inline_array_mark(void *);
+static void inline_array_compact(void *);
+static size_t inline_array_memsize(const void *);
+static void store_reference_value(VALUE self, StructField* f, Struct* s, VALUE value);
+
+const rb_data_type_t rbffi_struct_data_type = { /* extern */
+ .wrap_struct_name = "FFI::Struct",
+ .function = {
+ .dmark = struct_mark,
+ .dfree = struct_free,
+ .dsize = struct_memsize,
+ ffi_compact_callback( struct_compact )
+ },
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
VALUE rbffi_StructClass = Qnil;
VALUE rbffi_StructInlineArrayClass = Qnil;
@@ -79,17 +95,19 @@ static ID id_get = 0, id_put = 0, id_to_ptr = 0, id_to_s = 0, id_layout = 0;
static inline char*
memory_address(VALUE self)
{
- return ((AbstractMemory *)DATA_PTR((self)))->address;
+ AbstractMemory *mem;
+ TypedData_Get_Struct(self, AbstractMemory, &rbffi_abstract_memory_data_type, mem);
+ return mem->address;
}
static VALUE
struct_allocate(VALUE klass)
{
Struct* s;
- VALUE obj = Data_Make_Struct(klass, Struct, struct_mark, struct_free, s);
+ VALUE obj = TypedData_Make_Struct(klass, Struct, &rbffi_struct_data_type, s);
- s->rbPointer = Qnil;
- s->rbLayout = Qnil;
+ RB_OBJ_WRITE(obj, &s->rbPointer, Qnil);
+ RB_OBJ_WRITE(obj, &s->rbLayout, Qnil);
return obj;
}
@@ -108,28 +126,29 @@ struct_initialize(int argc, VALUE* argv, VALUE self)
VALUE rbPointer = Qnil, rest = Qnil, klass = CLASS_OF(self);
int nargs;
- Data_Get_Struct(self, Struct, s);
+ TypedData_Get_Struct(self, Struct, &rbffi_struct_data_type, s);
nargs = rb_scan_args(argc, argv, "01*", &rbPointer, &rest);
/* Call up into ruby code to adjust the layout */
if (nargs > 1) {
- s->rbLayout = rb_funcall2(CLASS_OF(self), id_layout, (int) RARRAY_LEN(rest), RARRAY_PTR(rest));
+ VALUE rbLayout = rb_apply(CLASS_OF(self), id_layout, rest);
+ RB_OBJ_WRITE(self, &s->rbLayout, rbLayout);
} else {
- s->rbLayout = struct_class_layout(klass);
+ RB_OBJ_WRITE(self, &s->rbLayout, struct_class_layout(klass));
}
if (!rb_obj_is_kind_of(s->rbLayout, rbffi_StructLayoutClass)) {
rb_raise(rb_eRuntimeError, "Invalid Struct layout");
}
- Data_Get_Struct(s->rbLayout, StructLayout, s->layout);
+ TypedData_Get_Struct(s->rbLayout, StructLayout, &rbffi_struct_layout_data_type, s->layout);
if (rbPointer != Qnil) {
s->pointer = MEMORY(rbPointer);
- s->rbPointer = rbPointer;
+ RB_OBJ_WRITE(self, &s->rbPointer, rbPointer);
} else {
- struct_malloc(s);
+ struct_malloc(self, s);
}
return self;
@@ -146,13 +165,13 @@ struct_initialize_copy(VALUE self, VALUE other)
Struct* src;
Struct* dst;
- Data_Get_Struct(self, Struct, dst);
- Data_Get_Struct(other, Struct, src);
+ TypedData_Get_Struct(self, Struct, &rbffi_struct_data_type, dst);
+ TypedData_Get_Struct(other, Struct, &rbffi_struct_data_type, src);
if (dst == src) {
return self;
}
- dst->rbLayout = src->rbLayout;
+ RB_OBJ_WRITE(self, &dst->rbLayout, src->rbLayout);
dst->layout = src->layout;
/*
@@ -161,17 +180,20 @@ struct_initialize_copy(VALUE self, VALUE other)
* be longer than just this struct.
*/
if (src->pointer->address != NULL) {
- dst->rbPointer = rbffi_MemoryPointer_NewInstance(1, src->layout->size, false);
+ RB_OBJ_WRITE(self, &dst->rbPointer, rbffi_MemoryPointer_NewInstance(1, src->layout->size, false));
dst->pointer = MEMORY(dst->rbPointer);
memcpy(dst->pointer->address, src->pointer->address, src->layout->size);
} else {
- dst->rbPointer = src->rbPointer;
+ RB_OBJ_WRITE(self, &dst->rbPointer, src->rbPointer);
dst->pointer = src->pointer;
}
if (src->layout->referenceFieldCount > 0) {
dst->rbReferences = ALLOC_N(VALUE, dst->layout->referenceFieldCount);
memcpy(dst->rbReferences, src->rbReferences, dst->layout->referenceFieldCount * sizeof(VALUE));
+ for (size_t index = 0; index < dst->layout->referenceFieldCount; index++) {
+ RB_OBJ_WRITTEN(self, Qundef, &dst->rbReferences[index]);
+ }
}
return self;
@@ -196,14 +218,15 @@ struct_class_layout(VALUE klass)
static StructLayout*
struct_layout(VALUE self)
{
- Struct* s = (Struct *) DATA_PTR(self);
+ Struct* s;
+ TypedData_Get_Struct(self, Struct, &rbffi_struct_data_type, s);
if (s->layout != NULL) {
return s->layout;
}
if (s->layout == NULL) {
- s->rbLayout = struct_class_layout(CLASS_OF(self));
- Data_Get_Struct(s->rbLayout, StructLayout, s->layout);
+ RB_OBJ_WRITE(self, &s->rbLayout, struct_class_layout(CLASS_OF(self)));
+ TypedData_Get_Struct(s->rbLayout, StructLayout, &rbffi_struct_layout_data_type, s->layout);
}
return s->layout;
@@ -213,52 +236,74 @@ static Struct*
struct_validate(VALUE self)
{
Struct* s;
- Data_Get_Struct(self, Struct, s);
+ TypedData_Get_Struct(self, Struct, &rbffi_struct_data_type, s);
if (struct_layout(self) == NULL) {
rb_raise(rb_eRuntimeError, "struct layout == null");
}
if (s->pointer == NULL) {
- struct_malloc(s);
+ struct_malloc(self, s);
}
return s;
}
static void
-struct_malloc(Struct* s)
+struct_malloc(VALUE self, Struct* s)
{
if (s->rbPointer == Qnil) {
- s->rbPointer = rbffi_MemoryPointer_NewInstance(s->layout->size, 1, true);
-
+ RB_OBJ_WRITE(self, &s->rbPointer, rbffi_MemoryPointer_NewInstance(s->layout->size, 1, true));
} else if (!rb_obj_is_kind_of(s->rbPointer, rbffi_AbstractMemoryClass)) {
rb_raise(rb_eRuntimeError, "invalid pointer in struct");
}
- s->pointer = (AbstractMemory *) DATA_PTR(s->rbPointer);
+ TypedData_Get_Struct(s->rbPointer, AbstractMemory, &rbffi_abstract_memory_data_type, s->pointer);
+}
+
+static void
+struct_mark(void *data)
+{
+ Struct *s = (Struct *)data;
+ rb_gc_mark_movable(s->rbPointer);
+ rb_gc_mark_movable(s->rbLayout);
+ if (s->rbReferences != NULL) {
+ for (size_t index = 0; index < s->layout->referenceFieldCount; index++) {
+ rb_gc_mark_movable(s->rbReferences[index]);
+ }
+ }
}
static void
-struct_mark(Struct *s)
+struct_compact(void *data)
{
- rb_gc_mark(s->rbPointer);
- rb_gc_mark(s->rbLayout);
+ Struct *s = (Struct *)data;
+ ffi_gc_location(s->rbPointer);
+ ffi_gc_location(s->rbLayout);
if (s->rbReferences != NULL) {
- rb_gc_mark_locations(&s->rbReferences[0], &s->rbReferences[s->layout->referenceFieldCount]);
+ for (size_t index = 0; index < s->layout->referenceFieldCount; index++) {
+ ffi_gc_location(s->rbReferences[index]);
+ }
}
}
static void
-struct_free(Struct* s)
+struct_free(void *data)
{
+ Struct *s = (Struct *)data;
xfree(s->rbReferences);
xfree(s);
}
+static size_t
+struct_memsize(const void *data)
+{
+ const Struct *s = (const Struct *)data;
+ return sizeof(Struct) + (s->layout->referenceFieldCount * sizeof(VALUE));
+}
static void
-store_reference_value(StructField* f, Struct* s, VALUE value)
+store_reference_value(VALUE self, StructField* f, Struct* s, VALUE value)
{
if (unlikely(f->referenceIndex == -1)) {
rb_raise(rb_eRuntimeError, "put_reference_value called for non-reference type");
@@ -268,11 +313,11 @@ store_reference_value(StructField* f, Struct* s, VALUE value)
int i;
s->rbReferences = ALLOC_N(VALUE, s->layout->referenceFieldCount);
for (i = 0; i < s->layout->referenceFieldCount; ++i) {
- s->rbReferences[i] = Qnil;
+ RB_OBJ_WRITE(self, &s->rbReferences[i], Qnil);
}
}
- s->rbReferences[f->referenceIndex] = value;
+ RB_OBJ_WRITE(self, &s->rbReferences[f->referenceIndex], value);
}
@@ -290,8 +335,8 @@ struct_field(Struct* s, VALUE fieldName)
rb_raise(rb_eArgError, "No such field '%s'", StringValueCStr(str));
}
/* Write the retrieved coder to the cache */
- p_ce->fieldName = fieldName;
- p_ce->field = (StructField *) DATA_PTR(rbField);
+ RB_OBJ_WRITE(s->rbLayout, &p_ce->fieldName, fieldName);
+ TypedData_Get_Struct(rbField, StructField, &rbffi_struct_field_data_type, p_ce->field);
}
return p_ce->field;
@@ -311,10 +356,7 @@ struct_aref(VALUE self, VALUE fieldName)
s = struct_validate(self);
f = struct_field(s, fieldName);
- if (f->get != NULL) {
- return (*f->get)(f, s);
-
- } else if (f->memoryOp != NULL) {
+ if (f->memoryOp != NULL) {
return (*f->memoryOp->get)(s->pointer, f->offset);
} else {
@@ -337,13 +379,11 @@ struct_aset(VALUE self, VALUE fieldName, VALUE value)
Struct* s;
StructField* f;
+ rb_check_frozen(self);
s = struct_validate(self);
f = struct_field(s, fieldName);
- if (f->put != NULL) {
- (*f->put)(f, s, value);
-
- } else if (f->memoryOp != NULL) {
+ if (f->memoryOp != NULL) {
(*f->memoryOp->put)(s->pointer, f->offset, value);
@@ -357,7 +397,7 @@ struct_aset(VALUE self, VALUE fieldName, VALUE value)
}
if (f->referenceRequired) {
- store_reference_value(f, s, value);
+ store_reference_value(self, f, s, value);
}
return value;
@@ -376,6 +416,7 @@ struct_set_pointer(VALUE self, VALUE pointer)
StructLayout* layout;
AbstractMemory* memory;
+ rb_check_frozen(self);
if (!rb_obj_is_kind_of(pointer, rbffi_AbstractMemoryClass)) {
rb_raise(rb_eTypeError, "wrong argument type %s (expected Pointer or Buffer)",
rb_obj_classname(pointer));
@@ -383,8 +424,8 @@ struct_set_pointer(VALUE self, VALUE pointer)
}
- Data_Get_Struct(self, Struct, s);
- Data_Get_Struct(pointer, AbstractMemory, memory);
+ TypedData_Get_Struct(self, Struct, &rbffi_struct_data_type, s);
+ TypedData_Get_Struct(pointer, AbstractMemory, &rbffi_abstract_memory_data_type, memory);
layout = struct_layout(self);
if ((int) layout->base.ffiType->size > memory->size) {
@@ -393,7 +434,7 @@ struct_set_pointer(VALUE self, VALUE pointer)
}
s->pointer = MEMORY(pointer);
- s->rbPointer = pointer;
+ RB_OBJ_WRITE(self, &s->rbPointer, pointer);
rb_ivar_set(self, id_pointer_ivar, pointer);
return self;
@@ -409,7 +450,7 @@ struct_get_pointer(VALUE self)
{
Struct* s;
- Data_Get_Struct(self, Struct, s);
+ TypedData_Get_Struct(self, Struct, &rbffi_struct_data_type, s);
return s->rbPointer;
}
@@ -424,15 +465,16 @@ static VALUE
struct_set_layout(VALUE self, VALUE layout)
{
Struct* s;
- Data_Get_Struct(self, Struct, s);
+ TypedData_Get_Struct(self, Struct, &rbffi_struct_data_type, s);
+ rb_check_frozen(self);
if (!rb_obj_is_kind_of(layout, rbffi_StructLayoutClass)) {
rb_raise(rb_eTypeError, "wrong argument type %s (expected %s)",
rb_obj_classname(layout), rb_class2name(rbffi_StructLayoutClass));
return Qnil;
}
- Data_Get_Struct(layout, StructLayout, s->layout);
+ TypedData_Get_Struct(layout, StructLayout, &rbffi_struct_layout_data_type, s->layout);
rb_ivar_set(self, id_layout_ivar, layout);
return self;
@@ -448,7 +490,7 @@ struct_get_layout(VALUE self)
{
Struct* s;
- Data_Get_Struct(self, Struct, s);
+ TypedData_Get_Struct(self, Struct, &rbffi_struct_data_type, s);
return s->rbLayout;
}
@@ -463,7 +505,7 @@ struct_null_p(VALUE self)
{
Struct* s;
- Data_Get_Struct(self, Struct, s);
+ TypedData_Get_Struct(self, Struct, &rbffi_struct_data_type, s);
return s->pointer->address == NULL ? Qtrue : Qfalse;
}
@@ -476,7 +518,7 @@ struct_order(int argc, VALUE* argv, VALUE self)
{
Struct* s;
- Data_Get_Struct(self, Struct, s);
+ TypedData_Get_Struct(self, Struct, &rbffi_struct_data_type, s);
if (argc == 0) {
return rb_funcall(s->rbPointer, rb_intern("order"), 0);
@@ -489,24 +531,52 @@ struct_order(int argc, VALUE* argv, VALUE self)
}
}
+static const rb_data_type_t inline_array_data_type = {
+ .wrap_struct_name = "FFI::Struct::InlineArray",
+ .function = {
+ .dmark = inline_array_mark,
+ .dfree = RUBY_TYPED_DEFAULT_FREE,
+ .dsize = inline_array_memsize,
+ ffi_compact_callback( inline_array_compact )
+ },
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
+
static VALUE
inline_array_allocate(VALUE klass)
{
InlineArray* array;
VALUE obj;
- obj = Data_Make_Struct(klass, InlineArray, inline_array_mark, -1, array);
- array->rbField = Qnil;
- array->rbMemory = Qnil;
+ obj = TypedData_Make_Struct(klass, InlineArray, &inline_array_data_type, array);
+ RB_OBJ_WRITE(obj, &array->rbMemory, Qnil);
+ RB_OBJ_WRITE(obj, &array->rbField, Qnil);
return obj;
}
static void
-inline_array_mark(InlineArray* array)
+inline_array_mark(void *data)
+{
+ InlineArray *array = (InlineArray *)data;
+ rb_gc_mark_movable(array->rbField);
+ rb_gc_mark_movable(array->rbMemory);
+}
+
+static void
+inline_array_compact(void *data)
+{
+ InlineArray *array = (InlineArray *)data;
+ ffi_gc_location(array->rbField);
+ ffi_gc_location(array->rbMemory);
+}
+
+static size_t
+inline_array_memsize(const void *data)
{
- rb_gc_mark(array->rbField);
- rb_gc_mark(array->rbMemory);
+ return sizeof(InlineArray);
}
/*
@@ -521,14 +591,14 @@ inline_array_initialize(VALUE self, VALUE rbMemory, VALUE rbField)
{
InlineArray* array;
- Data_Get_Struct(self, InlineArray, array);
- array->rbMemory = rbMemory;
- array->rbField = rbField;
+ TypedData_Get_Struct(self, InlineArray, &inline_array_data_type, array);
+ RB_OBJ_WRITE(self, &array->rbMemory, rbMemory);
+ RB_OBJ_WRITE(self, &array->rbField, rbField);
- Data_Get_Struct(rbMemory, AbstractMemory, array->memory);
- Data_Get_Struct(rbField, StructField, array->field);
- Data_Get_Struct(array->field->rbType, ArrayType, array->arrayType);
- Data_Get_Struct(array->arrayType->rbComponentType, Type, array->componentType);
+ TypedData_Get_Struct(rbMemory, AbstractMemory, &rbffi_abstract_memory_data_type, array->memory);
+ TypedData_Get_Struct(rbField, StructField, &rbffi_struct_field_data_type, array->field);
+ TypedData_Get_Struct(array->field->rbType, ArrayType, &rbffi_array_type_data_type, array->arrayType);
+ TypedData_Get_Struct(array->arrayType->rbComponentType, Type, &rbffi_type_data_type, array->componentType);
array->op = get_memory_op(array->componentType);
if (array->op == NULL && array->componentType->nativeType == NATIVE_MAPPED) {
@@ -550,7 +620,7 @@ inline_array_size(VALUE self)
{
InlineArray* array;
- Data_Get_Struct(self, InlineArray, array);
+ TypedData_Get_Struct(self, InlineArray, &inline_array_data_type, array);
return UINT2NUM(((ArrayType *) array->field->type)->length);
}
@@ -575,7 +645,7 @@ inline_array_aref(VALUE self, VALUE rbIndex)
{
InlineArray* array;
- Data_Get_Struct(self, InlineArray, array);
+ TypedData_Get_Struct(self, InlineArray, &inline_array_data_type, array);
if (array->op != NULL) {
VALUE rbNativeValue = array->op->get(array->memory,
@@ -611,7 +681,8 @@ inline_array_aset(VALUE self, VALUE rbIndex, VALUE rbValue)
{
InlineArray* array;
- Data_Get_Struct(self, InlineArray, array);
+ rb_check_frozen(self);
+ TypedData_Get_Struct(self, InlineArray, &inline_array_data_type, array);
if (array->op != NULL) {
if (unlikely(array->componentType->nativeType == NATIVE_MAPPED)) {
@@ -633,7 +704,7 @@ inline_array_aset(VALUE self, VALUE rbIndex, VALUE rbValue)
checkWrite(array->memory);
checkBounds(array->memory, offset, array->componentType->ffiType->size);
- Data_Get_Struct(rbValue, Struct, s);
+ TypedData_Get_Struct(rbValue, Struct, &rbffi_struct_data_type, s);
checkRead(s->pointer);
checkBounds(s->pointer, 0, array->componentType->ffiType->size);
@@ -641,7 +712,7 @@ inline_array_aset(VALUE self, VALUE rbIndex, VALUE rbValue)
} else {
ArrayType* arrayType;
- Data_Get_Struct(array->field->rbType, ArrayType, arrayType);
+ TypedData_Get_Struct(array->field->rbType, ArrayType, &rbffi_array_type_data_type, arrayType);
rb_raise(rb_eArgError, "set not supported for %s", rb_obj_classname(arrayType->rbComponentType));
return Qnil;
@@ -661,7 +732,7 @@ inline_array_each(VALUE self)
int i;
- Data_Get_Struct(self, InlineArray, array);
+ TypedData_Get_Struct(self, InlineArray, &inline_array_data_type, array);
for (i = 0; i < array->length; ++i) {
rb_yield(inline_array_aref(self, INT2FIX(i)));
@@ -682,7 +753,7 @@ inline_array_to_a(VALUE self)
VALUE obj;
int i;
- Data_Get_Struct(self, InlineArray, array);
+ TypedData_Get_Struct(self, InlineArray, &inline_array_data_type, array);
obj = rb_ary_new2(array->length);
@@ -705,7 +776,7 @@ inline_array_to_s(VALUE self)
InlineArray* array;
VALUE argv[2];
- Data_Get_Struct(self, InlineArray, array);
+ TypedData_Get_Struct(self, InlineArray, &inline_array_data_type, array);
if (array->componentType->nativeType != NATIVE_INT8 && array->componentType->nativeType != NATIVE_UINT8) {
VALUE dummy = Qnil;
@@ -728,7 +799,7 @@ inline_array_to_ptr(VALUE self)
{
InlineArray* array;
- Data_Get_Struct(self, InlineArray, array);
+ TypedData_Get_Struct(self, InlineArray, &inline_array_data_type, array);
return rb_funcall(array->rbMemory, rb_intern("slice"), 2,
UINT2NUM(array->field->offset), UINT2NUM(array->arrayType->base.ffiType->size));
diff --git a/ext/ffi_c/Struct.h b/ext/ffi_c/Struct.h
index eb6edf2..b86607d 100644
--- a/ext/ffi_c/Struct.h
+++ b/ext/ffi_c/Struct.h
@@ -42,6 +42,9 @@ extern "C" {
extern void rbffi_Struct_Init(VALUE ffiModule);
extern void rbffi_StructLayout_Init(VALUE ffiModule);
+ extern const rb_data_type_t rbffi_struct_layout_data_type;
+ extern const rb_data_type_t rbffi_struct_field_data_type;
+
typedef struct StructField_ StructField;
typedef struct StructLayout_ StructLayout;
typedef struct Struct_ Struct;
@@ -56,9 +59,6 @@ extern "C" {
VALUE rbType;
VALUE rbName;
- VALUE (*get)(StructField* field, Struct* s);
- void (*put)(StructField* field, Struct* s, VALUE value);
-
MemoryOp* memoryOp;
};
@@ -75,11 +75,12 @@ extern "C" {
* This avoids full ruby hash lookups for repeated lookups.
*/
#define FIELD_CACHE_LOOKUP(this, sym) ( &(this)->cache_row[((sym) >> 8) & 0xff] )
+ #define FIELD_CACHE_ROWS 0x100
struct field_cache_entry {
VALUE fieldName;
StructField *field;
- } cache_row[0x100];
+ } cache_row[FIELD_CACHE_ROWS];
/** The number of reference tracking fields in this struct */
int referenceFieldCount;
@@ -98,6 +99,8 @@ extern "C" {
VALUE rbPointer;
};
+ extern const rb_data_type_t rbffi_struct_data_type;
+ extern const rb_data_type_t rbffi_struct_field_data_type;
extern VALUE rbffi_StructClass, rbffi_StructLayoutClass;
extern VALUE rbffi_StructLayoutFieldClass, rbffi_StructLayoutFunctionFieldClass;
extern VALUE rbffi_StructLayoutArrayFieldClass;
diff --git a/ext/ffi_c/StructByValue.c b/ext/ffi_c/StructByValue.c
index a3255f4..df03684 100644
--- a/ext/ffi_c/StructByValue.c
+++ b/ext/ffi_c/StructByValue.c
@@ -49,20 +49,36 @@
static VALUE sbv_allocate(VALUE);
static VALUE sbv_initialize(VALUE, VALUE);
-static void sbv_mark(StructByValue *);
-static void sbv_free(StructByValue *);
+static void sbv_mark(void *);
+static void sbv_compact(void *);
+static void sbv_free(void *);
+static size_t sbv_memsize(const void *);
VALUE rbffi_StructByValueClass = Qnil;
+static const rb_data_type_t sbv_type_data_type = {
+ .wrap_struct_name = "FFI::StructByValue",
+ .function = {
+ .dmark = sbv_mark,
+ .dfree = sbv_free,
+ .dsize = sbv_memsize,
+ ffi_compact_callback( sbv_compact )
+ },
+ .parent = &rbffi_type_data_type,
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
+
static VALUE
sbv_allocate(VALUE klass)
{
StructByValue* sbv;
- VALUE obj = Data_Make_Struct(klass, StructByValue, sbv_mark, sbv_free, sbv);
+ VALUE obj = TypedData_Make_Struct(klass, StructByValue, &sbv_type_data_type, sbv);
- sbv->rbStructClass = Qnil;
- sbv->rbStructLayout = Qnil;
+ RB_OBJ_WRITE(obj, &sbv->rbStructClass, Qnil);
+ RB_OBJ_WRITE(obj, &sbv->rbStructLayout, Qnil);
sbv->base.nativeType = NATIVE_STRUCT;
sbv->base.ffiType = xcalloc(1, sizeof(*sbv->base.ffiType));
@@ -85,38 +101,54 @@ sbv_initialize(VALUE self, VALUE rbStructClass)
rb_raise(rb_eTypeError, "wrong type in @layout ivar (expected FFI::StructLayout)");
}
- Data_Get_Struct(rbLayout, StructLayout, layout);
- Data_Get_Struct(self, StructByValue, sbv);
- sbv->rbStructClass = rbStructClass;
- sbv->rbStructLayout = rbLayout;
+ TypedData_Get_Struct(rbLayout, StructLayout, &rbffi_struct_layout_data_type, layout);
+ TypedData_Get_Struct(self, StructByValue, &sbv_type_data_type, sbv);
+ RB_OBJ_WRITE(self, &sbv->rbStructClass, rbStructClass);
+ RB_OBJ_WRITE(self, &sbv->rbStructLayout, rbLayout);
/* We can just use everything from the ffi_type directly */
*sbv->base.ffiType = *layout->base.ffiType;
-
+
return self;
}
static void
-sbv_mark(StructByValue *sbv)
+sbv_mark(void *data)
+{
+ StructByValue *sbv = (StructByValue *)data;
+ rb_gc_mark_movable(sbv->rbStructClass);
+ rb_gc_mark_movable(sbv->rbStructLayout);
+}
+
+static void
+sbv_compact(void *data)
{
- rb_gc_mark(sbv->rbStructClass);
- rb_gc_mark(sbv->rbStructLayout);
+ StructByValue *sbv = (StructByValue *)data;
+ ffi_gc_location(sbv->rbStructClass);
+ ffi_gc_location(sbv->rbStructLayout);
}
static void
-sbv_free(StructByValue *sbv)
+sbv_free(void *data)
{
+ StructByValue *sbv = (StructByValue *)data;
xfree(sbv->base.ffiType);
xfree(sbv);
}
+static size_t
+sbv_memsize(const void *data)
+{
+ const StructByValue *sbv = (const StructByValue *)data;
+ return sizeof(StructByValue) + sizeof(*sbv->base.ffiType);
+}
static VALUE
sbv_layout(VALUE self)
{
StructByValue* sbv;
- Data_Get_Struct(self, StructByValue, sbv);
+ TypedData_Get_Struct(self, StructByValue, &sbv_type_data_type, sbv);
return sbv->rbStructLayout;
}
@@ -125,7 +157,7 @@ sbv_struct_class(VALUE self)
{
StructByValue* sbv;
- Data_Get_Struct(self, StructByValue, sbv);
+ TypedData_Get_Struct(self, StructByValue, &sbv_type_data_type, sbv);
return sbv->rbStructClass;
}
diff --git a/ext/ffi_c/StructLayout.c b/ext/ffi_c/StructLayout.c
index d318b8c..a56d48f 100644
--- a/ext/ffi_c/StructLayout.c
+++ b/ext/ffi_c/StructLayout.c
@@ -51,9 +51,13 @@
#define FFI_ALIGN(v, a) (((((size_t) (v))-1) | ((a)-1))+1)
-static void struct_layout_mark(StructLayout *);
-static void struct_layout_free(StructLayout *);
-static void struct_field_mark(StructField* );
+static void struct_layout_mark(void *);
+static void struct_layout_compact(void *);
+static void struct_layout_free(void *);
+static size_t struct_layout_memsize(const void *);
+static void struct_field_mark(void *);
+static void struct_field_compact(void *);
+static size_t struct_field_memsize(const void *);
VALUE rbffi_StructLayoutFieldClass = Qnil;
VALUE rbffi_StructLayoutNumberFieldClass = Qnil, rbffi_StructLayoutPointerFieldClass = Qnil;
@@ -62,6 +66,33 @@ VALUE rbffi_StructLayoutFunctionFieldClass = Qnil, rbffi_StructLayoutArrayFieldC
VALUE rbffi_StructLayoutClass = Qnil;
+const rb_data_type_t rbffi_struct_layout_data_type = { /* extern */
+ .wrap_struct_name = "FFI::StructLayout",
+ .function = {
+ .dmark = struct_layout_mark,
+ .dfree = struct_layout_free,
+ .dsize = struct_layout_memsize,
+ ffi_compact_callback( struct_layout_compact )
+ },
+ .parent = &rbffi_type_data_type,
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
+
+const rb_data_type_t rbffi_struct_field_data_type = { /* extern */
+ .wrap_struct_name = "FFI::StructField",
+ .function = {
+ .dmark = struct_field_mark,
+ .dfree = RUBY_TYPED_DEFAULT_FREE,
+ .dsize = struct_field_memsize,
+ ffi_compact_callback( struct_field_compact )
+ },
+ .parent = &rbffi_type_data_type,
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
static VALUE
struct_field_allocate(VALUE klass)
@@ -69,18 +100,33 @@ struct_field_allocate(VALUE klass)
StructField* field;
VALUE obj;
- obj = Data_Make_Struct(klass, StructField, struct_field_mark, -1, field);
- field->rbType = Qnil;
- field->rbName = Qnil;
+ obj = TypedData_Make_Struct(klass, StructField, &rbffi_struct_field_data_type, field);
+ RB_OBJ_WRITE(obj, &field->rbType, Qnil);
+ RB_OBJ_WRITE(obj, &field->rbName, Qnil);
return obj;
}
static void
-struct_field_mark(StructField* f)
+struct_field_mark(void *data)
{
- rb_gc_mark(f->rbType);
- rb_gc_mark(f->rbName);
+ StructField *f = (StructField *)data;
+ rb_gc_mark_movable(f->rbType);
+ rb_gc_mark_movable(f->rbName);
+}
+
+static void
+struct_field_compact(void *data)
+{
+ StructField *f = (StructField *)data;
+ ffi_gc_location(f->rbType);
+ ffi_gc_location(f->rbName);
+}
+
+static size_t
+struct_field_memsize(const void *data)
+{
+ return sizeof(StructField);
}
/*
@@ -98,7 +144,7 @@ struct_field_initialize(int argc, VALUE* argv, VALUE self)
StructField* field;
int nargs;
- Data_Get_Struct(self, StructField, field);
+ TypedData_Get_Struct(self, StructField, &rbffi_struct_field_data_type, field);
nargs = rb_scan_args(argc, argv, "3", &rbName, &rbOffset, &rbType);
@@ -115,9 +161,9 @@ struct_field_initialize(int argc, VALUE* argv, VALUE self)
}
field->offset = NUM2UINT(rbOffset);
- field->rbName = (TYPE(rbName) == T_SYMBOL) ? rbName : rb_str_intern(rbName);
- field->rbType = rbType;
- Data_Get_Struct(field->rbType, Type, field->type);
+ RB_OBJ_WRITE(self, &field->rbName, (TYPE(rbName) == T_SYMBOL) ? rbName : rb_str_intern(rbName));
+ RB_OBJ_WRITE(self, &field->rbType, rbType);
+ TypedData_Get_Struct(field->rbType, Type, &rbffi_type_data_type, field->type);
field->memoryOp = get_memory_op(field->type);
field->referenceIndex = -1;
@@ -135,6 +181,8 @@ struct_field_initialize(int argc, VALUE* argv, VALUE self)
break;
}
+ rb_obj_freeze(self);
+
return self;
}
@@ -147,7 +195,7 @@ static VALUE
struct_field_offset(VALUE self)
{
StructField* field;
- Data_Get_Struct(self, StructField, field);
+ TypedData_Get_Struct(self, StructField, &rbffi_struct_field_data_type, field);
return UINT2NUM(field->offset);
}
@@ -160,7 +208,7 @@ static VALUE
struct_field_size(VALUE self)
{
StructField* field;
- Data_Get_Struct(self, StructField, field);
+ TypedData_Get_Struct(self, StructField, &rbffi_struct_field_data_type, field);
return UINT2NUM(field->type->ffiType->size);
}
@@ -173,7 +221,7 @@ static VALUE
struct_field_alignment(VALUE self)
{
StructField* field;
- Data_Get_Struct(self, StructField, field);
+ TypedData_Get_Struct(self, StructField, &rbffi_struct_field_data_type, field);
return UINT2NUM(field->type->ffiType->alignment);
}
@@ -186,7 +234,7 @@ static VALUE
struct_field_type(VALUE self)
{
StructField* field;
- Data_Get_Struct(self, StructField, field);
+ TypedData_Get_Struct(self, StructField, &rbffi_struct_field_data_type, field);
return field->rbType;
}
@@ -200,7 +248,7 @@ static VALUE
struct_field_name(VALUE self)
{
StructField* field;
- Data_Get_Struct(self, StructField, field);
+ TypedData_Get_Struct(self, StructField, &rbffi_struct_field_data_type, field);
return field->rbName;
}
@@ -215,7 +263,7 @@ struct_field_get(VALUE self, VALUE pointer)
{
StructField* f;
- Data_Get_Struct(self, StructField, f);
+ TypedData_Get_Struct(self, StructField, &rbffi_struct_field_data_type, f);
if (f->memoryOp == NULL) {
rb_raise(rb_eArgError, "get not supported for %s", rb_obj_classname(f->rbType));
return Qnil;
@@ -236,7 +284,7 @@ struct_field_put(VALUE self, VALUE pointer, VALUE value)
{
StructField* f;
- Data_Get_Struct(self, StructField, f);
+ TypedData_Get_Struct(self, StructField, &rbffi_struct_field_data_type, f);
if (f->memoryOp == NULL) {
rb_raise(rb_eArgError, "put not supported for %s", rb_obj_classname(f->rbType));
return self;
@@ -258,7 +306,7 @@ function_field_get(VALUE self, VALUE pointer)
{
StructField* f;
- Data_Get_Struct(self, StructField, f);
+ TypedData_Get_Struct(self, StructField, &rbffi_struct_field_data_type, f);
return rbffi_Function_NewInstance(f->rbType, (*rbffi_AbstractMemoryOps.pointer->get)(MEMORY(pointer), f->offset));
}
@@ -278,7 +326,7 @@ function_field_put(VALUE self, VALUE pointer, VALUE proc)
StructField* f;
VALUE value = Qnil;
- Data_Get_Struct(self, StructField, f);
+ TypedData_Get_Struct(self, StructField, &rbffi_struct_field_data_type, f);
if (NIL_P(proc) || rb_obj_is_kind_of(proc, rbffi_FunctionClass)) {
value = proc;
@@ -313,8 +361,8 @@ array_field_get(VALUE self, VALUE pointer)
ArrayType* array;
VALUE argv[2];
- Data_Get_Struct(self, StructField, f);
- Data_Get_Struct(f->rbType, ArrayType, array);
+ TypedData_Get_Struct(self, StructField, &rbffi_struct_field_data_type, f);
+ TypedData_Get_Struct(f->rbType, ArrayType, &rbffi_array_type_data_type, array);
argv[0] = pointer;
argv[1] = self;
@@ -336,9 +384,8 @@ array_field_put(VALUE self, VALUE pointer, VALUE value)
StructField* f;
ArrayType* array;
-
- Data_Get_Struct(self, StructField, f);
- Data_Get_Struct(f->rbType, ArrayType, array);
+ TypedData_Get_Struct(self, StructField, &rbffi_struct_field_data_type, f);
+ TypedData_Get_Struct(f->rbType, ArrayType, &rbffi_array_type_data_type, array);
if (isCharArray(array) && rb_obj_is_instance_of(value, rb_cString)) {
VALUE argv[2];
@@ -385,12 +432,8 @@ array_field_put(VALUE self, VALUE pointer, VALUE value)
VALUE entry = rb_ary_entry(value, i);
Struct* s;
- if (!rb_obj_is_kind_of(entry, rbffi_StructClass)) {
- rb_raise(rb_eTypeError, "array element not an instance of FFI::Struct");
- break;
- }
+ TypedData_Get_Struct(entry, Struct, &rbffi_struct_data_type, s);
- Data_Get_Struct(entry, Struct, s);
checkRead(s->pointer);
checkBounds(s->pointer, 0, array->componentType->ffiType->size);
@@ -416,7 +459,7 @@ struct_layout_allocate(VALUE klass)
StructLayout* layout;
VALUE obj;
- obj = Data_Make_Struct(klass, StructLayout, struct_layout_mark, struct_layout_free, layout);
+ obj = TypedData_Make_Struct(klass, StructLayout, &rbffi_struct_layout_data_type, layout);
layout->rbFieldMap = Qnil;
layout->rbFieldNames = Qnil;
layout->rbFields = Qnil;
@@ -443,7 +486,7 @@ struct_layout_initialize(VALUE self, VALUE fields, VALUE size, VALUE align)
ffi_type* ltype;
int i;
- Data_Get_Struct(self, StructLayout, layout);
+ TypedData_Get_Struct(self, StructLayout, &rbffi_struct_layout_data_type, layout);
layout->fieldCount = (int) RARRAY_LEN(fields);
layout->rbFieldMap = rb_hash_new();
layout->rbFieldNames = rb_ary_new2(layout->fieldCount);
@@ -470,7 +513,7 @@ struct_layout_initialize(VALUE self, VALUE fields, VALUE size, VALUE align)
}
rbName = rb_funcall2(rbField, rb_intern("name"), 0, NULL);
- Data_Get_Struct(rbField, StructField, field);
+ TypedData_Get_Struct(rbField, StructField, &rbffi_struct_field_data_type, field);
layout->fields[i] = field;
if (field->type == NULL || field->type->ffiType == NULL) {
@@ -497,6 +540,11 @@ struct_layout_initialize(VALUE self, VALUE fields, VALUE size, VALUE align)
rb_raise(rb_eRuntimeError, "Struct size is zero");
}
+ rb_obj_freeze(layout->rbFieldMap);
+ rb_obj_freeze(layout->rbFields);
+ rb_obj_freeze(layout->rbFieldNames);
+ rb_obj_freeze(self);
+
return self;
}
@@ -515,7 +563,7 @@ struct_layout_union_bang(VALUE self)
ffi_type *t = NULL;
int count, i;
- Data_Get_Struct(self, StructLayout, layout);
+ TypedData_Get_Struct(self, StructLayout, &rbffi_struct_layout_data_type, layout);
for (i = 0; alignment_types[i] != NULL; ++i) {
if (alignment_types[i]->alignment == layout->align) {
@@ -545,7 +593,7 @@ struct_layout_aref(VALUE self, VALUE field)
{
StructLayout* layout;
- Data_Get_Struct(self, StructLayout, layout);
+ TypedData_Get_Struct(self, StructLayout, &rbffi_struct_layout_data_type, layout);
return rb_hash_aref(layout->rbFieldMap, field);
}
@@ -560,7 +608,7 @@ struct_layout_fields(VALUE self)
{
StructLayout* layout;
- Data_Get_Struct(self, StructLayout, layout);
+ TypedData_Get_Struct(self, StructLayout, &rbffi_struct_layout_data_type, layout);
return rb_ary_dup(layout->rbFields);
}
@@ -575,7 +623,7 @@ struct_layout_members(VALUE self)
{
StructLayout* layout;
- Data_Get_Struct(self, StructLayout, layout);
+ TypedData_Get_Struct(self, StructLayout, &rbffi_struct_layout_data_type, layout);
return rb_ary_dup(layout->rbFieldNames);
}
@@ -590,26 +638,37 @@ struct_layout_to_a(VALUE self)
{
StructLayout* layout;
- Data_Get_Struct(self, StructLayout, layout);
+ TypedData_Get_Struct(self, StructLayout, &rbffi_struct_layout_data_type, layout);
return rb_ary_dup(layout->rbFields);
}
static void
-struct_layout_mark(StructLayout *layout)
+struct_layout_mark(void *data)
{
- rb_gc_mark(layout->rbFieldMap);
- rb_gc_mark(layout->rbFieldNames);
- rb_gc_mark(layout->rbFields);
- /* Clear the cache, to be safe from changes of fieldName VALUE by GC.compact.
- * TODO: Move cache clearing to compactation callback provided by Ruby-2.7+.
- */
+ StructLayout *layout = (StructLayout *)data;
+ rb_gc_mark_movable(layout->rbFieldMap);
+ rb_gc_mark_movable(layout->rbFieldNames);
+ rb_gc_mark_movable(layout->rbFields);
+ /* The values stored in layout->cache_row.fieldName are primary stored in layout->rbFieldMap and are marked there */
+}
+
+static void
+struct_layout_compact(void *data)
+{
+ StructLayout *layout = (StructLayout *)data;
+ ffi_gc_location(layout->rbFieldMap);
+ ffi_gc_location(layout->rbFieldNames);
+ ffi_gc_location(layout->rbFields);
+
+ /* Clear the cache, to be safe from changes of fieldName VALUE by GC.compact */
memset(&layout->cache_row, 0, sizeof(layout->cache_row));
}
static void
-struct_layout_free(StructLayout *layout)
+struct_layout_free(void *data)
{
+ StructLayout *layout = (StructLayout *)data;
xfree(layout->ffiTypes);
xfree(layout->base.ffiType);
xfree(layout->fields);
@@ -617,6 +676,16 @@ struct_layout_free(StructLayout *layout)
}
+static size_t
+struct_layout_memsize(const void * data)
+{
+ const StructLayout *layout = (const StructLayout *)data;
+ size_t memsize = sizeof(StructLayout);
+ memsize += layout->fieldCount * (sizeof(StructField *) + sizeof(ffi_type *));
+ memsize += sizeof(*layout->base.ffiType);
+ return memsize;
+}
+
void
rbffi_StructLayout_Init(VALUE moduleFFI)
{
diff --git a/ext/ffi_c/Type.c b/ext/ffi_c/Type.c
index 7776bb0..a94c009 100644
--- a/ext/ffi_c/Type.c
+++ b/ext/ffi_c/Type.c
@@ -14,7 +14,7 @@
* * Neither the name of the Ruby FFI project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -33,36 +33,74 @@
#include <sys/types.h>
#include <ruby.h>
+#if HAVE_RB_EXT_RACTOR_SAFE
+#include <ruby/ractor.h>
+#endif
#include <ffi.h>
#include "rbffi.h"
#include "compat.h"
#include "Types.h"
#include "Type.h"
+static size_t type_memsize(const void *);
typedef struct BuiltinType_ {
Type type;
- char* name;
+ const char* name;
} BuiltinType;
-static void builtin_type_free(BuiltinType *);
+static size_t builtin_type_memsize(const void *);
VALUE rbffi_TypeClass = Qnil;
static VALUE classBuiltinType = Qnil;
static VALUE moduleNativeType = Qnil;
-static VALUE typeMap = Qnil, sizeMap = Qnil;
-static ID id_find_type = 0, id_type_size = 0, id_size = 0;
+static VALUE typeMap = Qnil;
+static ID id_type_size = 0, id_size = 0;
+#if HAVE_RB_EXT_RACTOR_SAFE
+static rb_ractor_local_key_t custom_typedefs_key;
+#endif
+
+const rb_data_type_t rbffi_type_data_type = { /* extern */
+ .wrap_struct_name = "FFI::Type",
+ .function = {
+ .dmark = NULL,
+ .dfree = RUBY_TYPED_DEFAULT_FREE,
+ .dsize = type_memsize,
+ },
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
+
+static const rb_data_type_t builtin_type_data_type = {
+ .wrap_struct_name = "FFI::Type::Builtin",
+ .function = {
+ .dmark = NULL,
+ .dfree = RUBY_TYPED_DEFAULT_FREE,
+ .dsize = builtin_type_memsize,
+ },
+ .parent = &rbffi_type_data_type,
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
+
+static size_t
+type_memsize(const void *data)
+{
+ return sizeof(Type);
+}
static VALUE
type_allocate(VALUE klass)
{
Type* type;
- VALUE obj = Data_Make_Struct(klass, Type, NULL, -1, type);
+ VALUE obj = TypedData_Make_Struct(klass, Type, &rbffi_type_data_type, type);
type->nativeType = -1;
type->ffiType = &ffi_type_void;
-
+
return obj;
}
@@ -78,18 +116,20 @@ type_initialize(VALUE self, VALUE value)
Type* type;
Type* other;
- Data_Get_Struct(self, Type, type);
+ TypedData_Get_Struct(self, Type, &rbffi_type_data_type, type);
if (FIXNUM_P(value)) {
type->nativeType = FIX2INT(value);
} else if (rb_obj_is_kind_of(value, rbffi_TypeClass)) {
- Data_Get_Struct(value, Type, other);
+ TypedData_Get_Struct(value, Type, &rbffi_type_data_type, other);
type->nativeType = other->nativeType;
type->ffiType = other->ffiType;
} else {
rb_raise(rb_eArgError, "wrong type");
}
-
+
+ rb_obj_freeze(self);
+
return self;
}
@@ -103,7 +143,7 @@ type_size(VALUE self)
{
Type *type;
- Data_Get_Struct(self, Type, type);
+ TypedData_Get_Struct(self, Type, &rbffi_type_data_type, type);
return INT2FIX(type->ffiType->size);
}
@@ -118,7 +158,7 @@ type_alignment(VALUE self)
{
Type *type;
- Data_Get_Struct(self, Type, type);
+ TypedData_Get_Struct(self, Type, &rbffi_type_data_type, type);
return INT2FIX(type->ffiType->alignment);
}
@@ -134,9 +174,9 @@ type_inspect(VALUE self)
char buf[100];
Type *type;
- Data_Get_Struct(self, Type, type);
+ TypedData_Get_Struct(self, Type, &rbffi_type_data_type, type);
- snprintf(buf, sizeof(buf), "#<%s:%p size=%d alignment=%d>",
+ snprintf(buf, sizeof(buf), "#<%s::%p size=%d alignment=%d>",
rb_obj_classname(self), type, (int) type->ffiType->size, (int) type->ffiType->alignment);
return rb_str_new2(buf);
@@ -148,20 +188,21 @@ builtin_type_new(VALUE klass, int nativeType, ffi_type* ffiType, const char* nam
BuiltinType* type;
VALUE obj = Qnil;
- obj = Data_Make_Struct(klass, BuiltinType, NULL, builtin_type_free, type);
-
- type->name = strdup(name);
+ obj = TypedData_Make_Struct(klass, BuiltinType, &builtin_type_data_type, type);
+
+ type->name = name;
type->type.nativeType = nativeType;
type->type.ffiType = ffiType;
+ rb_obj_freeze(obj);
+
return obj;
}
-static void
-builtin_type_free(BuiltinType *type)
+static size_t
+builtin_type_memsize(const void *data)
{
- free(type->name);
- xfree(type);
+ return sizeof(BuiltinType) + sizeof(ffi_type);
}
/*
@@ -175,8 +216,8 @@ builtin_type_inspect(VALUE self)
char buf[100];
BuiltinType *type;
- Data_Get_Struct(self, BuiltinType, type);
- snprintf(buf, sizeof(buf), "#<%s:%s size=%d alignment=%d>",
+ TypedData_Get_Struct(self, BuiltinType, &builtin_type_data_type, type);
+ snprintf(buf, sizeof(buf), "#<%s::%s size=%d alignment=%d>",
rb_obj_classname(self), type->name, (int) type->type.ffiType->size, type->type.ffiType->alignment);
return rb_str_new2(buf);
@@ -186,21 +227,21 @@ int
rbffi_type_size(VALUE type)
{
int t = TYPE(type);
-
+
if (t == T_FIXNUM || t == T_BIGNUM) {
return NUM2INT(type);
-
+
} else if (t == T_SYMBOL) {
/*
- * Try looking up directly in the type and size maps
+ * Try looking up directly in the type map
*/
VALUE nType;
if ((nType = rb_hash_lookup(typeMap, type)) != Qnil) {
if (rb_obj_is_kind_of(nType, rbffi_TypeClass)) {
Type* type;
- Data_Get_Struct(nType, Type, type);
+ TypedData_Get_Struct(nType, Type, &rbffi_type_data_type, type);
return (int) type->ffiType->size;
-
+
} else if (rb_respond_to(nType, id_size)) {
return NUM2INT(rb_funcall2(nType, id_size, 0, NULL));
}
@@ -208,26 +249,51 @@ rbffi_type_size(VALUE type)
/* Not found - call up to the ruby version to resolve */
return NUM2INT(rb_funcall2(rbffi_FFIModule, id_type_size, 1, &type));
-
+
} else {
return NUM2INT(rb_funcall2(type, id_size, 0, NULL));
}
}
+static VALUE
+custom_typedefs(VALUE self)
+{
+#if HAVE_RB_EXT_RACTOR_SAFE
+ VALUE hash = rb_ractor_local_storage_value(custom_typedefs_key);
+ if (hash == Qnil) {
+ hash = rb_hash_new();
+ rb_ractor_local_storage_value_set(custom_typedefs_key, hash);
+ }
+#else
+ static VALUE hash = Qundef;
+ if (hash == Qundef) {
+ rb_global_variable(&hash);
+ hash = rb_hash_new();
+ }
+#endif
+ return hash;
+}
+
VALUE
rbffi_Type_Lookup(VALUE name)
{
int t = TYPE(name);
if (t == T_SYMBOL || t == T_STRING) {
/*
- * Try looking up directly in the type Map
+ * Try looking up directly in the type map
*/
VALUE nType;
+ VALUE cust = custom_typedefs(Qnil);
+
+ if ((nType = rb_hash_lookup(cust, name)) != Qnil && rb_obj_is_kind_of(nType, rbffi_TypeClass)) {
+ return nType;
+ }
+
if ((nType = rb_hash_lookup(typeMap, name)) != Qnil && rb_obj_is_kind_of(nType, rbffi_TypeClass)) {
return nType;
}
} else if (rb_obj_is_kind_of(name, rbffi_TypeClass)) {
-
+
return name;
}
@@ -251,13 +317,15 @@ rbffi_Type_Init(VALUE moduleFFI)
* Document-constant: FFI::TypeDefs
*/
rb_define_const(moduleFFI, "TypeDefs", typeMap = rb_hash_new());
- rb_define_const(moduleFFI, "SizeTypes", sizeMap = rb_hash_new());
rb_global_variable(&typeMap);
- rb_global_variable(&sizeMap);
- id_find_type = rb_intern("find_type");
id_type_size = rb_intern("type_size");
id_size = rb_intern("size");
+#if HAVE_RB_EXT_RACTOR_SAFE
+ custom_typedefs_key = rb_ractor_local_storage_value_newkey();
+#endif
+ rb_define_module_function(moduleFFI, "custom_typedefs", custom_typedefs, 0);
+
/*
* Document-class: FFI::Type::Builtin
* Class for Built-in types.
@@ -297,7 +365,7 @@ rbffi_Type_Init(VALUE moduleFFI)
* * BUFFER_OUT
* * VARARGS (function takes a variable number of arguments)
*
- * All these constants are exported to {FFI} module prefixed with "TYPE_".
+ * All these constants are exported to {FFI} module prefixed with "TYPE_".
* They are objets from {FFI::Type::Builtin} class.
*/
moduleNativeType = rb_define_module_under(moduleFFI, "NativeType");
@@ -318,7 +386,7 @@ rbffi_Type_Init(VALUE moduleFFI)
/* Make Type::Builtin non-allocatable */
rb_undef_method(CLASS_OF(classBuiltinType), "new");
rb_define_method(classBuiltinType, "inspect", builtin_type_inspect, 0);
-
+
rb_global_variable(&rbffi_TypeClass);
rb_global_variable(&classBuiltinType);
diff --git a/ext/ffi_c/Type.h b/ext/ffi_c/Type.h
index b81995a..b6de634 100644
--- a/ext/ffi_c/Type.h
+++ b/ext/ffi_c/Type.h
@@ -44,7 +44,7 @@ extern "C" {
typedef struct Type_ Type;
#include "Types.h"
-
+
struct Type_ {
NativeType nativeType;
ffi_type* ffiType;
@@ -53,6 +53,8 @@ struct Type_ {
extern VALUE rbffi_TypeClass;
extern VALUE rbffi_Type_Lookup(VALUE type);
+extern const rb_data_type_t rbffi_type_data_type;
+
#ifdef __cplusplus
}
#endif
diff --git a/ext/ffi_c/Types.c b/ext/ffi_c/Types.c
index 77741e0..8695a3b 100644
--- a/ext/ffi_c/Types.c
+++ b/ext/ffi_c/Types.c
@@ -97,7 +97,7 @@ rbffi_NativeValue_ToRuby(Type* type, VALUE rbType, const void* ptr)
AbstractMemory* mem;
VALUE rbMemory = rbffi_MemoryPointer_NewInstance(1, sbv->base.ffiType->size, false);
- Data_Get_Struct(rbMemory, AbstractMemory, mem);
+ TypedData_Get_Struct(rbMemory, AbstractMemory, &rbffi_abstract_memory_data_type, mem);
memcpy(mem->address, ptr, sbv->base.ffiType->size);
RB_GC_GUARD(rbMemory);
RB_GC_GUARD(rbType);
diff --git a/ext/ffi_c/Variadic.c b/ext/ffi_c/Variadic.c
index 8ad38b1..09d7ce8 100644
--- a/ext/ffi_c/Variadic.c
+++ b/ext/ffi_c/Variadic.c
@@ -36,6 +36,9 @@
#include <stdint.h>
#include <stdbool.h>
#include <ruby.h>
+#if HAVE_RB_EXT_RACTOR_SAFE
+#include <ruby/ractor.h>
+#endif
#include <ffi.h>
#include "rbffi.h"
@@ -62,35 +65,65 @@ typedef struct VariadicInvoker_ {
bool blocking;
} VariadicInvoker;
-
static VALUE variadic_allocate(VALUE klass);
static VALUE variadic_initialize(VALUE self, VALUE rbFunction, VALUE rbParameterTypes,
VALUE rbReturnType, VALUE options);
-static void variadic_mark(VariadicInvoker *);
+static void variadic_mark(void *);
+static void variadic_compact(void *);
+static size_t variadic_memsize(const void *);
static VALUE classVariadicInvoker = Qnil;
+static const rb_data_type_t variadic_data_type = {
+ .wrap_struct_name = "FFI::VariadicInvoker",
+ .function = {
+ .dmark = variadic_mark,
+ .dfree = RUBY_TYPED_DEFAULT_FREE,
+ .dsize = variadic_memsize,
+ ffi_compact_callback( variadic_compact )
+ },
+ // IMPORTANT: WB_PROTECTED objects must only use the RB_OBJ_WRITE()
+ // macro to update VALUE references, as to trigger write barriers.
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
+};
+
static VALUE
variadic_allocate(VALUE klass)
{
VariadicInvoker *invoker;
- VALUE obj = Data_Make_Struct(klass, VariadicInvoker, variadic_mark, -1, invoker);
+ VALUE obj = TypedData_Make_Struct(klass, VariadicInvoker, &variadic_data_type, invoker);
- invoker->rbAddress = Qnil;
- invoker->rbEnums = Qnil;
- invoker->rbReturnType = Qnil;
+ RB_OBJ_WRITE(obj, &invoker->rbAddress, Qnil);
+ RB_OBJ_WRITE(obj, &invoker->rbEnums, Qnil);
+ RB_OBJ_WRITE(obj, &invoker->rbReturnType, Qnil);
invoker->blocking = false;
return obj;
}
static void
-variadic_mark(VariadicInvoker *invoker)
+variadic_mark(void *data)
+{
+ VariadicInvoker *invoker = (VariadicInvoker *)data;
+ rb_gc_mark_movable(invoker->rbEnums);
+ rb_gc_mark_movable(invoker->rbAddress);
+ rb_gc_mark_movable(invoker->rbReturnType);
+}
+
+static void
+variadic_compact(void *data)
{
- rb_gc_mark(invoker->rbEnums);
- rb_gc_mark(invoker->rbAddress);
- rb_gc_mark(invoker->rbReturnType);
+ VariadicInvoker *invoker = (VariadicInvoker *)data;
+ ffi_gc_location(invoker->rbEnums);
+ ffi_gc_location(invoker->rbAddress);
+ ffi_gc_location(invoker->rbReturnType);
+}
+
+static size_t
+variadic_memsize(const void *data)
+{
+ return sizeof(VariadicInvoker);
}
static VALUE
@@ -108,10 +141,10 @@ variadic_initialize(VALUE self, VALUE rbFunction, VALUE rbParameterTypes, VALUE
Check_Type(options, T_HASH);
convention = rb_hash_aref(options, ID2SYM(rb_intern("convention")));
- Data_Get_Struct(self, VariadicInvoker, invoker);
- invoker->rbEnums = rb_hash_aref(options, ID2SYM(rb_intern("enums")));
- invoker->rbAddress = rbFunction;
- invoker->function = rbffi_AbstractMemory_Cast(rbFunction, rbffi_PointerClass)->address;
+ TypedData_Get_Struct(self, VariadicInvoker, &variadic_data_type, invoker);
+ RB_OBJ_WRITE(self, &invoker->rbEnums, rb_hash_aref(options, ID2SYM(rb_intern("enums"))));
+ RB_OBJ_WRITE(self, &invoker->rbAddress, rbFunction);
+ invoker->function = rbffi_AbstractMemory_Cast(rbFunction, &rbffi_pointer_data_type)->address;
invoker->blocking = RTEST(rb_hash_aref(options, ID2SYM(rb_intern("blocking"))));
#if defined(X86_WIN32)
@@ -122,13 +155,13 @@ variadic_initialize(VALUE self, VALUE rbFunction, VALUE rbParameterTypes, VALUE
invoker->abi = FFI_DEFAULT_ABI;
#endif
- invoker->rbReturnType = rbffi_Type_Lookup(rbReturnType);
+ RB_OBJ_WRITE(self, &invoker->rbReturnType, rbffi_Type_Lookup(rbReturnType));
if (!RTEST(invoker->rbReturnType)) {
VALUE typeName = rb_funcall2(rbReturnType, rb_intern("inspect"), 0, NULL);
rb_raise(rb_eTypeError, "Invalid return type (%s)", RSTRING_PTR(typeName));
}
- Data_Get_Struct(rbReturnType, Type, invoker->returnType);
+ TypedData_Get_Struct(rbReturnType, Type, &rbffi_type_data_type, invoker->returnType);
invoker->paramCount = -1;
@@ -142,7 +175,7 @@ variadic_initialize(VALUE self, VALUE rbFunction, VALUE rbParameterTypes, VALUE
VALUE typeName = rb_funcall2(entry, rb_intern("inspect"), 0, NULL);
rb_raise(rb_eTypeError, "Invalid parameter type (%s)", RSTRING_PTR(typeName));
}
- Data_Get_Struct(rbType, Type, type);
+ TypedData_Get_Struct(rbType, Type, &rbffi_type_data_type, type);
if (type->nativeType != NATIVE_VARARGS) {
rb_ary_push(fixed, entry);
}
@@ -150,7 +183,7 @@ variadic_initialize(VALUE self, VALUE rbFunction, VALUE rbParameterTypes, VALUE
/*
* @fixed and @type_map are used by the parameter mangling ruby code
*/
- rb_iv_set(self, "@fixed", fixed);
+ rb_iv_set(self, "@fixed", rb_obj_freeze(fixed));
rb_iv_set(self, "@type_map", rb_hash_aref(options, ID2SYM(rb_intern("type_map"))));
return retval;
@@ -176,7 +209,7 @@ variadic_invoke(VALUE self, VALUE parameterTypes, VALUE parameterValues)
Check_Type(parameterTypes, T_ARRAY);
Check_Type(parameterValues, T_ARRAY);
- Data_Get_Struct(self, VariadicInvoker, invoker);
+ TypedData_Get_Struct(self, VariadicInvoker, &variadic_data_type, invoker);
paramCount = (int) RARRAY_LEN(parameterTypes);
paramTypes = ALLOCA_N(Type *, paramCount);
ffiParamTypes = ALLOCA_N(ffi_type *, paramCount);
@@ -192,25 +225,25 @@ variadic_invoke(VALUE self, VALUE parameterTypes, VALUE parameterValues)
if (!rb_obj_is_kind_of(rbType, rbffi_TypeClass)) {
rb_raise(rb_eTypeError, "wrong type. Expected (FFI::Type)");
}
- Data_Get_Struct(rbType, Type, paramTypes[i]);
+ TypedData_Get_Struct(rbType, Type, &rbffi_type_data_type, paramTypes[i]);
switch (paramTypes[i]->nativeType) {
case NATIVE_INT8:
case NATIVE_INT16:
case NATIVE_INT32:
rbType = rb_const_get(rbffi_TypeClass, rb_intern("INT32"));
- Data_Get_Struct(rbType, Type, paramTypes[i]);
+ TypedData_Get_Struct(rbType, Type, &rbffi_type_data_type, paramTypes[i]);
break;
case NATIVE_UINT8:
case NATIVE_UINT16:
case NATIVE_UINT32:
rbType = rb_const_get(rbffi_TypeClass, rb_intern("UINT32"));
- Data_Get_Struct(rbType, Type, paramTypes[i]);
+ TypedData_Get_Struct(rbType, Type, &rbffi_type_data_type, paramTypes[i]);
break;
case NATIVE_FLOAT32:
rbType = rb_const_get(rbffi_TypeClass, rb_intern("DOUBLE"));
- Data_Get_Struct(rbType, Type, paramTypes[i]);
+ TypedData_Get_Struct(rbType, Type, &rbffi_type_data_type, paramTypes[i]);
break;
case NATIVE_FUNCTION:
@@ -288,6 +321,14 @@ variadic_invoke(VALUE self, VALUE parameterTypes, VALUE parameterValues)
return rbffi_NativeValue_ToRuby(invoker->returnType, invoker->rbReturnType, retval);
}
+static VALUE
+variadic_return_type(VALUE self)
+{
+ VariadicInvoker* invoker;
+
+ TypedData_Get_Struct(self, VariadicInvoker, &variadic_data_type, invoker);
+ return invoker->rbReturnType;
+}
void
rbffi_Variadic_Init(VALUE moduleFFI)
@@ -299,5 +340,6 @@ rbffi_Variadic_Init(VALUE moduleFFI)
rb_define_method(classVariadicInvoker, "initialize", variadic_initialize, 4);
rb_define_method(classVariadicInvoker, "invoke", variadic_invoke, 2);
+ rb_define_method(classVariadicInvoker, "return_type", variadic_return_type, 0);
}
diff --git a/ext/ffi_c/compat.h b/ext/ffi_c/compat.h
index 3f7bbae..a1be55d 100644
--- a/ext/ffi_c/compat.h
+++ b/ext/ffi_c/compat.h
@@ -32,26 +32,6 @@
#include <ruby.h>
-#ifndef RARRAY_LEN
-# define RARRAY_LEN(ary) RARRAY(ary)->len
-#endif
-
-#ifndef RARRAY_PTR
-# define RARRAY_PTR(ary) RARRAY(ary)->ptr
-#endif
-
-#ifndef RSTRING_LEN
-# define RSTRING_LEN(s) RSTRING(s)->len
-#endif
-
-#ifndef RSTRING_PTR
-# define RSTRING_PTR(s) RSTRING(s)->ptr
-#endif
-
-#ifndef NUM2ULL
-# define NUM2ULL(x) rb_num2ull((VALUE)x)
-#endif
-
#ifndef roundup
# define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
#endif
@@ -75,8 +55,28 @@
# define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
-#ifndef RB_GC_GUARD
-# define RB_GC_GUARD(x) (x)
+
+
+/* For compatibility with ruby < 2.7 */
+#ifdef HAVE_RB_GC_MARK_MOVABLE
+#define ffi_compact_callback(x) .dcompact = (x),
+#define ffi_gc_location(x) x = rb_gc_location(x)
+#else
+#define rb_gc_mark_movable(x) rb_gc_mark(x)
+#define ffi_compact_callback(x)
+#define ffi_gc_location(x)
+#endif
+
+
+/* For compatibility with ruby < 3.0 */
+#ifndef RUBY_TYPED_FROZEN_SHAREABLE
+#define FFI_RUBY_TYPED_FROZEN_SHAREABLE 0
+#else
+#define FFI_RUBY_TYPED_FROZEN_SHAREABLE RUBY_TYPED_FROZEN_SHAREABLE
+#endif
+
+#ifndef HAVE_RB_EXT_RACTOR_SAFE
+#define rb_ractor_make_shareable(self) rb_obj_freeze(self);
#endif
#endif /* RBFFI_COMPAT_H */
diff --git a/ext/ffi_c/extconf.rb b/ext/ffi_c/extconf.rb
index 720fb06..b3eb020 100644..100755
--- a/ext/ffi_c/extconf.rb
+++ b/ext/ffi_c/extconf.rb
@@ -33,7 +33,7 @@ if RUBY_ENGINE == 'ruby' || RUBY_ENGINE == 'rbx'
$CFLAGS.gsub!(/[\s+]-ansi/, '')
$CFLAGS.gsub!(/[\s+]-std=[^\s]+/, '')
# solaris 10 needs -c99 for <stdbool.h>
- $CFLAGS << " -std=c99" if RbConfig::CONFIG['host_os'] =~ /solaris(!?2\.11)/
+ $CFLAGS << " -g -std=c99" if RbConfig::CONFIG['host_os'] =~ /solaris(!?2\.11)/
# Check whether we use system libffi
system_libffi = enable_config('system-libffi', :try)
@@ -57,10 +57,13 @@ if RUBY_ENGINE == 'ruby' || RUBY_ENGINE == 'rbx'
append_ldflags "-Wl,--exclude-libs,ALL"
end
+ have_func 'rb_gc_mark_movable' # since ruby-2.7
+
# Some linux archs need explicit linking to pthread, see https://github.com/ffi/ffi/issues/893
append_ldflags "-pthread"
ffi_alloc_default = RbConfig::CONFIG['host_os'] =~ /darwin/i && RbConfig::CONFIG['host'] =~ /arm|aarch64/i
+ ffi_alloc_default = ffi_alloc_default || RbConfig::CONFIG['host'] =~ /hppa/i
if enable_config('libffi-alloc', ffi_alloc_default)
$defs << "-DUSE_FFI_ALLOC"
end
diff --git a/ext/ffi_c/ffi.c b/ext/ffi_c/ffi.c
index 22ea3bf..e297f8a 100644
--- a/ext/ffi_c/ffi.c
+++ b/ext/ffi_c/ffi.c
@@ -60,6 +60,10 @@ static VALUE moduleFFI = Qnil;
void
Init_ffi_c(void)
{
+ #ifdef HAVE_RB_EXT_RACTOR_SAFE
+ rb_ext_ractor_safe(1);
+ #endif
+
/*
* Document-module: FFI
*
diff --git a/ext/ffi_c/libffi b/ext/ffi_c/libffi
-Subproject 5c63b463b87d3c06102a4a7f05f395929d9ea79
+Subproject ac598b7f5272d536b75f4b3833a4610cf4cd940
diff --git a/ext/ffi_c/rbffi.h b/ext/ffi_c/rbffi.h
index 89b3e32..0e4e91a 100644
--- a/ext/ffi_c/rbffi.h
+++ b/ext/ffi_c/rbffi.h
@@ -39,7 +39,7 @@ extern "C" {
#define MAX_PARAMETERS (32)
extern VALUE rbffi_FFIModule;
-
+
extern void rbffi_Type_Init(VALUE ffiModule);
extern void rbffi_Buffer_Init(VALUE ffiModule);
extern void rbffi_Invoker_Init(VALUE ffiModule);