diff options
-rw-r--r-- | ext/ffi_c/AbstractMemory.c | 18 | ||||
-rw-r--r-- | ext/ffi_c/ArrayType.c | 2 | ||||
-rw-r--r-- | ext/ffi_c/Buffer.c | 4 | ||||
-rw-r--r-- | ext/ffi_c/DynamicLibrary.c | 4 | ||||
-rw-r--r-- | ext/ffi_c/Function.c | 3 | ||||
-rw-r--r-- | ext/ffi_c/FunctionInfo.c | 2 | ||||
-rw-r--r-- | ext/ffi_c/LastError.c | 3 | ||||
-rw-r--r-- | ext/ffi_c/MappedType.c | 2 | ||||
-rw-r--r-- | ext/ffi_c/MemoryPointer.c | 3 | ||||
-rw-r--r-- | ext/ffi_c/Pointer.c | 5 | ||||
-rw-r--r-- | ext/ffi_c/Struct.c | 8 | ||||
-rw-r--r-- | ext/ffi_c/StructByValue.c | 2 | ||||
-rw-r--r-- | ext/ffi_c/StructLayout.c | 4 | ||||
-rw-r--r-- | ext/ffi_c/Type.c | 4 | ||||
-rw-r--r-- | ext/ffi_c/Variadic.c | 2 | ||||
-rw-r--r-- | ext/ffi_c/compat.h | 11 | ||||
-rw-r--r-- | ext/ffi_c/ffi.c | 4 | ||||
-rw-r--r-- | lib/ffi/autopointer.rb | 1 | ||||
-rw-r--r-- | lib/ffi/compat.rb | 43 | ||||
-rw-r--r-- | lib/ffi/ffi.rb | 1 | ||||
-rw-r--r-- | lib/ffi/struct.rb | 2 | ||||
-rw-r--r-- | spec/ffi/function_spec.rb | 24 | ||||
-rw-r--r-- | spec/ffi/memorypointer_spec.rb | 30 | ||||
-rw-r--r-- | spec/ffi/pointer_spec.rb | 10 | ||||
-rw-r--r-- | spec/ffi/spec_helper.rb | 1 | ||||
-rw-r--r-- | spec/ffi/struct_spec.rb | 50 |
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 |