diff options
Diffstat (limited to 'ext/ffi_c')
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); |