summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml12
-rw-r--r--.gitignore13
-rw-r--r--.travis.yml19
-rw-r--r--README.md5
-rw-r--r--Rakefile3
-rw-r--r--ext/ffi_c/AbstractMemory.c90
-rw-r--r--ext/ffi_c/AbstractMemory.h5
-rw-r--r--ext/ffi_c/ArrayType.c62
-rw-r--r--ext/ffi_c/ArrayType.h1
-rw-r--r--ext/ffi_c/Buffer.c115
-rw-r--r--ext/ffi_c/Call.c18
-rw-r--r--ext/ffi_c/DynamicLibrary.c115
-rw-r--r--ext/ffi_c/Function.c336
-rw-r--r--ext/ffi_c/Function.h1
-rw-r--r--ext/ffi_c/FunctionInfo.c102
-rw-r--r--ext/ffi_c/LastError.c32
-rw-r--r--ext/ffi_c/MappedType.c89
-rw-r--r--ext/ffi_c/MappedType.h2
-rw-r--r--ext/ffi_c/MemoryPointer.c41
-rw-r--r--ext/ffi_c/MethodHandle.c4
-rw-r--r--ext/ffi_c/Pointer.c97
-rw-r--r--ext/ffi_c/Pointer.h1
-rw-r--r--ext/ffi_c/Struct.c231
-rw-r--r--ext/ffi_c/Struct.h11
-rw-r--r--ext/ffi_c/StructByValue.c64
-rw-r--r--ext/ffi_c/StructLayout.c165
-rw-r--r--ext/ffi_c/Type.c140
-rw-r--r--ext/ffi_c/Type.h4
-rw-r--r--ext/ffi_c/Types.c2
-rw-r--r--ext/ffi_c/Variadic.c88
-rw-r--r--ext/ffi_c/compat.h44
-rwxr-xr-x[-rw-r--r--]ext/ffi_c/extconf.rb5
-rw-r--r--ext/ffi_c/ffi.c4
m---------ext/ffi_c/libffi0
-rw-r--r--ext/ffi_c/rbffi.h2
-rw-r--r--ffi.gemspec2
-rw-r--r--gen/Rakefile2
-rw-r--r--lib/ffi/autopointer.rb29
-rw-r--r--lib/ffi/compat.rb43
-rw-r--r--lib/ffi/data_converter.rb4
-rw-r--r--lib/ffi/dynamic_library.rb89
-rw-r--r--lib/ffi/ffi.rb2
-rw-r--r--lib/ffi/function.rb71
-rw-r--r--lib/ffi/library.rb114
-rw-r--r--lib/ffi/managedstruct.rb2
-rw-r--r--lib/ffi/platform.rb28
-rw-r--r--lib/ffi/platform/hppa1.1-linux/types.conf178
-rw-r--r--lib/ffi/platform/hppa2.0-linux/types.conf178
-rw-r--r--lib/ffi/platform/sparcv9-linux/types.conf (renamed from lib/ffi/platform/sparc64-linux/types.conf)0
-rw-r--r--lib/ffi/platform/sw_64-linux/types.conf141
-rw-r--r--lib/ffi/struct.rb3
-rw-r--r--lib/ffi/struct_layout.rb2
-rw-r--r--lib/ffi/struct_layout_builder.rb2
-rw-r--r--lib/ffi/types.rb35
-rw-r--r--lib/ffi/variadic.rb27
-rw-r--r--spec/ffi/async_callback_spec.rb23
-rw-r--r--spec/ffi/buffer_spec.rb10
-rw-r--r--spec/ffi/dynamic_library_spec.rb60
-rw-r--r--spec/ffi/enum_spec.rb38
-rw-r--r--spec/ffi/errno_spec.rb8
-rw-r--r--spec/ffi/fixtures/ClosureTest.c12
-rw-r--r--spec/ffi/fixtures/PipeHelperWindows.c4
-rw-r--r--spec/ffi/fixtures/PointerTest.c2
-rw-r--r--spec/ffi/fixtures/compile.rb4
-rw-r--r--spec/ffi/function_spec.rb42
-rw-r--r--spec/ffi/function_type_spec.rb27
-rw-r--r--spec/ffi/gc_compact_spec.rb66
-rw-r--r--spec/ffi/library_spec.rb95
-rw-r--r--spec/ffi/managed_struct_spec.rb2
-rw-r--r--spec/ffi/memorypointer_spec.rb34
-rw-r--r--spec/ffi/platform_spec.rb13
-rw-r--r--spec/ffi/pointer_spec.rb39
-rw-r--r--spec/ffi/rbx/memory_pointer_spec.rb20
-rw-r--r--spec/ffi/spec_helper.rb4
-rw-r--r--spec/ffi/struct_by_ref_spec.rb9
-rw-r--r--spec/ffi/struct_spec.rb109
-rw-r--r--spec/ffi/type_spec.rb76
-rw-r--r--spec/ffi/variadic_spec.rb32
78 files changed, 2886 insertions, 718 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 376c068..fc48721 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,5 +1,8 @@
name: CI
on: [push, pull_request]
+permissions:
+ contents: read
+
jobs:
system-libffi:
# Run on latest MRI with explicit selection of system or builtin libffi
@@ -29,7 +32,7 @@ jobs:
env:
# work around misconfiguration of libffi on MacOS with homebrew
PKG_CONFIG_PATH: ${{ env.PKG_CONFIG_PATH }}:/usr/local/opt/libffi/lib/pkgconfig
- - run: bundle exec rake test
+ - run: bundle exec rake test FFI_TEST_GC=true
- run: bundle exec rake types_conf && git --no-pager diff
specs:
@@ -39,12 +42,10 @@ jobs:
fail-fast: false
matrix:
os: [ ubuntu, macos, windows ]
- ruby: [ 2.3, 2.4, 2.5, 2.6, 2.7, ruby-head, truffleruby-head, jruby-head ]
+ ruby: [ 2.5, 2.6, 2.7, '3.0', 3.1, 3.2, ruby-head, truffleruby-head, jruby-head ]
exclude:
- os: windows
ruby: truffleruby-head
- - os: windows
- ruby: 2.3 # compilation fails
runs-on: ${{ matrix.os }}-latest
steps:
- uses: actions/checkout@v2
@@ -59,8 +60,7 @@ jobs:
- run: bundle exec rake libffi
- run: bundle exec rake compile
- - run: bundle exec rake test
- continue-on-error: ${{ matrix.ruby == 'jruby-head' }}
+ - run: bundle exec rake test FFI_TEST_GC=true
- run: bundle exec rake bench:all
if: ${{ matrix.ruby != 'truffleruby-head' && matrix.ruby != 'jruby-head' }}
diff --git a/.gitignore b/.gitignore
index ff9d895..93c98ce 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,14 +12,17 @@ build
*.so
*.[oa]
core
-lib/ffi/types.conf
-lib/ffi_c.bundle
-lib/ffi_c.so
+/lib/ffi/types.conf
+/lib/ffi_c.bundle
+/lib/ffi_c.so
vendor
.bundle
Gemfile.lock
types_log
*.gem
embed-test.rb.log
-spec/ffi/embed-test/ext/Makefile
-spec/ffi/embed-test/ext/embed_test.bundle
+/spec/ffi/embed-test/ext/Makefile
+/spec/ffi/embed-test/ext/embed_test.bundle
+/ext/ffi_c/Makefile
+/ext/ffi_c/extconf.h
+/ext/ffi_c/mkmf.log
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 518ff08..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,19 +0,0 @@
-dist: trusty
-group: beta
-language: generic
-git:
- submodules: false
-matrix:
- include:
- - name: powerpc
- before_install: |
- docker run --rm --privileged multiarch/qemu-user-static:register --reset &&
- docker build --rm -t ffi-powerpc -f spec/env/Dockerfile.powerpc .
- script: |
- docker run --rm -t -v `pwd`:/ffi ffi-powerpc
- - name: armhf
- before_install: |
- docker run --rm --privileged multiarch/qemu-user-static:register --reset &&
- docker build --rm -t ffi-armhf -f spec/env/Dockerfile.armhf .
- script: |
- docker run --rm -t -v `pwd`:/ffi ffi-armhf
diff --git a/README.md b/README.md
index 5845f26..574881f 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Ruby-FFI https://github.com/ffi/ffi/wiki [![Build Status](https://travis-ci.com/ffi/ffi.svg?branch=master)](https://travis-ci.com/ffi/ffi) [![Build status Windows](https://ci.appveyor.com/api/projects/status/r8wxn1sd4s794gg1/branch/master?svg=true)](https://ci.appveyor.com/project/larskanis/ffi-aofqa/branch/master)
+# Ruby-FFI https://github.com/ffi/ffi/wiki
## Description
@@ -15,6 +15,7 @@ using Ruby-FFI](https://github.com/ffi/ffi/wiki/why-use-ffi).
* C structs (also nested), enums and global variables
* Callbacks from C to Ruby
* Automatic garbage collection of native memory
+* Usable in Ractor
## Synopsis
@@ -62,7 +63,7 @@ On JRuby and TruffleRuby, there are no requirements to install the FFI gem, and
From rubygems:
[sudo] gem install ffi
-
+
From a Gemfile using git or GitHub
gem 'ffi', github: 'ffi/ffi', submodules: true
diff --git a/Rakefile b/Rakefile
index f3b3746..ce9c46a 100644
--- a/Rakefile
+++ b/Rakefile
@@ -31,8 +31,7 @@ CLEAN.include 'spec/ffi/fixtures/*.o'
CLEAN.include 'spec/ffi/embed-test/ext/*.{o,def}'
CLEAN.include 'spec/ffi/embed-test/ext/Makefile'
CLEAN.include "pkg/ffi-*-{mingw32,java}"
-CLEAN.include 'lib/1.*'
-CLEAN.include 'lib/2.*'
+CLEAN.include 'lib/{2,3}.*'
# clean all shipped files, that are not in git
CLEAN.include(
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);
diff --git a/ffi.gemspec b/ffi.gemspec
index 0e9d86a..71b5931 100644
--- a/ffi.gemspec
+++ b/ffi.gemspec
@@ -34,7 +34,7 @@ Gem::Specification.new do |s|
s.rdoc_options = %w[--exclude=ext/ffi_c/.*\.o$ --exclude=ffi_c\.(bundle|so)$]
s.license = 'BSD-3-Clause'
s.require_paths << 'ext/ffi_c'
- s.required_ruby_version = '>= 2.3'
+ s.required_ruby_version = '>= 2.5'
s.add_development_dependency 'rake', '~> 13.0'
s.add_development_dependency 'rake-compiler', '~> 1.1'
s.add_development_dependency 'rake-compiler-dock', '~> 1.0'
diff --git a/gen/Rakefile b/gen/Rakefile
index f71a1da..65f62c9 100644
--- a/gen/Rakefile
+++ b/gen/Rakefile
@@ -9,7 +9,7 @@ logfile = File.join(File.dirname(__FILE__), 'log')
file types_conf do |task|
options = {}
- FileUtils.mkdir_p(File.dirname(task.name), { :mode => 0755 })
+ FileUtils.mkdir_p(File.dirname(task.name), mode: 0755)
File.open(task.name, File::CREAT|File::TRUNC|File::RDWR, 0644) do |f|
f.puts FFI::TypesGenerator.generate(options)
end
diff --git a/lib/ffi/autopointer.rb b/lib/ffi/autopointer.rb
index 679d7e6..bbcb6db 100644
--- a/lib/ffi/autopointer.rb
+++ b/lib/ffi/autopointer.rb
@@ -76,21 +76,21 @@ module FFI
# going to be useful if you subclass {AutoPointer}, and override
# #release, which by default does nothing.
def initialize(ptr, proc=nil, &block)
+ raise TypeError, "Invalid pointer" if ptr.nil? || !ptr.kind_of?(Pointer) ||
+ ptr.kind_of?(MemoryPointer) || ptr.kind_of?(AutoPointer)
super(ptr.type_size, ptr)
- raise TypeError, "Invalid pointer" if ptr.nil? || !ptr.kind_of?(Pointer) \
- || ptr.kind_of?(MemoryPointer) || ptr.kind_of?(AutoPointer)
@releaser = if proc
if not proc.respond_to?(:call)
raise RuntimeError.new("proc must be callable")
end
- CallableReleaser.new(ptr, proc)
+ Releaser.new(ptr, proc)
else
- if not self.class.respond_to?(:release)
+ if not self.class.respond_to?(:release, true)
raise RuntimeError.new("no release method defined")
end
- DefaultReleaser.new(ptr, self.class)
+ Releaser.new(ptr, self.class.method(:release))
end
ObjectSpace.define_finalizer(self, @releaser)
@@ -107,6 +107,7 @@ module FFI
# @return [Boolean] +autorelease+
# Set +autorelease+ property. See {Pointer Autorelease section at Pointer}.
def autorelease=(autorelease)
+ raise FrozenError.new("can't modify frozen #{self.class}") if frozen?
@releaser.autorelease=(autorelease)
end
@@ -149,23 +150,7 @@ module FFI
def call(*args)
release(@ptr) if @autorelease && @ptr
end
- end
-
- # DefaultReleaser is a {Releaser} used when an {AutoPointer} is defined
- # without Proc or Method. In this case, the pointer to release must be of
- # a class derived from AutoPointer with a {release} class method.
- class DefaultReleaser < Releaser
- # @param [Pointer] ptr
- # @return [nil]
- # Release +ptr+ using the {release} class method of its class.
- def release(ptr)
- @proc.release(ptr)
- end
- end
- # CallableReleaser is a {Releaser} used when an {AutoPointer} is defined with a
- # Proc or a Method.
- class CallableReleaser < Releaser
# Release +ptr+ by using Proc or Method defined at +ptr+
# {AutoPointer#initialize initialization}.
#
@@ -182,7 +167,7 @@ module FFI
# @return [Type::POINTER]
# @raise {RuntimeError} if class does not implement a +#release+ method
def self.native_type
- if not self.respond_to?(:release)
+ if not self.respond_to?(:release, true)
raise RuntimeError.new("no release method defined for #{self.inspect}")
end
Type::POINTER
diff --git a/lib/ffi/compat.rb b/lib/ffi/compat.rb
new file mode 100644
index 0000000..7569013
--- /dev/null
+++ b/lib/ffi/compat.rb
@@ -0,0 +1,43 @@
+#
+# Copyright (C) 2023-2023 Lars Kanis
+#
+# This file is part of ruby-ffi.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * 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
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+
+module FFI
+ if defined?(Ractor.make_shareable)
+ # This is for FFI internal use only.
+ def self.make_shareable(obj)
+ Ractor.make_shareable(obj)
+ end
+ else
+ def self.make_shareable(obj)
+ obj.freeze
+ end
+ end
+end
diff --git a/lib/ffi/data_converter.rb b/lib/ffi/data_converter.rb
index 1527588..7852e7e 100644
--- a/lib/ffi/data_converter.rb
+++ b/lib/ffi/data_converter.rb
@@ -31,7 +31,7 @@
module FFI
# This module is used to extend somes classes and give then a common API.
#
- # Most of methods defined here must be overriden.
+ # Most of methods defined here must be overridden.
module DataConverter
# Get native type.
#
@@ -41,7 +41,7 @@ module FFI
# Get native type from +type+.
#
# @overload native_type
- # @raise {NotImplementedError} This method must be overriden.
+ # @raise {NotImplementedError} This method must be overridden.
def native_type(type = nil)
if type
@native_type = FFI.find_type(type)
diff --git a/lib/ffi/dynamic_library.rb b/lib/ffi/dynamic_library.rb
new file mode 100644
index 0000000..b415033
--- /dev/null
+++ b/lib/ffi/dynamic_library.rb
@@ -0,0 +1,89 @@
+#
+# Copyright (C) 2008-2010 Wayne Meissner
+#
+# This file is part of ruby-ffi.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * 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
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.#
+
+module FFI
+ class DynamicLibrary
+ SEARCH_PATH = %w[/usr/lib /usr/local/lib /opt/local/lib]
+ if FFI::Platform::ARCH == 'aarch64' && FFI::Platform.mac?
+ SEARCH_PATH << '/opt/homebrew/lib'
+ end
+
+ SEARCH_PATH_MESSAGE = "Searched in <system library path>, #{SEARCH_PATH.join(', ')}".freeze
+
+ def self.load_library(name, flags)
+ if name == FFI::CURRENT_PROCESS
+ FFI::DynamicLibrary.open(nil, RTLD_LAZY | RTLD_LOCAL)
+ else
+ flags ||= RTLD_LAZY | RTLD_LOCAL
+
+ libnames = (name.is_a?(::Array) ? name : [name])
+ libnames = libnames.map(&:to_s).map { |n| [n, FFI.map_library_name(n)].uniq }.flatten.compact
+ errors = []
+
+ libnames.each do |libname|
+ lib = try_load(libname, flags, errors)
+ return lib if lib
+
+ unless libname.start_with?("/") || FFI::Platform.windows?
+ SEARCH_PATH.each do |prefix|
+ path = "#{prefix}/#{libname}"
+ if File.exist?(path)
+ lib = try_load(path, flags, errors)
+ return lib if lib
+ end
+ end
+ end
+ end
+
+ raise LoadError, [*errors, SEARCH_PATH_MESSAGE].join(".\n")
+ end
+ end
+ private_class_method :load_library
+
+ def self.try_load(libname, flags, errors)
+ begin
+ lib = FFI::DynamicLibrary.open(libname, flags)
+ return lib if lib
+
+ # LoadError for C ext & JRuby, RuntimeError for TruffleRuby
+ rescue LoadError, RuntimeError => ex
+ if ex.message =~ /(([^ \t()])+\.so([^ \t:()])*):([ \t])*(invalid ELF header|file too short|invalid file format)/
+ if File.binread($1) =~ /(?:GROUP|INPUT) *\( *([^ \)]+)/
+ return try_load($1, flags, errors)
+ end
+ end
+
+ errors << ex
+ nil
+ end
+ end
+ private_class_method :try_load
+ end
+end
diff --git a/lib/ffi/ffi.rb b/lib/ffi/ffi.rb
index dfffa8c..bc1b2e6 100644
--- a/lib/ffi/ffi.rb
+++ b/lib/ffi/ffi.rb
@@ -28,6 +28,7 @@
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+require 'ffi/compat'
require 'ffi/platform'
require 'ffi/data_converter'
require 'ffi/types'
@@ -45,3 +46,4 @@ require 'ffi/autopointer'
require 'ffi/variadic'
require 'ffi/enum'
require 'ffi/version'
+require 'ffi/function'
diff --git a/lib/ffi/function.rb b/lib/ffi/function.rb
new file mode 100644
index 0000000..ac4daf0
--- /dev/null
+++ b/lib/ffi/function.rb
@@ -0,0 +1,71 @@
+#
+# Copyright (C) 2008-2010 JRuby project
+#
+# This file is part of ruby-ffi.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright notice
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# * 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
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+module FFI
+ class Function
+ # Only MRI allows function type queries
+ if private_method_defined?(:type)
+ # Retrieve the return type of the function
+ #
+ # This method returns FFI type returned by the function.
+ #
+ # @return [FFI::Type]
+ def return_type
+ type.return_type
+ end
+
+ # Retrieve Array of parameter types
+ #
+ # This method returns an Array of FFI types accepted as function parameters.
+ #
+ # @return [Array<FFI::Type>]
+ def param_types
+ type.param_types
+ end
+ end
+
+ # Stash the Function in a module variable so it can be inspected by attached_functions.
+ # On CRuby it also ensures that it does not get garbage collected.
+ module RegisterAttach
+ def attach(mod, name)
+ funcs = mod.instance_variable_get("@ffi_functions")
+ unless funcs
+ funcs = {}
+ mod.instance_variable_set("@ffi_functions", funcs)
+ end
+ funcs[name.to_sym] = self
+ # Jump to the native attach method of CRuby, JRuby or Tuffleruby
+ super
+ end
+ end
+ private_constant :RegisterAttach
+ prepend RegisterAttach
+ end
+end
diff --git a/lib/ffi/library.rb b/lib/ffi/library.rb
index c2628ab..2556975 100644
--- a/lib/ffi/library.rb
+++ b/lib/ffi/library.rb
@@ -28,8 +28,10 @@
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.#
+require 'ffi/dynamic_library'
+
module FFI
- CURRENT_PROCESS = USE_THIS_PROCESS_AS_LIBRARY = Object.new
+ CURRENT_PROCESS = USE_THIS_PROCESS_AS_LIBRARY = FFI.make_shareable(Object.new)
class LibraryPath < ::Struct.new(:name, :abi_number, :root)
PATTERN = /(#{Platform::LIBPREFIX})?(?<name>.*?)(\.|\z)/
@@ -134,62 +136,11 @@ module FFI
def ffi_lib(*names)
raise LoadError.new("library names list must not be empty") if names.empty?
- lib_flags = defined?(@ffi_lib_flags) ? @ffi_lib_flags : FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_LOCAL
- ffi_libs = names.map do |name|
-
- if name == FFI::CURRENT_PROCESS
- FFI::DynamicLibrary.open(nil, FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_LOCAL)
-
- else
- libnames = (name.is_a?(::Array) ? name : [ name ]).map(&:to_s).map { |n| [ n, FFI.map_library_name(n) ].uniq }.flatten.compact
- lib = nil
- errors = {}
-
- libnames.each do |libname|
- begin
- orig = libname
- lib = FFI::DynamicLibrary.open(libname, lib_flags)
- break if lib
-
- rescue Exception => ex
- ldscript = false
- if ex.message =~ /(([^ \t()])+\.so([^ \t:()])*):([ \t])*(invalid ELF header|file too short|invalid file format)/
- if File.binread($1) =~ /(?:GROUP|INPUT) *\( *([^ \)]+)/
- libname = $1
- ldscript = true
- end
- end
-
- if ldscript
- retry
- else
- # TODO better library lookup logic
- unless libname.start_with?("/") || FFI::Platform.windows?
- path = ['/usr/lib/','/usr/local/lib/','/opt/local/lib/', '/opt/homebrew/lib/'].find do |pth|
- File.exist?(pth + libname)
- end
- if path
- libname = path + libname
- retry
- end
- end
-
- libr = (orig == libname ? orig : "#{orig} #{libname}")
- errors[libr] = ex
- end
- end
- end
-
- if lib.nil?
- raise LoadError.new(errors.values.join(".\n"))
- end
+ lib_flags = defined?(@ffi_lib_flags) && @ffi_lib_flags
- # return the found lib
- lib
- end
+ @ffi_libs = names.map do |name|
+ FFI::DynamicLibrary.send(:load_library, name, lib_flags)
end
-
- @ffi_libs = ffi_libs
end
# Set the calling convention for {#attach_function} and {#callback}
@@ -297,7 +248,7 @@ module FFI
end
raise LoadError unless function
- invokers << if arg_types.length > 0 && arg_types[arg_types.length - 1] == FFI::NativeType::VARARGS
+ invokers << if arg_types[-1] == FFI::NativeType::VARARGS
VariadicInvoker.new(function, arg_types, find_type(ret_type), options)
else
@@ -369,6 +320,7 @@ module FFI
# Attach C variable +cname+ to this module.
def attach_variable(mname, a1, a2 = nil)
cname, type = a2 ? [ a1, a2 ] : [ mname.to_s, a1 ]
+ mname = mname.to_sym
address = nil
ffi_libraries.each do |lib|
begin
@@ -383,9 +335,10 @@ module FFI
# If it is a global struct, just attach directly to the pointer
s = s = type.new(address) # Assigning twice to suppress unused variable warning
self.module_eval <<-code, __FILE__, __LINE__
- @@ffi_gvar_#{mname} = s
+ @ffi_gsvars = {} unless defined?(@ffi_gsvars)
+ @ffi_gsvars[#{mname.inspect}] = s
def self.#{mname}
- @@ffi_gvar_#{mname}
+ @ffi_gsvars[#{mname.inspect}]
end
code
@@ -397,12 +350,13 @@ module FFI
# Attach to this module as mname/mname=
#
self.module_eval <<-code, __FILE__, __LINE__
- @@ffi_gvar_#{mname} = s
+ @ffi_gvars = {} unless defined?(@ffi_gvars)
+ @ffi_gvars[#{mname.inspect}] = s
def self.#{mname}
- @@ffi_gvar_#{mname}[:gvar]
+ @ffi_gvars[#{mname.inspect}][:gvar]
end
def self.#{mname}=(value)
- @@ffi_gvar_#{mname}[:gvar] = value
+ @ffi_gvars[#{mname.inspect}][:gvar] = value
end
code
@@ -627,5 +581,43 @@ module FFI
end || FFI.find_type(t)
end
+
+ # Retrieve all attached functions and their function signature
+ #
+ # This method returns a Hash of method names of attached functions connected by #attach_function and the corresponding function type.
+ # The function type responds to #return_type and #param_types which return the FFI types of the function signature.
+ #
+ # @return [Hash< Symbol => [FFI::Function, FFI::VariadicInvoker] >]
+ def attached_functions
+ @ffi_functions || {}
+ end
+
+ # Retrieve all attached variables and their type
+ #
+ # This method returns a Hash of variable names and the corresponding type or variables connected by #attach_variable .
+ #
+ # @return [Hash< Symbol => ffi_type >]
+ def attached_variables
+ (
+ (@ffi_gsvars || {}).map do |name, gvar|
+ [name, gvar.class]
+ end +
+ (@ffi_gvars || {}).map do |name, gvar|
+ [name, gvar.layout[:gvar].type]
+ end
+ ).to_h
+ end
+
+ # Freeze all definitions of the module
+ #
+ # This freezes the module's definitions, so that it can be used in a Ractor.
+ # No further methods or variables can be attached and no further enums or typedefs can be created in this module afterwards.
+ def freeze
+ instance_variables.each do |name|
+ var = instance_variable_get(name)
+ FFI.make_shareable(var)
+ end
+ nil
+ end
end
end
diff --git a/lib/ffi/managedstruct.rb b/lib/ffi/managedstruct.rb
index b5ec8a3..5d243e5 100644
--- a/lib/ffi/managedstruct.rb
+++ b/lib/ffi/managedstruct.rb
@@ -75,7 +75,7 @@ module FFI
# @overload initialize
# A new instance of FFI::ManagedStruct.
def initialize(pointer=nil)
- raise NoMethodError, "release() not implemented for class #{self}" unless self.class.respond_to? :release
+ raise NoMethodError, "release() not implemented for class #{self}" unless self.class.respond_to?(:release, true)
raise ArgumentError, "Must supply a pointer to memory for the Struct" unless pointer
super AutoPointer.new(pointer, self.class.method(:release))
end
diff --git a/lib/ffi/platform.rb b/lib/ffi/platform.rb
index bf01a27..5ac4dd7 100644
--- a/lib/ffi/platform.rb
+++ b/lib/ffi/platform.rb
@@ -29,13 +29,15 @@
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.#
require 'rbconfig'
+require_relative 'compat'
+
module FFI
class PlatformError < LoadError; end
# This module defines different constants and class methods to play with
# various platforms.
module Platform
- OS = case RbConfig::CONFIG['host_os'].downcase
+ OS = FFI.make_shareable(case RbConfig::CONFIG['host_os'].downcase
when /linux/
"linux"
when /darwin/
@@ -54,13 +56,13 @@ module FFI
"windows"
else
RbConfig::CONFIG['host_os'].downcase
- end
+ end)
OSVERSION = RbConfig::CONFIG['host_os'].gsub(/[^\d]/, '').to_i
- CPU = RbConfig::CONFIG['host_cpu']
+ CPU = FFI.make_shareable(RbConfig::CONFIG['host_cpu'])
- ARCH = case CPU.downcase
+ ARCH = FFI.make_shareable(case CPU.downcase
when /amd64|x86_64|x64/
"x86_64"
when /i\d86|x86|i86pc/
@@ -81,7 +83,7 @@ module FFI
end
else
RbConfig::CONFIG['host_cpu']
- end
+ end)
private
# @param [String) os
@@ -105,21 +107,21 @@ module FFI
# Add the version for known ABI breaks
name_version = "12" if IS_FREEBSD && OSVERSION >= 12 # 64-bit inodes
- NAME = "#{ARCH}-#{OS}#{name_version}"
- CONF_DIR = File.join(File.dirname(__FILE__), 'platform', NAME)
+ NAME = FFI.make_shareable("#{ARCH}-#{OS}#{name_version}")
+ CONF_DIR = FFI.make_shareable(File.join(File.dirname(__FILE__), 'platform', NAME))
public
- LIBPREFIX = case OS
+ LIBPREFIX = FFI.make_shareable(case OS
when /windows|msys/
''
when /cygwin/
'cyg'
else
'lib'
- end
+ end)
- LIBSUFFIX = case OS
+ LIBSUFFIX = FFI.make_shareable(case OS
when /darwin/
'dylib'
when /linux|bsd|solaris/
@@ -129,9 +131,9 @@ module FFI
else
# Punt and just assume a sane unix (i.e. anything but AIX)
'so'
- end
+ end)
- LIBC = if IS_WINDOWS
+ LIBC = FFI.make_shareable(if IS_WINDOWS
crtname = RbConfig::CONFIG["RUBY_SO_NAME"][/msvc\w+/] || 'ucrtbase'
"#{crtname}.dll"
elsif IS_GNU
@@ -143,7 +145,7 @@ module FFI
"msys-2.0.dll"
else
"#{LIBPREFIX}c.#{LIBSUFFIX}"
- end
+ end)
LITTLE_ENDIAN = 1234 unless defined?(LITTLE_ENDIAN)
BIG_ENDIAN = 4321 unless defined?(BIG_ENDIAN)
diff --git a/lib/ffi/platform/hppa1.1-linux/types.conf b/lib/ffi/platform/hppa1.1-linux/types.conf
new file mode 100644
index 0000000..c8a8141
--- /dev/null
+++ b/lib/ffi/platform/hppa1.1-linux/types.conf
@@ -0,0 +1,178 @@
+rbx.platform.typedef.*__caddr_t = char
+rbx.platform.typedef.__blkcnt64_t = long_long
+rbx.platform.typedef.__blkcnt_t = long
+rbx.platform.typedef.__blksize_t = long
+rbx.platform.typedef.__clock_t = long
+rbx.platform.typedef.__clockid_t = int
+rbx.platform.typedef.__daddr_t = int
+rbx.platform.typedef.__dev_t = ulong_long
+rbx.platform.typedef.__fd_mask = long
+rbx.platform.typedef.__fsblkcnt64_t = ulong_long
+rbx.platform.typedef.__fsblkcnt_t = ulong
+rbx.platform.typedef.__fsfilcnt64_t = ulong_long
+rbx.platform.typedef.__fsfilcnt_t = ulong
+rbx.platform.typedef.__fsword_t = int
+rbx.platform.typedef.__gid_t = uint
+rbx.platform.typedef.__id_t = uint
+rbx.platform.typedef.__ino64_t = ulong_long
+rbx.platform.typedef.__ino_t = ulong
+rbx.platform.typedef.__int16_t = short
+rbx.platform.typedef.__int32_t = int
+rbx.platform.typedef.__int64_t = long_long
+rbx.platform.typedef.__int8_t = char
+rbx.platform.typedef.__int_least16_t = short
+rbx.platform.typedef.__int_least32_t = int
+rbx.platform.typedef.__int_least64_t = long_long
+rbx.platform.typedef.__int_least8_t = char
+rbx.platform.typedef.__intmax_t = long_long
+rbx.platform.typedef.__intptr_t = int
+rbx.platform.typedef.__kernel_caddr_t = string
+rbx.platform.typedef.__kernel_clock_t = long
+rbx.platform.typedef.__kernel_clockid_t = int
+rbx.platform.typedef.__kernel_daddr_t = int
+rbx.platform.typedef.__kernel_gid16_t = ushort
+rbx.platform.typedef.__kernel_gid32_t = uint
+rbx.platform.typedef.__kernel_gid_t = uint
+rbx.platform.typedef.__kernel_ino64_t = ulong_long
+rbx.platform.typedef.__kernel_ino_t = ulong
+rbx.platform.typedef.__kernel_ipc_pid_t = ushort
+rbx.platform.typedef.__kernel_key_t = int
+rbx.platform.typedef.__kernel_loff_t = long_long
+rbx.platform.typedef.__kernel_long_t = long
+rbx.platform.typedef.__kernel_mode_t = ushort
+rbx.platform.typedef.__kernel_mqd_t = int
+rbx.platform.typedef.__kernel_off64_t = long_long
+rbx.platform.typedef.__kernel_off_t = long
+rbx.platform.typedef.__kernel_old_dev_t = uint
+rbx.platform.typedef.__kernel_old_gid_t = uint
+rbx.platform.typedef.__kernel_old_time_t = long
+rbx.platform.typedef.__kernel_old_uid_t = uint
+rbx.platform.typedef.__kernel_pid_t = int
+rbx.platform.typedef.__kernel_ptrdiff_t = int
+rbx.platform.typedef.__kernel_size_t = uint
+rbx.platform.typedef.__kernel_ssize_t = int
+rbx.platform.typedef.__kernel_suseconds_t = long
+rbx.platform.typedef.__kernel_time64_t = long_long
+rbx.platform.typedef.__kernel_time_t = long
+rbx.platform.typedef.__kernel_timer_t = int
+rbx.platform.typedef.__kernel_uid16_t = ushort
+rbx.platform.typedef.__kernel_uid32_t = uint
+rbx.platform.typedef.__kernel_uid_t = uint
+rbx.platform.typedef.__kernel_ulong_t = ulong
+rbx.platform.typedef.__key_t = int
+rbx.platform.typedef.__loff_t = long_long
+rbx.platform.typedef.__mode_t = uint
+rbx.platform.typedef.__nlink_t = uint
+rbx.platform.typedef.__off64_t = long_long
+rbx.platform.typedef.__off_t = long
+rbx.platform.typedef.__pid_t = int
+rbx.platform.typedef.__priority_which_t = int
+rbx.platform.typedef.__quad_t = long_long
+rbx.platform.typedef.__rlim64_t = ulong_long
+rbx.platform.typedef.__rlim_t = ulong
+rbx.platform.typedef.__rlimit_resource_t = int
+rbx.platform.typedef.__rusage_who_t = int
+rbx.platform.typedef.__sig_atomic_t = int
+rbx.platform.typedef.__socklen_t = uint
+rbx.platform.typedef.__ssize_t = int
+rbx.platform.typedef.__suseconds64_t = long_long
+rbx.platform.typedef.__suseconds_t = long
+rbx.platform.typedef.__syscall_slong_t = long
+rbx.platform.typedef.__syscall_ulong_t = ulong
+rbx.platform.typedef.__thrd_t = ulong
+rbx.platform.typedef.__time64_t = long_long
+rbx.platform.typedef.__time_t = long
+rbx.platform.typedef.__timer_t = pointer
+rbx.platform.typedef.__tss_t = uint
+rbx.platform.typedef.__u_char = uchar
+rbx.platform.typedef.__u_int = uint
+rbx.platform.typedef.__u_long = ulong
+rbx.platform.typedef.__u_quad_t = ulong_long
+rbx.platform.typedef.__u_short = ushort
+rbx.platform.typedef.__uid_t = uint
+rbx.platform.typedef.__uint16_t = ushort
+rbx.platform.typedef.__uint32_t = uint
+rbx.platform.typedef.__uint64_t = ulong_long
+rbx.platform.typedef.__uint8_t = uchar
+rbx.platform.typedef.__uint_least16_t = ushort
+rbx.platform.typedef.__uint_least32_t = uint
+rbx.platform.typedef.__uint_least64_t = ulong_long
+rbx.platform.typedef.__uint_least8_t = uchar
+rbx.platform.typedef.__uintmax_t = ulong_long
+rbx.platform.typedef.__useconds_t = uint
+rbx.platform.typedef.blkcnt_t = long_long
+rbx.platform.typedef.blksize_t = long
+rbx.platform.typedef.clock_t = long
+rbx.platform.typedef.clockid_t = int
+rbx.platform.typedef.daddr_t = int
+rbx.platform.typedef.dev_t = ulong_long
+rbx.platform.typedef.fd_mask = long
+rbx.platform.typedef.fsblkcnt_t = ulong_long
+rbx.platform.typedef.fsfilcnt_t = ulong_long
+rbx.platform.typedef.gid_t = uint
+rbx.platform.typedef.id_t = uint
+rbx.platform.typedef.in_addr_t = uint
+rbx.platform.typedef.in_port_t = ushort
+rbx.platform.typedef.ino_t = ulong_long
+rbx.platform.typedef.int16_t = short
+rbx.platform.typedef.int32_t = int
+rbx.platform.typedef.int64_t = long_long
+rbx.platform.typedef.int8_t = char
+rbx.platform.typedef.int_fast16_t = int
+rbx.platform.typedef.int_fast32_t = int
+rbx.platform.typedef.int_fast64_t = long_long
+rbx.platform.typedef.int_fast8_t = char
+rbx.platform.typedef.int_least16_t = short
+rbx.platform.typedef.int_least32_t = int
+rbx.platform.typedef.int_least64_t = long_long
+rbx.platform.typedef.int_least8_t = char
+rbx.platform.typedef.intmax_t = long_long
+rbx.platform.typedef.intptr_t = int
+rbx.platform.typedef.key_t = int
+rbx.platform.typedef.loff_t = long_long
+rbx.platform.typedef.mode_t = uint
+rbx.platform.typedef.nlink_t = uint
+rbx.platform.typedef.off_t = long_long
+rbx.platform.typedef.pid_t = int
+rbx.platform.typedef.pthread_key_t = uint
+rbx.platform.typedef.pthread_once_t = int
+rbx.platform.typedef.pthread_t = ulong
+rbx.platform.typedef.ptrdiff_t = int
+rbx.platform.typedef.quad_t = long_long
+rbx.platform.typedef.register_t = long
+rbx.platform.typedef.rlim_t = ulong_long
+rbx.platform.typedef.sa_family_t = ushort
+rbx.platform.typedef.size_t = uint
+rbx.platform.typedef.socklen_t = uint
+rbx.platform.typedef.ssize_t = int
+rbx.platform.typedef.suseconds_t = long
+rbx.platform.typedef.time_t = long
+rbx.platform.typedef.timer_t = pointer
+rbx.platform.typedef.u_char = uchar
+rbx.platform.typedef.u_int = uint
+rbx.platform.typedef.u_int16_t = ushort
+rbx.platform.typedef.u_int32_t = uint
+rbx.platform.typedef.u_int64_t = ulong_long
+rbx.platform.typedef.u_int8_t = uchar
+rbx.platform.typedef.u_long = ulong
+rbx.platform.typedef.u_quad_t = ulong_long
+rbx.platform.typedef.u_short = ushort
+rbx.platform.typedef.uid_t = uint
+rbx.platform.typedef.uint = uint
+rbx.platform.typedef.uint16_t = ushort
+rbx.platform.typedef.uint32_t = uint
+rbx.platform.typedef.uint64_t = ulong_long
+rbx.platform.typedef.uint8_t = uchar
+rbx.platform.typedef.uint_fast16_t = uint
+rbx.platform.typedef.uint_fast32_t = uint
+rbx.platform.typedef.uint_fast64_t = ulong_long
+rbx.platform.typedef.uint_fast8_t = uchar
+rbx.platform.typedef.uint_least16_t = ushort
+rbx.platform.typedef.uint_least32_t = uint
+rbx.platform.typedef.uint_least64_t = ulong_long
+rbx.platform.typedef.uint_least8_t = uchar
+rbx.platform.typedef.uintmax_t = ulong_long
+rbx.platform.typedef.uintptr_t = uint
+rbx.platform.typedef.ulong = ulong
+rbx.platform.typedef.ushort = ushort
+rbx.platform.typedef.wchar_t = long
diff --git a/lib/ffi/platform/hppa2.0-linux/types.conf b/lib/ffi/platform/hppa2.0-linux/types.conf
new file mode 100644
index 0000000..c8a8141
--- /dev/null
+++ b/lib/ffi/platform/hppa2.0-linux/types.conf
@@ -0,0 +1,178 @@
+rbx.platform.typedef.*__caddr_t = char
+rbx.platform.typedef.__blkcnt64_t = long_long
+rbx.platform.typedef.__blkcnt_t = long
+rbx.platform.typedef.__blksize_t = long
+rbx.platform.typedef.__clock_t = long
+rbx.platform.typedef.__clockid_t = int
+rbx.platform.typedef.__daddr_t = int
+rbx.platform.typedef.__dev_t = ulong_long
+rbx.platform.typedef.__fd_mask = long
+rbx.platform.typedef.__fsblkcnt64_t = ulong_long
+rbx.platform.typedef.__fsblkcnt_t = ulong
+rbx.platform.typedef.__fsfilcnt64_t = ulong_long
+rbx.platform.typedef.__fsfilcnt_t = ulong
+rbx.platform.typedef.__fsword_t = int
+rbx.platform.typedef.__gid_t = uint
+rbx.platform.typedef.__id_t = uint
+rbx.platform.typedef.__ino64_t = ulong_long
+rbx.platform.typedef.__ino_t = ulong
+rbx.platform.typedef.__int16_t = short
+rbx.platform.typedef.__int32_t = int
+rbx.platform.typedef.__int64_t = long_long
+rbx.platform.typedef.__int8_t = char
+rbx.platform.typedef.__int_least16_t = short
+rbx.platform.typedef.__int_least32_t = int
+rbx.platform.typedef.__int_least64_t = long_long
+rbx.platform.typedef.__int_least8_t = char
+rbx.platform.typedef.__intmax_t = long_long
+rbx.platform.typedef.__intptr_t = int
+rbx.platform.typedef.__kernel_caddr_t = string
+rbx.platform.typedef.__kernel_clock_t = long
+rbx.platform.typedef.__kernel_clockid_t = int
+rbx.platform.typedef.__kernel_daddr_t = int
+rbx.platform.typedef.__kernel_gid16_t = ushort
+rbx.platform.typedef.__kernel_gid32_t = uint
+rbx.platform.typedef.__kernel_gid_t = uint
+rbx.platform.typedef.__kernel_ino64_t = ulong_long
+rbx.platform.typedef.__kernel_ino_t = ulong
+rbx.platform.typedef.__kernel_ipc_pid_t = ushort
+rbx.platform.typedef.__kernel_key_t = int
+rbx.platform.typedef.__kernel_loff_t = long_long
+rbx.platform.typedef.__kernel_long_t = long
+rbx.platform.typedef.__kernel_mode_t = ushort
+rbx.platform.typedef.__kernel_mqd_t = int
+rbx.platform.typedef.__kernel_off64_t = long_long
+rbx.platform.typedef.__kernel_off_t = long
+rbx.platform.typedef.__kernel_old_dev_t = uint
+rbx.platform.typedef.__kernel_old_gid_t = uint
+rbx.platform.typedef.__kernel_old_time_t = long
+rbx.platform.typedef.__kernel_old_uid_t = uint
+rbx.platform.typedef.__kernel_pid_t = int
+rbx.platform.typedef.__kernel_ptrdiff_t = int
+rbx.platform.typedef.__kernel_size_t = uint
+rbx.platform.typedef.__kernel_ssize_t = int
+rbx.platform.typedef.__kernel_suseconds_t = long
+rbx.platform.typedef.__kernel_time64_t = long_long
+rbx.platform.typedef.__kernel_time_t = long
+rbx.platform.typedef.__kernel_timer_t = int
+rbx.platform.typedef.__kernel_uid16_t = ushort
+rbx.platform.typedef.__kernel_uid32_t = uint
+rbx.platform.typedef.__kernel_uid_t = uint
+rbx.platform.typedef.__kernel_ulong_t = ulong
+rbx.platform.typedef.__key_t = int
+rbx.platform.typedef.__loff_t = long_long
+rbx.platform.typedef.__mode_t = uint
+rbx.platform.typedef.__nlink_t = uint
+rbx.platform.typedef.__off64_t = long_long
+rbx.platform.typedef.__off_t = long
+rbx.platform.typedef.__pid_t = int
+rbx.platform.typedef.__priority_which_t = int
+rbx.platform.typedef.__quad_t = long_long
+rbx.platform.typedef.__rlim64_t = ulong_long
+rbx.platform.typedef.__rlim_t = ulong
+rbx.platform.typedef.__rlimit_resource_t = int
+rbx.platform.typedef.__rusage_who_t = int
+rbx.platform.typedef.__sig_atomic_t = int
+rbx.platform.typedef.__socklen_t = uint
+rbx.platform.typedef.__ssize_t = int
+rbx.platform.typedef.__suseconds64_t = long_long
+rbx.platform.typedef.__suseconds_t = long
+rbx.platform.typedef.__syscall_slong_t = long
+rbx.platform.typedef.__syscall_ulong_t = ulong
+rbx.platform.typedef.__thrd_t = ulong
+rbx.platform.typedef.__time64_t = long_long
+rbx.platform.typedef.__time_t = long
+rbx.platform.typedef.__timer_t = pointer
+rbx.platform.typedef.__tss_t = uint
+rbx.platform.typedef.__u_char = uchar
+rbx.platform.typedef.__u_int = uint
+rbx.platform.typedef.__u_long = ulong
+rbx.platform.typedef.__u_quad_t = ulong_long
+rbx.platform.typedef.__u_short = ushort
+rbx.platform.typedef.__uid_t = uint
+rbx.platform.typedef.__uint16_t = ushort
+rbx.platform.typedef.__uint32_t = uint
+rbx.platform.typedef.__uint64_t = ulong_long
+rbx.platform.typedef.__uint8_t = uchar
+rbx.platform.typedef.__uint_least16_t = ushort
+rbx.platform.typedef.__uint_least32_t = uint
+rbx.platform.typedef.__uint_least64_t = ulong_long
+rbx.platform.typedef.__uint_least8_t = uchar
+rbx.platform.typedef.__uintmax_t = ulong_long
+rbx.platform.typedef.__useconds_t = uint
+rbx.platform.typedef.blkcnt_t = long_long
+rbx.platform.typedef.blksize_t = long
+rbx.platform.typedef.clock_t = long
+rbx.platform.typedef.clockid_t = int
+rbx.platform.typedef.daddr_t = int
+rbx.platform.typedef.dev_t = ulong_long
+rbx.platform.typedef.fd_mask = long
+rbx.platform.typedef.fsblkcnt_t = ulong_long
+rbx.platform.typedef.fsfilcnt_t = ulong_long
+rbx.platform.typedef.gid_t = uint
+rbx.platform.typedef.id_t = uint
+rbx.platform.typedef.in_addr_t = uint
+rbx.platform.typedef.in_port_t = ushort
+rbx.platform.typedef.ino_t = ulong_long
+rbx.platform.typedef.int16_t = short
+rbx.platform.typedef.int32_t = int
+rbx.platform.typedef.int64_t = long_long
+rbx.platform.typedef.int8_t = char
+rbx.platform.typedef.int_fast16_t = int
+rbx.platform.typedef.int_fast32_t = int
+rbx.platform.typedef.int_fast64_t = long_long
+rbx.platform.typedef.int_fast8_t = char
+rbx.platform.typedef.int_least16_t = short
+rbx.platform.typedef.int_least32_t = int
+rbx.platform.typedef.int_least64_t = long_long
+rbx.platform.typedef.int_least8_t = char
+rbx.platform.typedef.intmax_t = long_long
+rbx.platform.typedef.intptr_t = int
+rbx.platform.typedef.key_t = int
+rbx.platform.typedef.loff_t = long_long
+rbx.platform.typedef.mode_t = uint
+rbx.platform.typedef.nlink_t = uint
+rbx.platform.typedef.off_t = long_long
+rbx.platform.typedef.pid_t = int
+rbx.platform.typedef.pthread_key_t = uint
+rbx.platform.typedef.pthread_once_t = int
+rbx.platform.typedef.pthread_t = ulong
+rbx.platform.typedef.ptrdiff_t = int
+rbx.platform.typedef.quad_t = long_long
+rbx.platform.typedef.register_t = long
+rbx.platform.typedef.rlim_t = ulong_long
+rbx.platform.typedef.sa_family_t = ushort
+rbx.platform.typedef.size_t = uint
+rbx.platform.typedef.socklen_t = uint
+rbx.platform.typedef.ssize_t = int
+rbx.platform.typedef.suseconds_t = long
+rbx.platform.typedef.time_t = long
+rbx.platform.typedef.timer_t = pointer
+rbx.platform.typedef.u_char = uchar
+rbx.platform.typedef.u_int = uint
+rbx.platform.typedef.u_int16_t = ushort
+rbx.platform.typedef.u_int32_t = uint
+rbx.platform.typedef.u_int64_t = ulong_long
+rbx.platform.typedef.u_int8_t = uchar
+rbx.platform.typedef.u_long = ulong
+rbx.platform.typedef.u_quad_t = ulong_long
+rbx.platform.typedef.u_short = ushort
+rbx.platform.typedef.uid_t = uint
+rbx.platform.typedef.uint = uint
+rbx.platform.typedef.uint16_t = ushort
+rbx.platform.typedef.uint32_t = uint
+rbx.platform.typedef.uint64_t = ulong_long
+rbx.platform.typedef.uint8_t = uchar
+rbx.platform.typedef.uint_fast16_t = uint
+rbx.platform.typedef.uint_fast32_t = uint
+rbx.platform.typedef.uint_fast64_t = ulong_long
+rbx.platform.typedef.uint_fast8_t = uchar
+rbx.platform.typedef.uint_least16_t = ushort
+rbx.platform.typedef.uint_least32_t = uint
+rbx.platform.typedef.uint_least64_t = ulong_long
+rbx.platform.typedef.uint_least8_t = uchar
+rbx.platform.typedef.uintmax_t = ulong_long
+rbx.platform.typedef.uintptr_t = uint
+rbx.platform.typedef.ulong = ulong
+rbx.platform.typedef.ushort = ushort
+rbx.platform.typedef.wchar_t = long
diff --git a/lib/ffi/platform/sparc64-linux/types.conf b/lib/ffi/platform/sparcv9-linux/types.conf
index 7626bfc..7626bfc 100644
--- a/lib/ffi/platform/sparc64-linux/types.conf
+++ b/lib/ffi/platform/sparcv9-linux/types.conf
diff --git a/lib/ffi/platform/sw_64-linux/types.conf b/lib/ffi/platform/sw_64-linux/types.conf
new file mode 100644
index 0000000..aa8716d
--- /dev/null
+++ b/lib/ffi/platform/sw_64-linux/types.conf
@@ -0,0 +1,141 @@
+rbx.platform.typedef.*__caddr_t = char
+rbx.platform.typedef.__blkcnt64_t = long
+rbx.platform.typedef.__blkcnt_t = long
+rbx.platform.typedef.__blksize_t = int
+rbx.platform.typedef.__clock_t = long
+rbx.platform.typedef.__clockid_t = int
+rbx.platform.typedef.__daddr_t = int
+rbx.platform.typedef.__dev_t = ulong
+rbx.platform.typedef.__fd_mask = long
+rbx.platform.typedef.__fsblkcnt64_t = ulong
+rbx.platform.typedef.__fsblkcnt_t = ulong
+rbx.platform.typedef.__fsfilcnt64_t = ulong
+rbx.platform.typedef.__fsfilcnt_t = ulong
+rbx.platform.typedef.__fsword_t = long
+rbx.platform.typedef.__gid_t = uint
+rbx.platform.typedef.__id_t = uint
+rbx.platform.typedef.__ino64_t = ulong
+rbx.platform.typedef.__ino_t = ulong
+rbx.platform.typedef.__int16_t = short
+rbx.platform.typedef.__int32_t = int
+rbx.platform.typedef.__int64_t = long
+rbx.platform.typedef.__int8_t = char
+rbx.platform.typedef.__int_least16_t = short
+rbx.platform.typedef.__int_least32_t = int
+rbx.platform.typedef.__int_least64_t = long
+rbx.platform.typedef.__int_least8_t = char
+rbx.platform.typedef.__intmax_t = long
+rbx.platform.typedef.__intptr_t = long
+rbx.platform.typedef.__key_t = int
+rbx.platform.typedef.__loff_t = long
+rbx.platform.typedef.__mode_t = uint
+rbx.platform.typedef.__nlink_t = uint
+rbx.platform.typedef.__off64_t = long
+rbx.platform.typedef.__off_t = long
+rbx.platform.typedef.__pid_t = int
+rbx.platform.typedef.__priority_which_t = int
+rbx.platform.typedef.__quad_t = long
+rbx.platform.typedef.__rlim64_t = ulong
+rbx.platform.typedef.__rlim_t = ulong
+rbx.platform.typedef.__rlimit_resource_t = int
+rbx.platform.typedef.__rusage_who_t = int
+rbx.platform.typedef.__sig_atomic_t = int
+rbx.platform.typedef.__socklen_t = uint
+rbx.platform.typedef.__ssize_t = long
+rbx.platform.typedef.__suseconds_t = long
+rbx.platform.typedef.__syscall_slong_t = long
+rbx.platform.typedef.__syscall_ulong_t = ulong
+rbx.platform.typedef.__time_t = long
+rbx.platform.typedef.__timer_t = pointer
+rbx.platform.typedef.__u_char = uchar
+rbx.platform.typedef.__u_int = uint
+rbx.platform.typedef.__u_long = ulong
+rbx.platform.typedef.__u_quad_t = ulong
+rbx.platform.typedef.__u_short = ushort
+rbx.platform.typedef.__uid_t = uint
+rbx.platform.typedef.__uint16_t = ushort
+rbx.platform.typedef.__uint32_t = uint
+rbx.platform.typedef.__uint64_t = ulong
+rbx.platform.typedef.__uint8_t = uchar
+rbx.platform.typedef.__uint_least16_t = ushort
+rbx.platform.typedef.__uint_least32_t = uint
+rbx.platform.typedef.__uint_least64_t = ulong
+rbx.platform.typedef.__uint_least8_t = uchar
+rbx.platform.typedef.__uintmax_t = ulong
+rbx.platform.typedef.__useconds_t = uint
+rbx.platform.typedef.blkcnt_t = long
+rbx.platform.typedef.blksize_t = int
+rbx.platform.typedef.clock_t = long
+rbx.platform.typedef.clockid_t = int
+rbx.platform.typedef.daddr_t = int
+rbx.platform.typedef.dev_t = ulong
+rbx.platform.typedef.fd_mask = long
+rbx.platform.typedef.fsblkcnt_t = ulong
+rbx.platform.typedef.fsfilcnt_t = ulong
+rbx.platform.typedef.gid_t = uint
+rbx.platform.typedef.id_t = uint
+rbx.platform.typedef.in_addr_t = uint
+rbx.platform.typedef.in_port_t = ushort
+rbx.platform.typedef.ino_t = ulong
+rbx.platform.typedef.int16_t = short
+rbx.platform.typedef.int32_t = int
+rbx.platform.typedef.int64_t = long
+rbx.platform.typedef.int8_t = char
+rbx.platform.typedef.int_fast16_t = long
+rbx.platform.typedef.int_fast32_t = long
+rbx.platform.typedef.int_fast64_t = long
+rbx.platform.typedef.int_fast8_t = char
+rbx.platform.typedef.int_least16_t = short
+rbx.platform.typedef.int_least32_t = int
+rbx.platform.typedef.int_least64_t = long
+rbx.platform.typedef.int_least8_t = char
+rbx.platform.typedef.intmax_t = long
+rbx.platform.typedef.intptr_t = long
+rbx.platform.typedef.key_t = int
+rbx.platform.typedef.loff_t = long
+rbx.platform.typedef.mode_t = uint
+rbx.platform.typedef.nlink_t = uint
+rbx.platform.typedef.off_t = long
+rbx.platform.typedef.pid_t = int
+rbx.platform.typedef.pthread_key_t = uint
+rbx.platform.typedef.pthread_once_t = int
+rbx.platform.typedef.pthread_t = ulong
+rbx.platform.typedef.ptrdiff_t = long
+rbx.platform.typedef.quad_t = long
+rbx.platform.typedef.register_t = long
+rbx.platform.typedef.rlim_t = ulong
+rbx.platform.typedef.sa_family_t = ushort
+rbx.platform.typedef.size_t = ulong
+rbx.platform.typedef.socklen_t = uint
+rbx.platform.typedef.ssize_t = long
+rbx.platform.typedef.suseconds_t = long
+rbx.platform.typedef.time_t = long
+rbx.platform.typedef.timer_t = pointer
+rbx.platform.typedef.u_char = uchar
+rbx.platform.typedef.u_int = uint
+rbx.platform.typedef.u_int16_t = ushort
+rbx.platform.typedef.u_int32_t = uint
+rbx.platform.typedef.u_int64_t = ulong_long
+rbx.platform.typedef.u_int8_t = uchar
+rbx.platform.typedef.u_long = ulong
+rbx.platform.typedef.u_quad_t = ulong
+rbx.platform.typedef.u_short = ushort
+rbx.platform.typedef.uid_t = uint
+rbx.platform.typedef.uint = uint
+rbx.platform.typedef.uint16_t = ushort
+rbx.platform.typedef.uint32_t = uint
+rbx.platform.typedef.uint64_t = ulong
+rbx.platform.typedef.uint8_t = uchar
+rbx.platform.typedef.uint_fast16_t = ulong
+rbx.platform.typedef.uint_fast32_t = ulong
+rbx.platform.typedef.uint_fast64_t = ulong
+rbx.platform.typedef.uint_fast8_t = uchar
+rbx.platform.typedef.uint_least16_t = ushort
+rbx.platform.typedef.uint_least32_t = uint
+rbx.platform.typedef.uint_least64_t = ulong
+rbx.platform.typedef.uint_least8_t = uchar
+rbx.platform.typedef.uintmax_t = ulong
+rbx.platform.typedef.uintptr_t = ulong
+rbx.platform.typedef.ulong = ulong
+rbx.platform.typedef.ushort = ushort
+rbx.platform.typedef.wchar_t = int
diff --git a/lib/ffi/struct.rb b/lib/ffi/struct.rb
index 7028258..725b9cb 100644
--- a/lib/ffi/struct.rb
+++ b/lib/ffi/struct.rb
@@ -203,9 +203,10 @@ module FFI
# :field3, :string
# end
def layout(*spec)
- warn "[DEPRECATION] Struct layout is already defined for class #{self.inspect}. Redefinition as in #{caller[0]} will be disallowed in ffi-2.0." if defined?(@layout)
return @layout if spec.size == 0
+ warn "[DEPRECATION] Struct layout is already defined for class #{self.inspect}. Redefinition as in #{caller[0]} will be disallowed in ffi-2.0." if defined?(@layout)
+
builder = StructLayoutBuilder.new
builder.union = self < Union
builder.packed = @packed if defined?(@packed)
diff --git a/lib/ffi/struct_layout.rb b/lib/ffi/struct_layout.rb
index d5a78a7..3fd68cb 100644
--- a/lib/ffi/struct_layout.rb
+++ b/lib/ffi/struct_layout.rb
@@ -80,8 +80,8 @@ module FFI
class Mapped < Field
def initialize(name, offset, type, orig_field)
- super(name, offset, type)
@orig_field = orig_field
+ super(name, offset, type)
end
def get(ptr)
diff --git a/lib/ffi/struct_layout_builder.rb b/lib/ffi/struct_layout_builder.rb
index 4d6a464..d7d26a2 100644
--- a/lib/ffi/struct_layout_builder.rb
+++ b/lib/ffi/struct_layout_builder.rb
@@ -112,7 +112,7 @@ module FFI
Type::FLOAT64,
Type::LONGDOUBLE,
Type::BOOL,
- ]
+ ].freeze
# @param [String, Symbol] name name of the field
# @param [Array, DataConverter, Struct, StructLayout::Field, Symbol, Type] type type of the field
diff --git a/lib/ffi/types.rb b/lib/ffi/types.rb
index 90f50c1..8f5d897 100644
--- a/lib/ffi/types.rb
+++ b/lib/ffi/types.rb
@@ -33,12 +33,24 @@
# see {file:README}
module FFI
+ unless defined?(self.custom_typedefs)
+ # Truffleruby and JRuby don't support Ractor so far.
+ # So they don't need separation between builtin and custom types.
+ def self.custom_typedefs
+ TypeDefs
+ end
+ writable_typemap = true
+ end
+
# @param [Type, DataConverter, Symbol] old type definition used by {FFI.find_type}
# @param [Symbol] add new type definition's name to add
# @return [Type]
# Add a definition type to type definitions.
+ #
+ # The type definition is local per Ractor.
def self.typedef(old, add)
- TypeDefs[add] = self.find_type(old)
+ tm = custom_typedefs
+ tm[add] = self.find_type(old)
end
# (see FFI.typedef)
@@ -46,6 +58,14 @@ module FFI
typedef old, add
end
+ class << self
+ private def __typedef(old, add)
+ TypeDefs[add] = self.find_type(old)
+ end
+
+ private :custom_typedefs
+ end
+
# @param [Type, DataConverter, Symbol] name
# @param [Hash] type_map if nil, {FFI::TypeDefs} is used
@@ -57,9 +77,12 @@ module FFI
if name.is_a?(Type)
name
- elsif type_map && type_map.has_key?(name)
+ elsif type_map&.has_key?(name)
type_map[name]
+ elsif (tm=custom_typedefs).has_key?(name)
+ tm[name]
+
elsif TypeDefs.has_key?(name)
TypeDefs[name]
@@ -168,7 +191,7 @@ module FFI
end
end
- typedef(StrPtrConverter, :strptr)
+ __typedef(StrPtrConverter, :strptr)
# @param type +type+ is an instance of class accepted by {FFI.find_type}
# @return [Numeric]
@@ -184,11 +207,13 @@ module FFI
f.each_line { |line|
if line.index(prefix) == 0
new_type, orig_type = line.chomp.slice(prefix.length..-1).split(/\s*=\s*/)
- typedef(orig_type.to_sym, new_type.to_sym)
+ __typedef(orig_type.to_sym, new_type.to_sym)
end
}
end
- typedef :pointer, :caddr_t
+ __typedef :pointer, :caddr_t
rescue Errno::ENOENT
end
+
+ FFI.make_shareable(TypeDefs) unless writable_typemap
end
diff --git a/lib/ffi/variadic.rb b/lib/ffi/variadic.rb
index 743ce7f..ee33409 100644
--- a/lib/ffi/variadic.rb
+++ b/lib/ffi/variadic.rb
@@ -54,16 +54,27 @@ module FFI
invoker = self
params = "*args"
call = "call"
- mod.module_eval <<-code
- @@#{mname} = invoker
- def self.#{mname}(#{params})
- @@#{mname}.#{call}(#{params})
- end
- def #{mname}(#{params})
- @@#{mname}.#{call}(#{params})
- end
+ mname = mname.to_sym
+ mod.module_eval <<-code, __FILE__, __LINE__
+ @ffi_functions = {} unless defined?(@ffi_functions)
+ @ffi_functions[#{mname.inspect}] = invoker
+
+ def self.#{mname}(#{params})
+ @ffi_functions[#{mname.inspect}].#{call}(#{params})
+ end
+
+ define_method(#{mname.inspect}, &method(#{mname.inspect}))
code
invoker
end
+
+ # Retrieve Array of parameter types
+ #
+ # This method returns an Array of FFI types accepted as function parameters.
+ #
+ # @return [Array<FFI::Type>]
+ def param_types
+ [*@fixed, Type::Builtin::VARARGS]
+ end
end
end
diff --git a/spec/ffi/async_callback_spec.rb b/spec/ffi/async_callback_spec.rb
index 51a4886..7bb9f70 100644
--- a/spec/ffi/async_callback_spec.rb
+++ b/spec/ffi/async_callback_spec.rb
@@ -50,4 +50,27 @@ describe "async callback" do
expect(callback_runner_thread.name).to eq("FFI Callback Runner")
end
+
+ it "works in Ractor", :ractor do
+ skip "not yet supported on TruffleRuby" if RUBY_ENGINE == "truffleruby"
+
+ res = Ractor.new do
+ v = 0xdeadbeef
+ correct_ractor = false
+ correct_thread = false
+ thread = Thread.current
+ rac = Ractor.current
+ cb = Proc.new do |i|
+ v = i
+ correct_ractor = rac == Ractor.current
+ correct_thread = thread != Thread.current
+ end
+ LibTest.testAsyncCallback(cb, 0x7fffffff)
+
+ [v, correct_ractor, correct_thread]
+ end.take
+
+ expect(res).to eq([0x7fffffff, true, true])
+ end
+
end
diff --git a/spec/ffi/buffer_spec.rb b/spec/ffi/buffer_spec.rb
index 619cb6b..e031f7c 100644
--- a/spec/ffi/buffer_spec.rb
+++ b/spec/ffi/buffer_spec.rb
@@ -285,3 +285,13 @@ describe "Buffer#initialize" do
expect(block_executed).to be true
end
end
+
+describe "Buffer#memsize_of" do
+ it "has a memsize function", skip: RUBY_ENGINE != "ruby" do
+ base_size = ObjectSpace.memsize_of(Object.new)
+
+ buf = FFI::Buffer.new 14
+ size = ObjectSpace.memsize_of(buf)
+ expect(size).to be > base_size
+ end
+end
diff --git a/spec/ffi/dynamic_library_spec.rb b/spec/ffi/dynamic_library_spec.rb
new file mode 100644
index 0000000..088a822
--- /dev/null
+++ b/spec/ffi/dynamic_library_spec.rb
@@ -0,0 +1,60 @@
+#
+# This file is part of ruby-ffi.
+# For licensing, see LICENSE.SPECS
+#
+
+require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
+
+describe FFI::DynamicLibrary do
+ it "should be shareable for Ractor", :ractor do
+ libtest = FFI::DynamicLibrary.open(TestLibrary::PATH,
+ FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL)
+
+ res = Ractor.new(libtest) do |libtest2|
+ libtest2.find_symbol("testClosureVrV").address
+ end.take
+
+ expect( res ).to be > 0
+ end
+
+ it "load a library in a Ractor", :ractor do
+ res = Ractor.new do
+ libtest = FFI::DynamicLibrary.open(TestLibrary::PATH,
+ FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL)
+ libtest.find_symbol("testClosureVrV")
+ end.take
+
+ expect(res.address).to be > 0
+ end
+
+ it "has a memsize function", skip: RUBY_ENGINE != "ruby" do
+ base_size = ObjectSpace.memsize_of(Object.new)
+
+ libtest = FFI::DynamicLibrary.open(TestLibrary::PATH,
+ FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL)
+ size = ObjectSpace.memsize_of(libtest)
+ expect(size).to be > base_size
+ end
+
+ describe Symbol do
+ before do
+ @libtest = FFI::DynamicLibrary.open(
+ TestLibrary::PATH,
+ FFI::DynamicLibrary::RTLD_LAZY | FFI::DynamicLibrary::RTLD_GLOBAL,
+ )
+ end
+
+ it "has a memsize function", skip: RUBY_ENGINE != "ruby" do
+ base_size = ObjectSpace.memsize_of(Object.new)
+
+ symbol = @libtest.find_symbol("gvar_gstruct_set")
+ size = ObjectSpace.memsize_of(symbol)
+ expect(size).to be > base_size
+ end
+
+ it "should be shareable for Ractor", :ractor do
+ symbol = @libtest.find_symbol("gvar_gstruct_set")
+ expect(Ractor.shareable?(symbol)).to be true
+ end
+ end
+end
diff --git a/spec/ffi/enum_spec.rb b/spec/ffi/enum_spec.rb
index b8c5b57..7a55b07 100644
--- a/spec/ffi/enum_spec.rb
+++ b/spec/ffi/enum_spec.rb
@@ -26,14 +26,18 @@ module TestEnum3
ffi_lib TestLibrary::PATH
enum :enum_type1, [:c1, :c2, :c3, :c4]
- enum :enum_type2, [:c5, 42, :c6, :c7, :c8]
- enum :enum_type3, [:c9, 42, :c10, :c11, 4242, :c12]
- enum :enum_type4, [:c13, 42, :c14, 4242, :c15, 424242, :c16, 42424242]
-
attach_function :test_tagged_typedef_enum1, [:enum_type1], :enum_type1
+
+ enum :enum_type2, [:c5, 42, :c6, :c7, :c8]
attach_function :test_tagged_typedef_enum2, [:enum_type2], :enum_type2
+
+ enum :enum_type3, [:c9, 42, :c10, :c11, 4242, :c12]
attach_function :test_tagged_typedef_enum3, [:enum_type3], :enum_type3
+
+ enum :enum_type4, [:c13, 42, :c14, 4242, :c15, 424242, :c16, 42424242]
attach_function :test_tagged_typedef_enum4, [:enum_type4], :enum_type4
+
+ freeze
end
module TestEnum4
@@ -44,18 +48,22 @@ module TestEnum4
enum :enum_type1, [:c5, 0x42, :c6, :c7, :c8]
enum :enum_type2, [:c9, 0x42, :c10, :c11, 0x4242, :c12]
enum :enum_type3, [:c13, 0x42, :c14, 0x4242, :c15, 0x42424242, :c16, 0x4242424242424242]
- enum FFI::Type::UINT16, :enum_type4, [:c17, 0x42, :c18, :c19, :c20]
- enum FFI::Type::UINT32, :enum_type5, [:c21, 0x42, :c22, :c23, 0x4242, :c24]
- enum FFI::Type::UINT64, :enum_type6, [:c25, 0x42, :c26, 0x4242, :c27, 0x42424242, :c28, 0x4242424242424242]
- enum FFI::Type::UINT64, [:c29, 0x4242424242424242, :c30, :c31, :c32]
attach_function :test_untagged_nonint_enum, [:uint8], :uint8
attach_function :test_tagged_nonint_enum1, [:uint16], :uint16
attach_function :test_tagged_nonint_enum2, [:uint32], :uint32
attach_function :test_tagged_nonint_enum3, [:uint64], :uint64
+
+ enum FFI::Type::UINT16, :enum_type4, [:c17, 0x42, :c18, :c19, :c20]
+ enum FFI::Type::UINT32, :enum_type5, [:c21, 0x42, :c22, :c23, 0x4242, :c24]
+ enum FFI::Type::UINT64, :enum_type6, [:c25, 0x42, :c26, 0x4242, :c27, 0x42424242, :c28, 0x4242424242424242]
+ enum FFI::Type::UINT64, [:c29, 0x4242424242424242, :c30, :c31, :c32]
+
attach_function :test_tagged_nonint_enum4, :test_tagged_nonint_enum1, [:enum_type4], :enum_type4
attach_function :test_tagged_nonint_enum5, :test_tagged_nonint_enum2, [:enum_type5], :enum_type5
attach_function :test_tagged_nonint_enum6, :test_tagged_nonint_enum3, [:enum_type6], :enum_type6
+
+ freeze
end
describe "A library with no enum defined" do
@@ -430,4 +438,18 @@ describe "All enums" do
end
end.to raise_error(ArgumentError, /duplicate/)
end
+
+ it "should be usable in Ractor", :ractor do
+ res = Ractor.new do
+ [
+ TestEnum1.test_untagged_enum(:c1),
+ TestEnum3.test_tagged_typedef_enum1(:c1),
+ TestEnum4.test_tagged_nonint_enum4(0x45),
+ TestEnum3.enum_type(:enum_type1)[0],
+ TestEnum4.enum_type(:enum_type6)[0x4242424242424242],
+ TestEnum4.enum_value(:c3)
+ ]
+ end.take
+ expect( res ).to eq( [0, :c1, :c20, :c1, :c28, 2] )
+ end
end
diff --git a/spec/ffi/errno_spec.rb b/spec/ffi/errno_spec.rb
index 4170f92..c0207b8 100644
--- a/spec/ffi/errno_spec.rb
+++ b/spec/ffi/errno_spec.rb
@@ -34,4 +34,12 @@ describe "FFI.errno" do
expect(FFI.errno).to eq(0x12345678)
end
end
+
+ it "works in Ractor", :ractor do
+ res = Ractor.new do
+ LibTest.setLastError(0x12345678)
+ FFI.errno
+ end.take
+ expect(res).to eq(0x12345678)
+ end
end
diff --git a/spec/ffi/fixtures/ClosureTest.c b/spec/ffi/fixtures/ClosureTest.c
index 16f72c4..47273e8 100644
--- a/spec/ffi/fixtures/ClosureTest.c
+++ b/spec/ffi/fixtures/ClosureTest.c
@@ -16,10 +16,10 @@
double testClosureVrDva(double d, ...) {
va_list args;
- double (*closure)(void);
+ typedef double (*closure_fun)(void);
va_start(args, d);
- closure = va_arg(args, double (*)(void));
+ closure_fun closure = va_arg(args, closure_fun);
va_end(args);
return d + closure();
@@ -27,12 +27,12 @@ double testClosureVrDva(double d, ...) {
long testClosureVrILva(int i, long l, ...) {
va_list args;
- int (*cl1)(int);
- long (*cl2)(long);
+ typedef int (*cl1_fun)(int);
+ typedef long (*cl2_fun)(long);
va_start(args, l);
- cl1 = va_arg(args, int (*)(int));
- cl2 = va_arg(args, long (*)(long));
+ cl1_fun cl1 = va_arg(args, cl1_fun);
+ cl2_fun cl2 = va_arg(args, cl2_fun);
va_end(args);
return cl1(i) + cl2(l);
diff --git a/spec/ffi/fixtures/PipeHelperWindows.c b/spec/ffi/fixtures/PipeHelperWindows.c
index 0bdbd2e..31b4a2a 100644
--- a/spec/ffi/fixtures/PipeHelperWindows.c
+++ b/spec/ffi/fixtures/PipeHelperWindows.c
@@ -16,7 +16,7 @@ int pipeHelperCreatePipe(FD_TYPE pipefd[2])
sprintf( name, "\\\\.\\Pipe\\pipeHelper-%u-%i",
(unsigned int)GetCurrentProcessId(), pipe_idx++ );
- pipefd[0] = CreateNamedPipe( name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
+ pipefd[0] = CreateNamedPipeA( name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_WAIT,
1, // Number of pipes
5, // Out buffer size
@@ -26,7 +26,7 @@ int pipeHelperCreatePipe(FD_TYPE pipefd[2])
if(pipefd[0] == INVALID_HANDLE_VALUE)
return -1;
- pipefd[1] = CreateFile( name, GENERIC_WRITE, 0, NULL,
+ pipefd[1] = CreateFileA( name, GENERIC_WRITE, 0, NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
diff --git a/spec/ffi/fixtures/PointerTest.c b/spec/ffi/fixtures/PointerTest.c
index a7f392a..02dbc27 100644
--- a/spec/ffi/fixtures/PointerTest.c
+++ b/spec/ffi/fixtures/PointerTest.c
@@ -5,7 +5,9 @@
*/
#include <sys/types.h>
+#ifndef _MSC_VER
#include <sys/param.h>
+#endif
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
diff --git a/spec/ffi/fixtures/compile.rb b/spec/ffi/fixtures/compile.rb
index f2e831a..2be97cb 100644
--- a/spec/ffi/fixtures/compile.rb
+++ b/spec/ffi/fixtures/compile.rb
@@ -22,6 +22,8 @@ module TestLibrary
"powerpc64"
when /ppc|powerpc/
"powerpc"
+ when /sparcv9|sparc64/
+ "sparcv9"
when /^arm/
if RbConfig::CONFIG['host_os'] =~ /darwin/
"aarch64"
@@ -67,5 +69,5 @@ module TestLibrary
lib
end
- PATH = compile_library(".", "libtest.#{FFI::Platform::LIBSUFFIX}")
+ PATH = FFI.make_shareable(compile_library(".", "libtest.#{FFI::Platform::LIBSUFFIX}"))
end
diff --git a/spec/ffi/function_spec.rb b/spec/ffi/function_spec.rb
index 5bc2421..77d09d8 100644
--- a/spec/ffi/function_spec.rb
+++ b/spec/ffi/function_spec.rb
@@ -48,6 +48,30 @@ describe FFI::Function do
expect(LibTest.testFunctionAdd(10, 10, function_add)).to eq(20)
end
+ def adder(a, b)
+ a + b
+ end
+
+ it "can be made shareable for Ractor", :ractor do
+ add = FFI::Function.new(:int, [:int, :int], &method(:adder))
+ Ractor.make_shareable(add)
+
+ res = Ractor.new(add) do |add2|
+ LibTest.testFunctionAdd(10, 10, add2)
+ end.take
+
+ expect( res ).to eq(20)
+ end
+
+ it "should be usable with Ractor", :ractor do
+ res = Ractor.new do
+ function_add = FFI::Function.new(:int, [:int, :int]) { |a, b| a + b }
+ LibTest.testFunctionAdd(10, 10, function_add)
+ end.take
+
+ expect( res ).to eq(20)
+ end
+
it 'can be used to wrap an existing function pointer' do
expect(FFI::Function.new(:int, [:int, :int], @libtest.find_function('testAdd')).call(10, 10)).to eq(20)
end
@@ -59,6 +83,16 @@ describe FFI::Function do
expect(Foo.add(10, 10)).to eq(20)
end
+ it 'can be attached to two modules' do
+ module Foo1; end
+ module Foo2; end
+ fp = FFI::Function.new(:int, [:int, :int], @libtest.find_function('testAdd'))
+ fp.attach(Foo1, 'add')
+ fp.attach(Foo2, 'add')
+ expect(Foo1.add(11, 11)).to eq(22)
+ expect(Foo2.add(12, 12)).to eq(24)
+ end
+
it 'can be used to extend an object' do
fp = FFI::Function.new(:int, [:int, :int], @libtest.find_function('testAdd'))
foo = Object.new
@@ -103,4 +137,12 @@ describe FFI::Function do
fp = FFI::Function.new(:int, [:int, :int], @libtest.find_function('testAdd'))
expect { fp.free }.to raise_error RuntimeError
end
+
+ it 'has a memsize function', skip: RUBY_ENGINE != "ruby" do
+ base_size = ObjectSpace.memsize_of(Object.new)
+
+ function = FFI::Function.new(:int, [:int, :int], @libtest.find_function('testAdd'))
+ size = ObjectSpace.memsize_of(function)
+ expect(size).to be > base_size
+ end
end
diff --git a/spec/ffi/function_type_spec.rb b/spec/ffi/function_type_spec.rb
new file mode 100644
index 0000000..2f171d8
--- /dev/null
+++ b/spec/ffi/function_type_spec.rb
@@ -0,0 +1,27 @@
+#
+# This file is part of ruby-ffi.
+# For licensing, see LICENSE.SPECS
+#
+
+require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
+
+describe "FFI::FunctionType", skip: RUBY_ENGINE != "ruby" do
+ it 'is initialized with return type and a list of parameter types' do
+ function_type = FFI::FunctionType.new(:int, [ :char, :ulong ])
+ expect(function_type.return_type).to be == FFI::Type::Builtin::INT
+ expect(function_type.param_types).to be == [ FFI::Type::Builtin::CHAR, FFI::Type::Builtin::ULONG ]
+ end
+
+ it 'has a memsize function' do
+ base_size = ObjectSpace.memsize_of(Object.new)
+
+ function_type = FFI::FunctionType.new(:int, [])
+ size = ObjectSpace.memsize_of(function_type)
+ expect(size).to be > base_size
+
+ base_size = size
+ function_type = FFI::FunctionType.new(:int, [:char])
+ size = ObjectSpace.memsize_of(function_type)
+ expect(size).to be > base_size
+ end
+end
diff --git a/spec/ffi/gc_compact_spec.rb b/spec/ffi/gc_compact_spec.rb
new file mode 100644
index 0000000..974d7f8
--- /dev/null
+++ b/spec/ffi/gc_compact_spec.rb
@@ -0,0 +1,66 @@
+# -*- rspec -*-
+# encoding: utf-8
+#
+# Tests to verify correct implementation of compaction callbacks in rb_data_type_t definitions.
+#
+# Compaction callbacks update moved VALUEs.
+# In ruby-2.7 they are invoked only while GC.compact or GC.verify_compaction_references.
+# Ruby constants are usually moved, but local variables are not.
+#
+# Effectiveness of the tests below should be verified by commenting the compact callback out like so:
+#
+# const rb_data_type_t rbffi_struct_layout_data_type = {
+# .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,
+# .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
+# };
+#
+# This should result in a segmentation fault aborting the whole process.
+# Therefore the effectiveness of only one test can be verified per rspec run.
+
+require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
+
+describe "GC.compact", if: GC.respond_to?(:compact) do
+ before :all do
+
+ class St1 < FFI::Struct
+ layout :i, :int
+ end
+ ST1 = St1.new
+
+ class St2 < FFI::Struct
+ layout :i, :int
+ end
+ ST2 = St2.new
+ ST2[:i] = 6789
+
+ begin
+ # Use GC.verify_compaction_references instead of GC.compact .
+ # This has the advantage that all movable objects are actually moved.
+ # The downside is that it doubles the heap space of the Ruby process.
+ # Therefore we call it only once and do several tests afterwards.
+ GC.verify_compaction_references(toward: :empty, double_heap: true)
+ rescue NotImplementedError, NoMethodError => err
+ skip("GC.compact skipped: #{err}")
+ end
+ end
+
+ it "should compact FFI::StructLayout without field cache" do
+ expect( ST1[:i] ).to eq( 0 )
+ end
+
+ it "should compact FFI::StructLayout with field cache" do
+ expect( ST2[:i] ).to eq( 6789 )
+ end
+
+ it "should compact FFI::StructLayout::Field" do
+ l = St1.layout
+ expect( l.fields.first.type ).to eq( FFI::Type::Builtin::INT32 )
+ end
+end
diff --git a/spec/ffi/library_spec.rb b/spec/ffi/library_spec.rb
index ca28974..8691b17 100644
--- a/spec/ffi/library_spec.rb
+++ b/spec/ffi/library_spec.rb
@@ -5,6 +5,12 @@
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
+module TestEnumValueRactor
+ extend FFI::Library
+ enum :something, [:one, :two]
+ freeze
+end
+
describe "Library" do
describe ".enum_value" do
m = Module.new do
@@ -20,6 +26,14 @@ describe "Library" do
it "should return nil for an invalid key" do
expect(m.enum_value(:three)).to be nil
end
+
+ it "should be queryable in Ractor", :ractor do
+ res = Ractor.new do
+ TestEnumValueRactor.enum_value(:one)
+ end.take
+
+ expect( res ).to eq(0)
+ end
end
describe "#ffi_convention" do
@@ -88,7 +102,7 @@ describe "Library" do
}.to raise_error(LoadError)
end
- it "interprets INPUT() in loader scripts", unless: FFI::Platform.windows? do
+ it "interprets INPUT() in linker scripts", unless: FFI::Platform.windows? || FFI::Platform.mac? do
path = File.dirname(TestLibrary::PATH)
file = File.basename(TestLibrary::PATH)
script = File.join(path, "ldscript.so")
@@ -184,15 +198,49 @@ describe "Library" do
end.getpid).to eq(Process.pid)
}.to raise_error(LoadError)
end
+ end
- it "attach_function :bool_return_true from [ File.expand_path(#{TestLibrary::PATH.inspect}) ]" do
- mod = Module.new do |m|
- m.extend FFI::Library
- ffi_lib File.expand_path(TestLibrary::PATH)
- attach_function :bool_return_true, [ ], :bool
- end
- expect(mod.bool_return_true).to be true
+ it "attach_function :bool_return_true from [ File.expand_path(#{TestLibrary::PATH.inspect}) ]" do
+ mod = Module.new do |m|
+ m.extend FFI::Library
+ ffi_lib File.expand_path(TestLibrary::PATH)
+ attach_function :bool_return_true, [ ], :bool
end
+ expect(mod.bool_return_true).to be true
+ end
+
+ it "can define a foo! function" do
+ mod = Module.new do |m|
+ m.extend FFI::Library
+ ffi_lib File.expand_path(TestLibrary::PATH)
+ attach_function :foo!, :bool_return_true, [], :bool
+ end
+ expect(mod.foo!).to be true
+ end
+
+ it "can define a foo? function" do
+ mod = Module.new do |m|
+ m.extend FFI::Library
+ ffi_lib File.expand_path(TestLibrary::PATH)
+ attach_function :foo?, :bool_return_true, [], :bool
+ end
+ expect(mod.foo?).to be true
+ end
+
+ it "can reveal the function type" do
+ skip 'this is not yet implemented on JRuby' if RUBY_ENGINE == 'jruby'
+ skip 'this is not yet implemented on Truffleruby' if RUBY_ENGINE == 'truffleruby'
+
+ mod = Module.new do |m|
+ m.extend FFI::Library
+ ffi_lib File.expand_path(TestLibrary::PATH)
+ attach_function :bool_return_true, [ :string ], :bool
+ end
+
+ fun = mod.attached_functions
+ expect(fun.keys).to eq([:bool_return_true])
+ expect(fun[:bool_return_true].param_types).to eq([FFI::Type::STRING])
+ expect(fun[:bool_return_true].return_type).to eq(FFI::Type::BOOL)
end
def gvar_lib(name, type)
@@ -322,4 +370,35 @@ describe "Library" do
expect(val[:data]).to eq(i)
end
end
+
+ it "can reveal its attached global struct based variables" do
+ lib = Module.new do |m|
+ m.extend FFI::Library
+ ffi_lib TestLibrary::PATH
+ attach_variable :gvari, "gvar_gstruct", GlobalStruct
+ end
+ expect(lib.attached_variables).to eq({ gvari: GlobalStruct })
+ end
+
+ it "can reveal its attached global variables" do
+ lib = Module.new do |m|
+ m.extend FFI::Library
+ ffi_lib TestLibrary::PATH
+ attach_variable "gvaro", "gvar_u32", :uint32
+ end
+ expect(lib.attached_variables).to eq({ gvaro: FFI::Type::UINT32 })
+ end
+
+ it "should have shareable constants for Ractor", :ractor do
+ res = Ractor.new do
+ [
+ FFI::Library::LIBC,
+ FFI::Library::CURRENT_PROCESS,
+ FFI::CURRENT_PROCESS,
+ FFI::USE_THIS_PROCESS_AS_LIBRARY,
+ ]
+ end.take
+
+ expect( res.size ).to be > 0
+ end
end
diff --git a/spec/ffi/managed_struct_spec.rb b/spec/ffi/managed_struct_spec.rb
index b4e80bb..6299bdc 100644
--- a/spec/ffi/managed_struct_spec.rb
+++ b/spec/ffi/managed_struct_spec.rb
@@ -46,6 +46,8 @@ describe "Managed Struct" do
def self.release(_ptr)
@@count += 1
end
+ private_class_method :release
+
def self.wait_gc(count)
loop = 5
while loop > 0 && @@count < count
diff --git a/spec/ffi/memorypointer_spec.rb b/spec/ffi/memorypointer_spec.rb
index c8d1b42..f29e2cb 100644
--- a/spec/ffi/memorypointer_spec.rb
+++ b/spec/ffi/memorypointer_spec.rb
@@ -28,6 +28,21 @@ describe "MemoryPointer#total" do
expect(MemoryPointer.new(1024).total).to eq 1024
end
end
+describe "MemoryPointer#clear" do
+ it "should clear the memory" do
+ ptr = MemoryPointer.new(:long)
+ ptr.write_long 1234
+ expect(ptr.read_long).to eq(1234)
+ ptr.clear
+ expect(ptr.read_long).to eq(0)
+ end
+ it "should deny changes when frozen" do
+ skip "not yet supported on TruffleRuby" if RUBY_ENGINE == "truffleruby"
+ skip "not yet supported on JRuby" if RUBY_ENGINE == "jruby"
+ ptr = MemoryPointer.new(:long).freeze
+ expect{ ptr.clear }.to raise_error(RuntimeError, /memory write/)
+ end
+end
describe "MemoryPointer#read_array_of_long" do
it "foo" do
ptr = MemoryPointer.new(:long, 1024)
@@ -76,3 +91,22 @@ describe "MemoryPointer return value" do
expect(Stdio.fclose(fp)).to eq 0 unless fp.nil? or fp.null?
end
end
+describe "#autorelease" do
+ it "should be true by default" do
+ expect(MemoryPointer.new(8).autorelease?).to be true
+ end
+
+ it "should return false when autorelease=(false)" do
+ ptr = MemoryPointer.new(8)
+ ptr.autorelease = false
+ expect(ptr.autorelease?).to be false
+ ptr.free
+ end
+
+ it "should deny changes when frozen" do
+ skip "not yet supported on TruffleRuby" if RUBY_ENGINE == "truffleruby"
+ skip "not yet supported on JRuby" if RUBY_ENGINE == "jruby"
+ ptr = MemoryPointer.new(8).freeze
+ expect{ ptr.autorelease = false }.to raise_error(FrozenError)
+ end
+end
diff --git a/spec/ffi/platform_spec.rb b/spec/ffi/platform_spec.rb
index ad23621..8890b07 100644
--- a/spec/ffi/platform_spec.rb
+++ b/spec/ffi/platform_spec.rb
@@ -134,4 +134,17 @@ describe "FFI::Platform.unix?" do
expect(FFI::Platform::BYTE_ORDER).to eq(order)
end
end
+
+ it "should have shareable constants for Ractor", :ractor do
+ res = Ractor.new do
+ [
+ FFI::Platform::OS,
+ FFI::Platform::CPU,
+ FFI::Platform::ARCH,
+ FFI::Platform::OS,
+ ]
+ end.take
+
+ expect( res.size ).to be > 0
+ end
end
diff --git a/spec/ffi/pointer_spec.rb b/spec/ffi/pointer_spec.rb
index b216a16..8716d30 100644
--- a/spec/ffi/pointer_spec.rb
+++ b/spec/ffi/pointer_spec.rb
@@ -90,6 +90,13 @@ describe "Pointer" do
expect(PointerTestLib.ptr_ret_pointer(memory, 0).address).to eq(0xdeadbeef)
end
+ it "#write_pointer frozen object" do
+ skip "not yet supported on TruffleRuby" if RUBY_ENGINE == "truffleruby"
+ skip "not yet supported on JRuby" if RUBY_ENGINE == "jruby"
+ memory = FFI::MemoryPointer.new(:pointer).freeze
+ expect{ memory.write_pointer(PointerTestLib.ptr_from_address(0xdeadbeef)) }.to raise_error(RuntimeError, /memory write/)
+ end
+
it "#read_array_of_pointer" do
values = [0x12345678, 0xfeedf00d, 0xdeadbeef]
memory = FFI::MemoryPointer.new :pointer, values.size
@@ -237,6 +244,14 @@ describe "Pointer" do
expect(FFI::Pointer.new(0).slice(0, 10).size_limit?).to be true
end
end
+
+ describe "#initialise" do
+ it 'can use adresses with high bit set' do
+ max_address = 2**FFI::Platform::ADDRESS_SIZE - 1
+ pointer = FFI::Pointer.new(:uint8, max_address)
+ expect(pointer.address).to eq(max_address)
+ end
+ end if (RUBY_ENGINE != "truffleruby" && RUBY_ENGINE != "jruby")
end
describe "AutoPointer" do
@@ -249,6 +264,7 @@ describe "AutoPointer" do
def self.release
@@count += 1 if @@count > 0
end
+ private_class_method(:release)
def self.reset
@@count = 0
end
@@ -267,10 +283,11 @@ describe "AutoPointer" do
end
class AutoPointerSubclass < FFI::AutoPointer
def self.release(ptr); end
+ private_class_method(:release)
end
# see #427
- it "cleanup via default release method", :broken => true do
+ it "cleanup via default release method", gc_dependent: true do
expect(AutoPointerSubclass).to receive(:release).at_least(loop_count-wiggle_room).times
AutoPointerTestHelper.reset
loop_count.times do
@@ -283,7 +300,7 @@ describe "AutoPointer" do
end
# see #427
- it "cleanup when passed a proc", :broken => true do
+ it "cleanup when passed a proc", gc_dependent: true do
# NOTE: passing a proc is touchy, because it's so easy to create a memory leak.
#
# specifically, if we made an inline call to
@@ -302,7 +319,7 @@ describe "AutoPointer" do
end
# see #427
- it "cleanup when passed a method", :broken => true do
+ it "cleanup when passed a method", gc_dependent: true do
expect(AutoPointerTestHelper).to receive(:release).at_least(loop_count-wiggle_room).times
AutoPointerTestHelper.reset
loop_count.times do
@@ -319,6 +336,7 @@ describe "AutoPointer" do
ffi_lib TestLibrary::PATH
class CustomAutoPointer < FFI::AutoPointer
def self.release(ptr); end
+ private_class_method(:release)
end
attach_function :ptr_from_address, [ FFI::Platform::ADDRESS_SIZE == 32 ? :uint : :ulong_long ], CustomAutoPointer
end
@@ -341,6 +359,7 @@ describe "AutoPointer" do
describe "#autorelease?" do
ptr_class = Class.new(FFI::AutoPointer) do
def self.release(ptr); end
+ private_class_method(:release)
end
it "should be true by default" do
@@ -352,11 +371,17 @@ describe "AutoPointer" do
ptr.autorelease = false
expect(ptr.autorelease?).to be false
end
+
+ it "should deny changes when frozen" do
+ ptr = ptr_class.new(FFI::Pointer.new(0xdeadbeef)).freeze
+ expect{ ptr.autorelease = false }.to raise_error(FrozenError)
+ end
end
describe "#type_size" do
ptr_class = Class.new(FFI::AutoPointer) do
def self.release(ptr); end
+ private_class_method(:release)
end
it "type_size of AutoPointer should match wrapped Pointer" do
@@ -373,5 +398,13 @@ describe "AutoPointer" do
expect(mptr[1].read_uint).to eq(0xcafebabe)
end
end
+
+ it "has a memsize function", skip: RUBY_ENGINE != "ruby" do
+ base_size = ObjectSpace.memsize_of(Object.new)
+
+ pointer = FFI::Pointer.new(:int, 0xdeadbeef)
+ size = ObjectSpace.memsize_of(pointer)
+ expect(size).to be > base_size
+ end
end
diff --git a/spec/ffi/rbx/memory_pointer_spec.rb b/spec/ffi/rbx/memory_pointer_spec.rb
index 3878973..1270c9b 100644
--- a/spec/ffi/rbx/memory_pointer_spec.rb
+++ b/spec/ffi/rbx/memory_pointer_spec.rb
@@ -104,6 +104,18 @@ describe "MemoryPointer" do
expect(m.read FFI::Type::BOOL).to eq(false)
end
+ it "allows definition of a custom typedef" do
+ FFI.typedef :uint32, :fubar_t
+ expect(FFI.find_type(:fubar_t)).to eq(FFI::Type::Builtin::UINT32)
+ end
+
+ it "allows overwriting of a default typedef" do
+ FFI.typedef :uint32, :char
+ expect(FFI.find_type(:char)).to eq(FFI::Type::Builtin::UINT32)
+ ensure
+ FFI.typedef FFI::Type::Builtin::CHAR, :char
+ end
+
it "allows writing a custom typedef" do
FFI.typedef :uint, :fubar_t
FFI.typedef :size_t, :fubar2_t
@@ -189,4 +201,12 @@ describe "MemoryPointer" do
end
expect(block_executed).to be true
end
+
+ it "has a memsize function", skip: RUBY_ENGINE != "ruby" do
+ base_size = ObjectSpace.memsize_of(Object.new)
+
+ pointer = FFI::MemoryPointer.from_string("FFI is Awesome")
+ size = ObjectSpace.memsize_of(pointer)
+ expect(size).to be > base_size
+ end
end
diff --git a/spec/ffi/spec_helper.rb b/spec/ffi/spec_helper.rb
index bb12050..22d1c47 100644
--- a/spec/ffi/spec_helper.rb
+++ b/spec/ffi/spec_helper.rb
@@ -5,9 +5,11 @@
require_relative 'fixtures/compile'
require 'timeout'
+require 'objspace'
RSpec.configure do |c|
- c.filter_run_excluding :broken => true
+ c.filter_run_excluding gc_dependent: true unless ENV['FFI_TEST_GC'] == 'true'
+ c.filter_run_excluding( :ractor ) unless defined?(Ractor) && RUBY_VERSION >= "3.1"
end
module TestLibrary
diff --git a/spec/ffi/struct_by_ref_spec.rb b/spec/ffi/struct_by_ref_spec.rb
index 0858423..a775f92 100644
--- a/spec/ffi/struct_by_ref_spec.rb
+++ b/spec/ffi/struct_by_ref_spec.rb
@@ -39,5 +39,14 @@ describe FFI::Struct, ' by_ref' do
expect { @api.struct_test(other_class.new) }.to raise_error(TypeError)
end
+
+ it "can reveal the mapped type converter" do
+ skip 'this is not yet implemented on JRuby' if RUBY_ENGINE == 'jruby'
+ skip 'this is not yet implemented on Truffleruby' if RUBY_ENGINE == 'truffleruby'
+
+ param_type = @api.attached_functions[:struct_test].param_types[0]
+ expect(param_type).to be_a(FFI::Type::Mapped)
+ expect(param_type.converter).to be_a(FFI::StructByReference)
+ end
end
diff --git a/spec/ffi/struct_spec.rb b/spec/ffi/struct_spec.rb
index fb574d2..ab62da1 100644
--- a/spec/ffi/struct_spec.rb
+++ b/spec/ffi/struct_spec.rb
@@ -410,6 +410,7 @@ module StructSpecsStructTests
s.pointer.put_double(0, 1.0)
expect(s.pointer.get_double(0)).to eq(1.0)
end
+
module EnumFields
extend FFI::Library
TestEnum = enum :test_enum, [:c1, 10, :c2, 20, :c3, 30, :c4, 40]
@@ -456,13 +457,24 @@ module StructSpecsStructTests
end
it "Can have CallbackInfo struct field" do
+ s = CallbackMember::TestStruct.new
+ add_proc = lambda { |a, b| a + b }
+ sub_proc = lambda { |a, b| a - b }
+ s[:add] = add_proc
+ s[:sub] = sub_proc
+ expect(CallbackMember.struct_call_add_cb(s, 40, 2)).to eq(42)
+ expect(CallbackMember.struct_call_sub_cb(s, 44, 2)).to eq(42)
+ end
+
+ it "Can use CallbackInfo struct field in Ractor", :ractor do
+ res = Ractor.new do
s = CallbackMember::TestStruct.new
- add_proc = lambda { |a, b| a+b }
- sub_proc = lambda { |a, b| a-b }
+ add_proc = lambda { |a, b| a + b }
s[:add] = add_proc
- s[:sub] = sub_proc
- expect(CallbackMember.struct_call_add_cb(s, 40, 2)).to eq(42)
- expect(CallbackMember.struct_call_sub_cb(s, 44, 2)).to eq(42)
+ CallbackMember.struct_call_add_cb(s, 40, 2)
+ end.take
+
+ expect( res ).to eq(42)
end
it "Can return its members as a list" do
@@ -532,6 +544,34 @@ module StructSpecsStructTests
expect(a.members).to eq([:a])
expect(b.members).to eq([:a, :b])
end
+
+ it "can be made shareable for Ractor", :ractor do
+ a = Class.new(FFI::Struct) do
+ layout :a, :char
+ end.new
+ a[:a] = -34
+ Ractor.make_shareable(a)
+
+ res = Ractor.new(a) do |a2|
+ a2[:a]
+ end.take
+
+ expect( res ).to eq(-34)
+ end
+
+ it "should be usable with Ractor", :ractor do
+ class TestStructRactor < FFI::Struct
+ layout :i, :int
+ end
+
+ res = Ractor.new do
+ s = TestStructRactor.new
+ s[:i] = 0x14
+ LibTest.ptr_ret_int32_t(s, 0)
+ end.take
+
+ expect( res ).to eq(0x14)
+ end
end
end
@@ -1027,6 +1067,65 @@ describe "variable-length arrays" do
end
end
+describe "Struct memsize functions", skip: RUBY_ENGINE != "ruby" do
+ it "has a memsize function" do
+ base_size = ObjectSpace.memsize_of(Object.new)
+
+ c = Class.new(FFI::Struct) do
+ layout :b, :bool
+ end
+ struct = c.new
+ size = ObjectSpace.memsize_of(struct)
+ expect(size).to be > base_size
+ end
+
+ class SmallCustomStruct < FFI::Struct
+ layout :pointer, :pointer
+ end
+
+ class LargerCustomStruct < FFI::Struct
+ layout :pointer, :pointer,
+ :c, :char,
+ :i, :int
+ end
+
+ it "StructLayout has a memsize function" do
+ base_size = ObjectSpace.memsize_of(Object.new)
+
+ layout = SmallCustomStruct.layout
+ size = ObjectSpace.memsize_of(layout)
+ expect(size).to be > base_size
+ base_size = size
+
+ layout = LargerCustomStruct.layout
+ size = ObjectSpace.memsize_of(layout)
+ expect(size).to be > base_size
+ end
+
+ it "StructField has a memsize function" do
+ base_size = ObjectSpace.memsize_of(Object.new)
+
+ layout = SmallCustomStruct.layout
+ size = ObjectSpace.memsize_of(layout[:pointer])
+ expect(size).to be > base_size
+ end
+
+ it "StructLayout should be shareable with Ractor", :ractor do
+ kl = Class.new(FFI::Struct) do
+ layout :ptr, :pointer
+ end
+ expect(Ractor.shareable?(kl.layout)).to eq(true)
+ end
+
+ it "StructField should be shareable with Ractor", :ractor do
+ kl = Class.new(FFI::Struct) do
+ layout :ptr, :pointer
+ end
+ expect(Ractor.shareable?(kl.layout[:ptr])).to eq(true)
+ end
+end
+
+
describe "Struct order" do
before :all do
@struct = Class.new(FFI::Struct) do
diff --git a/spec/ffi/type_spec.rb b/spec/ffi/type_spec.rb
new file mode 100644
index 0000000..e880c83
--- /dev/null
+++ b/spec/ffi/type_spec.rb
@@ -0,0 +1,76 @@
+#
+# This file is part of ruby-ffi.
+# For licensing, see LICENSE.SPECS
+#
+
+require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))
+
+describe FFI::Type do
+ it 'has a memsize function', skip: RUBY_ENGINE != "ruby" do
+ base_size = ObjectSpace.memsize_of(Object.new)
+
+ size = ObjectSpace.memsize_of(FFI::Type.new(42))
+ expect(size).to be > base_size
+ base_size = size
+
+ converter = Module.new do
+ extend FFI::DataConverter
+
+ def self.native_type
+ @native_type_called = true
+ FFI::Type::INT32
+ end
+
+ def self.to_native(val, ctx)
+ @to_native_called = true
+ ToNativeMap[val]
+ end
+
+ def self.from_native(val, ctx)
+ @from_native_called = true
+ FromNativeMap[val]
+ end
+ end
+
+ size = ObjectSpace.memsize_of(FFI::Type::Mapped.new(converter))
+ expect(size).to be > base_size
+ base_size = size
+
+ # Builtin types are larger as they also have a name and own ffi_type
+ size = ObjectSpace.memsize_of(FFI::Type::Builtin::CHAR)
+ expect(size).to be > base_size
+ end
+
+ it "should be shareable with Ractor", :ractor do
+ expect(Ractor.shareable?(FFI::Type.new(5))).to eq(true)
+ end
+
+ describe :Builtin do
+ it "should be shareable with Ractor", :ractor do
+ expect(Ractor.shareable?(FFI::Type::INT32)).to eq(true)
+ end
+ end
+
+ describe :Mapped do
+ it "should be shareable with Ractor", :ractor do
+ converter = Module.new do
+ extend FFI::DataConverter
+
+ def self.native_type
+ FFI::Type::INT32
+ end
+
+ def self.to_native(val, ctx)
+ ToNativeMap[val]
+ end
+
+ def self.from_native(val, ctx)
+ FromNativeMap[val]
+ end
+ end
+ expect(Ractor.shareable?(converter)).to eq(true)
+ type = FFI::Type::Mapped.new(converter)
+ expect(Ractor.shareable?(type)).to eq(true)
+ end
+ end
+end
diff --git a/spec/ffi/variadic_spec.rb b/spec/ffi/variadic_spec.rb
index f379ed4..b528c09 100644
--- a/spec/ffi/variadic_spec.rb
+++ b/spec/ffi/variadic_spec.rb
@@ -23,6 +23,8 @@ describe "Function with variadic arguments" do
attach_function :testBlockingClose, [ :pointer ], :void
attach_function :testCallbackVrDva, :testClosureVrDva, [ :double, :varargs ], :double
attach_function :testCallbackVrILva, :testClosureVrILva, [ :int, :long, :varargs ], :long
+
+ freeze
end
it "takes enum arguments" do
@@ -37,6 +39,15 @@ describe "Function with variadic arguments" do
expect(LibTest.pack_varargs2(buf, :c1, "ii", :int, :c3, :int, :c4)).to eq(:c2)
end
+ it "can reveal its return and parameters" do
+ skip 'this is not yet implemented on JRuby' if RUBY_ENGINE == 'jruby'
+ skip 'this is not yet implemented on Truffleruby' if RUBY_ENGINE == 'truffleruby'
+
+ fun = LibTest.attached_functions[:testBlockingWRva]
+ expect(fun.param_types).to eq([FFI::Type::POINTER, FFI::Type::CHAR, FFI::Type::VARARGS])
+ expect(fun.return_type).to eq(FFI::Type::INT8)
+ end
+
it 'can wrap a blocking function with varargs' do
handle = LibTest.testBlockingOpen
expect(handle).not_to be_null
@@ -87,12 +98,33 @@ describe "Function with variadic arguments" do
expect(LibTest.testCallbackVrDva(3.0, :cbVrD, pr)).to be_within(0.0000001).of(45.0)
end
+ it "can be called as instance method" do
+ kl = Class.new do
+ include LibTest
+ def call
+ pr = proc { 42.0 }
+ testCallbackVrDva(3.0, :cbVrD, pr)
+ end
+ end
+ expect(kl.new.call).to be_within(0.0000001).of(45.0)
+ end
+
it "call variadic with several callback arguments" do
pr1 = proc { |i| i + 1 }
pr2 = proc { |l| l + 2 }
expect(LibTest.testCallbackVrILva(5, 6, :cbVrI, pr1, :cbVrL, pr2)).to eq(14)
end
+ it "should be usable with Ractor", :ractor do
+ res = Ractor.new do
+ pr = proc { 42.0 }
+ LibTest.testCallbackVrDva(3.0, :cbVrD, pr)
+ end.take
+
+ expect(res).to be_within(0.0000001).of(45.0)
+ end
+
+
module Varargs
PACK_VALUES = {
'c' => [ 0x12 ],