diff options
author | Lars Kanis <lars@greiz-reinsdorf.de> | 2023-04-16 22:10:32 +0200 |
---|---|---|
committer | Lars Kanis <lars@greiz-reinsdorf.de> | 2023-04-18 13:17:10 +0200 |
commit | 1b78458ff28819cb541d7410616d3d2907e30f73 (patch) | |
tree | 92e3431545e21755abaa7a1408117abee36765df | |
parent | 82f5942f97731d8a46401d2465d007d89092a45c (diff) | |
download | ffi-1b78458ff28819cb541d7410616d3d2907e30f73.tar.gz |
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.
-rw-r--r-- | ext/ffi_c/Type.c | 36 | ||||
-rw-r--r-- | lib/ffi/types.rb | 26 | ||||
-rw-r--r-- | 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 <sys/types.h> #include <ruby.h> +#if HAVE_RB_EXT_RACTOR_SAFE +#include <ruby/ractor.h> +#endif #include <ffi.h> #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 |