summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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