diff options
-rw-r--r-- | ext/ffi_c/DynamicLibrary.c | 7 | ||||
-rw-r--r-- | ext/ffi_c/Function.c | 12 | ||||
-rw-r--r-- | ext/ffi_c/FunctionInfo.c | 2 | ||||
-rw-r--r-- | ext/ffi_c/MappedType.c | 2 | ||||
-rw-r--r-- | ext/ffi_c/StructLayout.c | 7 | ||||
-rw-r--r-- | ext/ffi_c/Type.c | 4 | ||||
-rw-r--r-- | ext/ffi_c/Variadic.c | 5 | ||||
-rw-r--r-- | ext/ffi_c/rbffi.h | 2 | ||||
-rw-r--r-- | lib/ffi/library.rb | 36 | ||||
-rw-r--r-- | lib/ffi/struct.rb | 2 | ||||
-rw-r--r-- | lib/ffi/struct_layout.rb | 2 | ||||
-rw-r--r-- | lib/ffi/struct_layout_builder.rb | 4 | ||||
-rw-r--r-- | lib/ffi/variadic.rb | 7 | ||||
-rw-r--r-- | spec/ffi/dynamic_library_spec.rb | 6 | ||||
-rw-r--r-- | spec/ffi/enum_spec.rb | 24 | ||||
-rw-r--r-- | spec/ffi/function_spec.rb | 2 | ||||
-rw-r--r-- | spec/ffi/library_spec.rb | 57 | ||||
-rw-r--r-- | spec/ffi/struct_by_ref_spec.rb | 2 | ||||
-rw-r--r-- | spec/ffi/struct_spec.rb | 16 | ||||
-rw-r--r-- | spec/ffi/type_spec.rb | 35 | ||||
-rw-r--r-- | spec/ffi/variadic_spec.rb | 2 |
21 files changed, 175 insertions, 61 deletions
diff --git a/ext/ffi_c/DynamicLibrary.c b/ext/ffi_c/DynamicLibrary.c index 9abafc7..1d83940 100644 --- a/ext/ffi_c/DynamicLibrary.c +++ b/ext/ffi_c/DynamicLibrary.c @@ -161,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; } @@ -266,8 +268,9 @@ symbol_new(VALUE library, void* address, VALUE name) sym->base.memory.typeSize = 1; sym->base.memory.flags = MEM_RD | MEM_WR; RB_OBJ_WRITE(obj, &sym->base.rbParent, library); - RB_OBJ_WRITE(obj, &sym->name, name); + RB_OBJ_WRITE(obj, &sym->name, rb_str_new_frozen(name)); + rb_obj_freeze(obj); return obj; } diff --git a/ext/ffi_c/Function.c b/ext/ffi_c/Function.c index 9da6b37..d0b65fb 100644 --- a/ext/ffi_c/Function.c +++ b/ext/ffi_c/Function.c @@ -492,7 +492,7 @@ static VALUE function_attach(VALUE self, VALUE module, VALUE name) { Function* fn; - char var[1024]; + VALUE funcs; StringValue(name); TypedData_Get_Struct(self, Function, &function_data_type, fn); @@ -514,9 +514,13 @@ function_attach(VALUE self, VALUE module, VALUE name) /* * Stash the Function in a module variable so it does not get garbage collected and can be inspected by attached_functions */ - snprintf(var, sizeof(var), "@ffi_function_%s", StringValueCStr(name)); - rb_ractor_make_shareable(self); - rb_iv_set(module, var, self); + + funcs = rb_iv_get(module, "@ffi_functions"); + if (RB_NIL_P(funcs)) { + funcs = rb_hash_new(); + rb_iv_set(module, "@ffi_functions", funcs); + } + rb_hash_aset(funcs, rb_str_intern(name), self); rb_define_singleton_method(module, StringValueCStr(name), rbffi_MethodHandle_CodeAddress(fn->methodHandle), -1); diff --git a/ext/ffi_c/FunctionInfo.c b/ext/ffi_c/FunctionInfo.c index 7495215..b5150d8 100644 --- a/ext/ffi_c/FunctionInfo.c +++ b/ext/ffi_c/FunctionInfo.c @@ -251,6 +251,8 @@ fntype_initialize(int argc, VALUE* argv, VALUE self) fnInfo->invoke = rbffi_GetInvoker(fnInfo); + rb_obj_freeze(fnInfo->rbParameterTypes); + rb_obj_freeze(self); return self; } diff --git a/ext/ffi_c/MappedType.c b/ext/ffi_c/MappedType.c index 20b14e9..2e506f2 100644 --- a/ext/ffi_c/MappedType.c +++ b/ext/ffi_c/MappedType.c @@ -110,6 +110,8 @@ mapped_initialize(VALUE self, VALUE 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; } diff --git a/ext/ffi_c/StructLayout.c b/ext/ffi_c/StructLayout.c index 613394a..a56d48f 100644 --- a/ext/ffi_c/StructLayout.c +++ b/ext/ffi_c/StructLayout.c @@ -181,6 +181,8 @@ struct_field_initialize(int argc, VALUE* argv, VALUE self) break; } + rb_obj_freeze(self); + return self; } @@ -538,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; } diff --git a/ext/ffi_c/Type.c b/ext/ffi_c/Type.c index 9bf5681..a94c009 100644 --- a/ext/ffi_c/Type.c +++ b/ext/ffi_c/Type.c @@ -128,6 +128,8 @@ type_initialize(VALUE self, VALUE value) rb_raise(rb_eArgError, "wrong type"); } + rb_obj_freeze(self); + return self; } @@ -192,6 +194,8 @@ builtin_type_new(VALUE klass, int nativeType, ffi_type* ffiType, const char* nam type->type.nativeType = nativeType; type->type.ffiType = ffiType; + rb_obj_freeze(obj); + return obj; } diff --git a/ext/ffi_c/Variadic.c b/ext/ffi_c/Variadic.c index 0f321d9..09d7ce8 100644 --- a/ext/ffi_c/Variadic.c +++ b/ext/ffi_c/Variadic.c @@ -183,9 +183,8 @@ 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, "@type_map", rb_obj_dup(rb_hash_aref(options, ID2SYM(rb_intern("type_map"))))); - rb_ractor_make_shareable(self); + 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; } 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/lib/ffi/library.rb b/lib/ffi/library.rb index e681b3e..8af457d 100644 --- a/lib/ffi/library.rb +++ b/lib/ffi/library.rb @@ -295,9 +295,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_gsvar_#{mname} = s + @ffi_gsvars = {} unless defined?(@ffi_gsvars) + @ffi_gsvars[#{mname.inspect}] = s def self.#{mname} - @ffi_gsvar_#{mname} + @ffi_gsvars[#{mname.inspect}] end code @@ -309,12 +310,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 @@ -541,20 +543,30 @@ module FFI end def attached_functions - instance_variables.grep(/\A@ffi_function_(.*)/) do |m| - [$1, instance_variable_get(m)] - end.to_h + @ffi_functions || {} end def attached_variables ( - instance_variables.grep(/\A@ffi_gsvar_(.*)/) do |m| - [$1, instance_variable_get(m).class] + (@ffi_gsvars || {}).map do |name, gvar| + [name, gvar.class] end + - instance_variables.grep(/\A@ffi_gvar_(.*)/) do |m| - [$1, instance_variable_get(m).layout[:gvar].type] + (@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/struct.rb b/lib/ffi/struct.rb index 1283d63..725b9cb 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 = FFI.make_shareable(cspec) unless self == Struct + @layout = cspec unless self == Struct @size = cspec.size return cspec end 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 5488033..d7d26a2 100644 --- a/lib/ffi/struct_layout_builder.rb +++ b/lib/ffi/struct_layout_builder.rb @@ -97,7 +97,7 @@ module FFI # List of number types - NUMBER_TYPES = FFI.make_shareable([ + NUMBER_TYPES = [ Type::INT8, Type::UINT8, Type::INT16, @@ -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/variadic.rb b/lib/ffi/variadic.rb index 800d121..246b52f 100644 --- a/lib/ffi/variadic.rb +++ b/lib/ffi/variadic.rb @@ -54,11 +54,12 @@ module FFI invoker = self params = "*args" call = "call" - mod.module_eval <<-code - @ffi_function_#{mname} = invoker + mod.module_eval <<-code, __FILE__, __LINE__ + @ffi_functions = {} unless defined?(@ffi_functions) + @ffi_functions[#{mname.inspect}] = invoker def self.#{mname}(#{params}) - @ffi_function_#{mname}.#{call}(#{params}) + @ffi_functions[#{mname.inspect}].#{call}(#{params}) end define_method(#{mname.inspect}, &method(:#{mname})) diff --git a/spec/ffi/dynamic_library_spec.rb b/spec/ffi/dynamic_library_spec.rb index a6ea2a9..088a822 100644 --- a/spec/ffi/dynamic_library_spec.rb +++ b/spec/ffi/dynamic_library_spec.rb @@ -9,7 +9,6 @@ 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) - Ractor.make_shareable(libtest) res = Ractor.new(libtest) do |libtest2| libtest2.find_symbol("testClosureVrV").address @@ -52,5 +51,10 @@ describe FFI::DynamicLibrary do 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 1fba2cb..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 diff --git a/spec/ffi/function_spec.rb b/spec/ffi/function_spec.rb index badd240..77d09d8 100644 --- a/spec/ffi/function_spec.rb +++ b/spec/ffi/function_spec.rb @@ -52,7 +52,7 @@ describe FFI::Function do a + b end - it "should be shareable for Ractor", :ractor do + it "can be made shareable for Ractor", :ractor do add = FFI::Function.new(:int, [:int, :int], &method(:adder)) Ractor.make_shareable(add) diff --git a/spec/ffi/library_spec.rb b/spec/ffi/library_spec.rb index 52a961f..1b8e8c1 100644 --- a/spec/ffi/library_spec.rb +++ b/spec/ffi/library_spec.rb @@ -8,6 +8,7 @@ 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 @@ -197,28 +198,46 @@ 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 reveal the function type" do - mod = Module.new do |m| - m.extend FFI::Library - ffi_lib File.expand_path(TestLibrary::PATH) - attach_function :bool_return_true, [ :string ], :bool - 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 - 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) + it "can reveal the function type" do + 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) @@ -355,7 +374,7 @@ describe "Library" do ffi_lib TestLibrary::PATH attach_variable :gvari, "gvar_gstruct", GlobalStruct end - expect(lib.attached_variables).to eq({ "gvari" => GlobalStruct }) + expect(lib.attached_variables).to eq({ gvari: GlobalStruct }) end it "can reveal its attached global variables" do @@ -364,7 +383,7 @@ describe "Library" do ffi_lib TestLibrary::PATH attach_variable :gvaro, "gvar_u32", :uint32 end - expect(lib.attached_variables).to eq({ "gvaro" => FFI::Type::UINT32 }) + expect(lib.attached_variables).to eq({ gvaro: FFI::Type::UINT32 }) end it "should have shareable constants for Ractor", :ractor do diff --git a/spec/ffi/struct_by_ref_spec.rb b/spec/ffi/struct_by_ref_spec.rb index 0c01137..f664937 100644 --- a/spec/ffi/struct_by_ref_spec.rb +++ b/spec/ffi/struct_by_ref_spec.rb @@ -41,7 +41,7 @@ describe FFI::Struct, ' by_ref' do end it "can reveal the mapped type converter" do - param_type = @api.attached_functions["struct_test"].param_types[0] + 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 diff --git a/spec/ffi/struct_spec.rb b/spec/ffi/struct_spec.rb index d09b7b6..ab62da1 100644 --- a/spec/ffi/struct_spec.rb +++ b/spec/ffi/struct_spec.rb @@ -545,7 +545,7 @@ module StructSpecsStructTests expect(b.members).to eq([:a, :b]) end - it "should be shareable for Ractor", :ractor do + it "can be made shareable for Ractor", :ractor do a = Class.new(FFI::Struct) do layout :a, :char end.new @@ -1109,6 +1109,20 @@ describe "Struct memsize functions", skip: RUBY_ENGINE != "ruby" do 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 diff --git a/spec/ffi/type_spec.rb b/spec/ffi/type_spec.rb index eb48a43..e880c83 100644 --- a/spec/ffi/type_spec.rb +++ b/spec/ffi/type_spec.rb @@ -5,7 +5,7 @@ require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper")) -describe "FFI::Type" do +describe FFI::Type do it 'has a memsize function', skip: RUBY_ENGINE != "ruby" do base_size = ObjectSpace.memsize_of(Object.new) @@ -40,4 +40,37 @@ describe "FFI::Type" do 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 917212a..21a803f 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 |