summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Kanis <lars@greiz-reinsdorf.de>2023-04-16 22:10:32 +0200
committerLars Kanis <lars@greiz-reinsdorf.de>2023-04-18 13:17:10 +0200
commit1b78458ff28819cb541d7410616d3d2907e30f73 (patch)
tree92e3431545e21755abaa7a1408117abee36765df
parent82f5942f97731d8a46401d2465d007d89092a45c (diff)
downloadffi-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.c36
-rw-r--r--lib/ffi/types.rb26
-rw-r--r--spec/ffi/rbx/memory_pointer_spec.rb14
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