summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Kanis <lars@greiz-reinsdorf.de>2023-04-16 20:40:17 +0200
committerLars Kanis <lars@greiz-reinsdorf.de>2023-04-18 11:27:14 +0200
commit5247d3e736f77ce19bbb3e69cf5186fa5a7084f4 (patch)
tree9675fed8212b9ca4ffbe0929112be10bdd1c5919
parent250c31a25d81339cfe928a433ada3c0f17eae580 (diff)
downloadffi-5247d3e736f77ce19bbb3e69cf5186fa5a7084f4.tar.gz
Add support for using FFI in Ractor
All objects are shareable now when frozen. All objects can be created in a non-main Ractor. Typedefs are a global mutable state and are not accessable from Ractor other than the main Ractor. So all Function, Struct, etc. must be defined in the main Ractor and can then be used in other Ractors.
-rw-r--r--ext/ffi_c/AbstractMemory.c18
-rw-r--r--ext/ffi_c/ArrayType.c2
-rw-r--r--ext/ffi_c/Buffer.c4
-rw-r--r--ext/ffi_c/DynamicLibrary.c4
-rw-r--r--ext/ffi_c/Function.c3
-rw-r--r--ext/ffi_c/FunctionInfo.c2
-rw-r--r--ext/ffi_c/LastError.c3
-rw-r--r--ext/ffi_c/MappedType.c2
-rw-r--r--ext/ffi_c/MemoryPointer.c3
-rw-r--r--ext/ffi_c/Pointer.c5
-rw-r--r--ext/ffi_c/Struct.c8
-rw-r--r--ext/ffi_c/StructByValue.c2
-rw-r--r--ext/ffi_c/StructLayout.c4
-rw-r--r--ext/ffi_c/Type.c4
-rw-r--r--ext/ffi_c/Variadic.c2
-rw-r--r--ext/ffi_c/compat.h11
-rw-r--r--ext/ffi_c/ffi.c4
-rw-r--r--lib/ffi/autopointer.rb1
-rw-r--r--lib/ffi/compat.rb43
-rw-r--r--lib/ffi/ffi.rb1
-rw-r--r--lib/ffi/struct.rb2
-rw-r--r--spec/ffi/function_spec.rb24
-rw-r--r--spec/ffi/memorypointer_spec.rb30
-rw-r--r--spec/ffi/pointer_spec.rb10
-rw-r--r--spec/ffi/spec_helper.rb1
-rw-r--r--spec/ffi/struct_spec.rb50
26 files changed, 216 insertions, 27 deletions
diff --git a/ext/ffi_c/AbstractMemory.c b/ext/ffi_c/AbstractMemory.c
index 3cf16a7..3b076bb 100644
--- a/ext/ffi_c/AbstractMemory.c
+++ b/ext/ffi_c/AbstractMemory.c
@@ -70,7 +70,7 @@ const rb_data_type_t rbffi_abstract_memory_data_type = { /* extern */
},
// 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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
static size_t
@@ -316,6 +316,7 @@ static VALUE
memory_clear(VALUE self)
{
AbstractMemory* ptr = MEMORY(self);
+ checkWrite(ptr);
memset(ptr->address, 0, ptr->size);
return self;
}
@@ -687,6 +688,20 @@ memory_copy_from(VALUE self, VALUE rbsrc, VALUE rblen)
return self;
}
+/*
+ * call-seq:
+ * res.freeze
+ *
+ * Freeze the AbstractMemory object and unset the writable flag.
+ */
+static VALUE
+memory_freeze(VALUE self)
+{
+ AbstractMemory* ptr = MEMORY(self);
+ ptr->flags &= ~MEM_WR;
+ return rb_call_super(0, NULL);
+}
+
AbstractMemory*
rbffi_AbstractMemory_Cast(VALUE obj, const rb_data_type_t *data_type)
{
@@ -1102,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/ArrayType.c b/ext/ffi_c/ArrayType.c
index 4bd77fd..b1cbcea 100644
--- a/ext/ffi_c/ArrayType.c
+++ b/ext/ffi_c/ArrayType.c
@@ -50,7 +50,7 @@ const rb_data_type_t rbffi_array_type_data_type = { /* extern */
.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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
diff --git a/ext/ffi_c/Buffer.c b/ext/ffi_c/Buffer.c
index 0bfcc02..78339f3 100644
--- a/ext/ffi_c/Buffer.c
+++ b/ext/ffi_c/Buffer.c
@@ -67,7 +67,7 @@ static const rb_data_type_t buffer_data_type = {
.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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
static const rb_data_type_t allocated_buffer_data_type = {
@@ -80,7 +80,7 @@ static const rb_data_type_t allocated_buffer_data_type = {
.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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
diff --git a/ext/ffi_c/DynamicLibrary.c b/ext/ffi_c/DynamicLibrary.c
index 9bfe86e..9abafc7 100644
--- a/ext/ffi_c/DynamicLibrary.c
+++ b/ext/ffi_c/DynamicLibrary.c
@@ -73,7 +73,7 @@ static const rb_data_type_t rbffi_library_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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
static const rb_data_type_t library_symbol_data_type = {
@@ -87,7 +87,7 @@ static const rb_data_type_t library_symbol_data_type = {
.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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
static VALUE LibraryClass = Qnil, SymbolClass = Qnil;
diff --git a/ext/ffi_c/Function.c b/ext/ffi_c/Function.c
index 0e68722..a74136a 100644
--- a/ext/ffi_c/Function.c
+++ b/ext/ffi_c/Function.c
@@ -108,7 +108,7 @@ static const rb_data_type_t function_data_type = {
.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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
VALUE rbffi_FunctionClass = Qnil;
@@ -462,6 +462,7 @@ function_set_autorelease(VALUE self, VALUE autorelease)
{
Function* fn;
+ rb_check_frozen(self);
TypedData_Get_Struct(self, Function, &function_data_type, fn);
fn->autorelease = RTEST(autorelease);
diff --git a/ext/ffi_c/FunctionInfo.c b/ext/ffi_c/FunctionInfo.c
index 7f77621..197c681 100644
--- a/ext/ffi_c/FunctionInfo.c
+++ b/ext/ffi_c/FunctionInfo.c
@@ -67,7 +67,7 @@ const rb_data_type_t rbffi_fntype_data_type = { /* extern */
.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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
VALUE rbffi_FunctionTypeClass = Qnil;
diff --git a/ext/ffi_c/LastError.c b/ext/ffi_c/LastError.c
index d5ef132..877a3d3 100644
--- a/ext/ffi_c/LastError.c
+++ b/ext/ffi_c/LastError.c
@@ -105,7 +105,7 @@ static const rb_data_type_t thread_data_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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
static ID id_thread_data;
@@ -173,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 b1532e1..e61af30 100644
--- a/ext/ffi_c/MappedType.c
+++ b/ext/ffi_c/MappedType.c
@@ -57,7 +57,7 @@ static const rb_data_type_t mapped_type_data_type = {
.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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
diff --git a/ext/ffi_c/MemoryPointer.c b/ext/ffi_c/MemoryPointer.c
index 8227183..a60168e 100644
--- a/ext/ffi_c/MemoryPointer.c
+++ b/ext/ffi_c/MemoryPointer.c
@@ -64,7 +64,7 @@ static const rb_data_type_t memory_pointer_data_type = {
.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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
static VALUE
@@ -136,6 +136,7 @@ memptr_free(VALUE self)
{
Pointer* ptr;
+ rb_check_frozen(self);
TypedData_Get_Struct(self, Pointer, &memory_pointer_data_type, ptr);
if (ptr->allocated) {
diff --git a/ext/ffi_c/Pointer.c b/ext/ffi_c/Pointer.c
index a6e8eb7..dae853a 100644
--- a/ext/ffi_c/Pointer.c
+++ b/ext/ffi_c/Pointer.c
@@ -33,6 +33,7 @@
#include <ruby.h>
#include "rbffi.h"
#include "rbffi_endian.h"
+#include "compat.h"
#include "AbstractMemory.h"
#include "Pointer.h"
@@ -57,7 +58,7 @@ const rb_data_type_t rbffi_pointer_data_type = { /* extern */
.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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
VALUE
@@ -397,6 +398,7 @@ ptr_free(VALUE self)
{
Pointer* ptr;
+ rb_check_frozen(self);
TypedData_Get_Struct(self, Pointer, &rbffi_pointer_data_type, ptr);
if (ptr->allocated) {
@@ -436,6 +438,7 @@ ptr_autorelease(VALUE self, VALUE autorelease)
{
Pointer* ptr;
+ rb_check_frozen(self);
TypedData_Get_Struct(self, Pointer, &rbffi_pointer_data_type, ptr);
ptr->autorelease = autorelease == Qtrue;
diff --git a/ext/ffi_c/Struct.c b/ext/ffi_c/Struct.c
index b9b6b2c..99b164d 100644
--- a/ext/ffi_c/Struct.c
+++ b/ext/ffi_c/Struct.c
@@ -82,7 +82,7 @@ const rb_data_type_t rbffi_struct_data_type = { /* extern */
},
// 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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
VALUE rbffi_StructClass = Qnil;
@@ -382,6 +382,7 @@ 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);
@@ -421,6 +422,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));
@@ -471,6 +473,7 @@ struct_set_layout(VALUE self, VALUE layout)
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));
@@ -544,7 +547,7 @@ static const rb_data_type_t inline_array_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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
static VALUE
@@ -684,6 +687,7 @@ inline_array_aset(VALUE self, VALUE rbIndex, VALUE rbValue)
{
InlineArray* array;
+ rb_check_frozen(self);
TypedData_Get_Struct(self, InlineArray, &inline_array_data_type, array);
if (array->op != NULL) {
diff --git a/ext/ffi_c/StructByValue.c b/ext/ffi_c/StructByValue.c
index 94061f3..df03684 100644
--- a/ext/ffi_c/StructByValue.c
+++ b/ext/ffi_c/StructByValue.c
@@ -67,7 +67,7 @@ static const rb_data_type_t sbv_type_data_type = {
.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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
static VALUE
diff --git a/ext/ffi_c/StructLayout.c b/ext/ffi_c/StructLayout.c
index 548ddb5..613394a 100644
--- a/ext/ffi_c/StructLayout.c
+++ b/ext/ffi_c/StructLayout.c
@@ -77,7 +77,7 @@ const rb_data_type_t rbffi_struct_layout_data_type = { /* extern */
.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
+ .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 */
@@ -91,7 +91,7 @@ const rb_data_type_t rbffi_struct_field_data_type = { /* extern */
.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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
static VALUE
diff --git a/ext/ffi_c/Type.c b/ext/ffi_c/Type.c
index 194c81e..3431681 100644
--- a/ext/ffi_c/Type.c
+++ b/ext/ffi_c/Type.c
@@ -64,7 +64,7 @@ const rb_data_type_t rbffi_type_data_type = { /* extern */
},
// 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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
static const rb_data_type_t builtin_type_data_type = {
@@ -77,7 +77,7 @@ static const rb_data_type_t builtin_type_data_type = {
.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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
static size_t
diff --git a/ext/ffi_c/Variadic.c b/ext/ffi_c/Variadic.c
index a255969..2e9d790 100644
--- a/ext/ffi_c/Variadic.c
+++ b/ext/ffi_c/Variadic.c
@@ -81,7 +81,7 @@ static const rb_data_type_t variadic_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
+ .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | FFI_RUBY_TYPED_FROZEN_SHAREABLE
};
diff --git a/ext/ffi_c/compat.h b/ext/ffi_c/compat.h
index 889a4be..a8fdcbe 100644
--- a/ext/ffi_c/compat.h
+++ b/ext/ffi_c/compat.h
@@ -79,6 +79,8 @@
# define RB_GC_GUARD(x) (x)
#endif
+
+/* 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)
@@ -88,4 +90,13 @@
#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
+
+
#endif /* RBFFI_COMPAT_H */
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/lib/ffi/autopointer.rb b/lib/ffi/autopointer.rb
index 6c6333c..bd4c911 100644
--- a/lib/ffi/autopointer.rb
+++ b/lib/ffi/autopointer.rb
@@ -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
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/ffi.rb b/lib/ffi/ffi.rb
index dfffa8c..31f25d1 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'
diff --git a/lib/ffi/struct.rb b/lib/ffi/struct.rb
index 725b9cb..1283d63 100644
--- a/lib/ffi/struct.rb
+++ b/lib/ffi/struct.rb
@@ -219,7 +219,7 @@ module FFI
end
builder.size = @size if defined?(@size) && @size > builder.size
cspec = builder.build
- @layout = cspec unless self == Struct
+ @layout = FFI.make_shareable(cspec) unless self == Struct
@size = cspec.size
return cspec
end
diff --git a/spec/ffi/function_spec.rb b/spec/ffi/function_spec.rb
index 7f90ea3..0d89a7d 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 "should be 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(@conninfo) do |conninfo|
+ 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
diff --git a/spec/ffi/memorypointer_spec.rb b/spec/ffi/memorypointer_spec.rb
index c8d1b42..904fdc9 100644
--- a/spec/ffi/memorypointer_spec.rb
+++ b/spec/ffi/memorypointer_spec.rb
@@ -28,6 +28,19 @@ 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
+ 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 +89,20 @@ 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
+ ptr = MemoryPointer.new(8).freeze
+ expect{ ptr.autorelease = false }.to raise_error(FrozenError)
+ end
+end
diff --git a/spec/ffi/pointer_spec.rb b/spec/ffi/pointer_spec.rb
index c8242c7..7232a5f 100644
--- a/spec/ffi/pointer_spec.rb
+++ b/spec/ffi/pointer_spec.rb
@@ -90,6 +90,11 @@ describe "Pointer" do
expect(PointerTestLib.ptr_ret_pointer(memory, 0).address).to eq(0xdeadbeef)
end
+ it "#write_pointer frozen object" do
+ 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
@@ -360,6 +365,11 @@ 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
diff --git a/spec/ffi/spec_helper.rb b/spec/ffi/spec_helper.rb
index e9ced3d..63f8b78 100644
--- a/spec/ffi/spec_helper.rb
+++ b/spec/ffi/spec_helper.rb
@@ -9,6 +9,7 @@ require 'objspace'
RSpec.configure do |c|
c.filter_run_excluding :broken => true
+ c.filter_run_excluding( :ractor ) unless defined?(Ractor)
end
module TestLibrary
diff --git a/spec/ffi/struct_spec.rb b/spec/ffi/struct_spec.rb
index 0ce5273..897e083 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 "should be 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(@conninfo) do |conninfo|
+ s = TestStructRactor.new
+ s[:i] = 0x14
+ LibTest.ptr_ret_int32_t(s, 0)
+ end.take
+
+ expect( res ).to eq(0x14)
+ end
end
end