From 1b78458ff28819cb541d7410616d3d2907e30f73 Mon Sep 17 00:00:00 2001 From: Lars Kanis Date: Sun, 16 Apr 2023 22:10:32 +0200 Subject: Freeze global typedefs and add per Ractor local custom typedefs Ractor-local custom typedefs are used now, since global writable typedefs aren't compatible with Ractor isolation. Now the builtin typedefs are frozen and available for all Ractors. But all custom typedefs are only per Ractor. Actually the global typedefs already were useable in Ractors, if they are resolved in C code. This is because the C code doesn't check the Ractor boundaries. --- ext/ffi_c/Type.c | 36 ++++++++++++++++++++++++++++++++++++ lib/ffi/types.rb | 26 +++++++++++++++++++++----- spec/ffi/rbx/memory_pointer_spec.rb | 14 ++++++++++++++ 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/ext/ffi_c/Type.c b/ext/ffi_c/Type.c index 3431681..9bf5681 100644 --- a/ext/ffi_c/Type.c +++ b/ext/ffi_c/Type.c @@ -33,6 +33,9 @@ #include #include +#if HAVE_RB_EXT_RACTOR_SAFE +#include +#endif #include #include "rbffi.h" #include "compat.h" @@ -54,6 +57,9 @@ static VALUE classBuiltinType = Qnil; static VALUE moduleNativeType = Qnil; static VALUE typeMap = Qnil; static ID id_type_size = 0, id_size = 0; +#if HAVE_RB_EXT_RACTOR_SAFE +static rb_ractor_local_key_t custom_typedefs_key; +#endif const rb_data_type_t rbffi_type_data_type = { /* extern */ .wrap_struct_name = "FFI::Type", @@ -245,6 +251,25 @@ rbffi_type_size(VALUE type) } } +static VALUE +custom_typedefs(VALUE self) +{ +#if HAVE_RB_EXT_RACTOR_SAFE + VALUE hash = rb_ractor_local_storage_value(custom_typedefs_key); + if (hash == Qnil) { + hash = rb_hash_new(); + rb_ractor_local_storage_value_set(custom_typedefs_key, hash); + } +#else + static VALUE hash = Qundef; + if (hash == Qundef) { + rb_global_variable(&hash); + hash = rb_hash_new(); + } +#endif + return hash; +} + VALUE rbffi_Type_Lookup(VALUE name) { @@ -254,6 +279,12 @@ rbffi_Type_Lookup(VALUE name) * Try looking up directly in the type map */ VALUE nType; + VALUE cust = custom_typedefs(Qnil); + + if ((nType = rb_hash_lookup(cust, name)) != Qnil && rb_obj_is_kind_of(nType, rbffi_TypeClass)) { + return nType; + } + if ((nType = rb_hash_lookup(typeMap, name)) != Qnil && rb_obj_is_kind_of(nType, rbffi_TypeClass)) { return nType; } @@ -286,6 +317,11 @@ rbffi_Type_Init(VALUE moduleFFI) id_type_size = rb_intern("type_size"); id_size = rb_intern("size"); +#if HAVE_RB_EXT_RACTOR_SAFE + custom_typedefs_key = rb_ractor_local_storage_value_newkey(); +#endif + rb_define_module_function(moduleFFI, "custom_typedefs", custom_typedefs, 0); + /* * Document-class: FFI::Type::Builtin * Class for Built-in types. diff --git a/lib/ffi/types.rb b/lib/ffi/types.rb index 90f50c1..da214ee 100644 --- a/lib/ffi/types.rb +++ b/lib/ffi/types.rb @@ -37,8 +37,11 @@ module FFI # @param [Symbol] add new type definition's name to add # @return [Type] # Add a definition type to type definitions. + # + # The type definition is local per Ractor. def self.typedef(old, add) - TypeDefs[add] = self.find_type(old) + tm = custom_typedefs + tm[add] = self.find_type(old) end # (see FFI.typedef) @@ -46,6 +49,14 @@ module FFI typedef old, add end + class << self + private def __typedef(old, add) + TypeDefs[add] = self.find_type(old) + end + + private :custom_typedefs + end + # @param [Type, DataConverter, Symbol] name # @param [Hash] type_map if nil, {FFI::TypeDefs} is used @@ -57,9 +68,12 @@ module FFI if name.is_a?(Type) name - elsif type_map && type_map.has_key?(name) + elsif type_map&.has_key?(name) type_map[name] + elsif (tm=custom_typedefs).has_key?(name) + tm[name] + elsif TypeDefs.has_key?(name) TypeDefs[name] @@ -168,7 +182,7 @@ module FFI end end - typedef(StrPtrConverter, :strptr) + __typedef(StrPtrConverter, :strptr) # @param type +type+ is an instance of class accepted by {FFI.find_type} # @return [Numeric] @@ -184,11 +198,13 @@ module FFI f.each_line { |line| if line.index(prefix) == 0 new_type, orig_type = line.chomp.slice(prefix.length..-1).split(/\s*=\s*/) - typedef(orig_type.to_sym, new_type.to_sym) + __typedef(orig_type.to_sym, new_type.to_sym) end } end - typedef :pointer, :caddr_t + __typedef :pointer, :caddr_t rescue Errno::ENOENT end + + FFI.make_shareable(TypeDefs) end diff --git a/spec/ffi/rbx/memory_pointer_spec.rb b/spec/ffi/rbx/memory_pointer_spec.rb index 2db89a9..83d77c3 100644 --- a/spec/ffi/rbx/memory_pointer_spec.rb +++ b/spec/ffi/rbx/memory_pointer_spec.rb @@ -104,6 +104,20 @@ describe "MemoryPointer" do expect(m.read FFI::Type::BOOL).to eq(false) end + it "allows definition of a custom typedef" do + FFI.typedef :uint32, :fubar_t + expect(FFI.find_type(:fubar_t)).to eq(FFI::Type::Builtin::UINT32) + end + + it "allows overwriting of a default typedef" do + begin + FFI.typedef :uint32, :char + expect(FFI.find_type(:char)).to eq(FFI::Type::Builtin::UINT32) + ensure + FFI.typedef FFI::Type::Builtin::CHAR, :char + end + end + it "allows writing a custom typedef" do FFI.typedef :uint, :fubar_t FFI.typedef :size_t, :fubar2_t -- cgit v1.2.1