[前][次][番号順一覧][スレッド一覧]

ruby-changes:63632

From: Aaron <ko1@a...>
Date: Wed, 18 Nov 2020 09:05:38 +0900 (JST)
Subject: [ruby-changes:63632] 307388ea19 (master): [ruby/fiddle] Add a "pinning" reference (#44)

https://git.ruby-lang.org/ruby.git/commit/?id=307388ea19

From 307388ea19f5c9d1c8c417d1979c40d970b420a2 Mon Sep 17 00:00:00 2001
From: Aaron Patterson <tenderlove@r...>
Date: Sun, 2 Aug 2020 14:26:40 -0700
Subject: [ruby/fiddle] Add a "pinning" reference (#44)

* Add a "pinning" reference

A `Fiddle::Pinned` objects will prevent the objects they point to from
moving.  This is useful in the case where you need to pass a reference
to a C extension that keeps the address in a global and needs the
address to be stable.

For example:

```ruby
class Foo
  A = "hi" # this is an embedded string

  some_c_function A # A might move!
end
```

If `A` moves, then the underlying string buffer may also move.
`Fiddle::Pinned` will prevent the object from moving:

```ruby
class Foo
  A = "hi" # this is an embedded string

  A_pinner = Fiddle::Pinned.new(A) # :nodoc:

  some_c_function A # A can't move because of `Fiddle::Pinned`
end
```

This is a similar strategy to what Graal uses:

  https://www.graalvm.org/sdk/javadoc/org/graalvm/nativeimage/PinnedObject.html#getObject--

* rename global to match exception name

* Introduce generic Fiddle::Error and rearrange error classes

Fiddle::Error is the generic exception base class for Fiddle exceptions.
This commit introduces the class and rearranges Fiddle exceptions to
inherit from it.

https://github.com/ruby/fiddle/commit/ac52d00223

diff --git a/ext/fiddle/fiddle.c b/ext/fiddle/fiddle.c
index e6435ba..5f2bd5a 100644
--- a/ext/fiddle/fiddle.c
+++ b/ext/fiddle/fiddle.c
@@ -1,9 +1,11 @@ https://github.com/ruby/ruby/blob/trunk/ext/fiddle/fiddle.c#L1
 #include <fiddle.h>
 
 VALUE mFiddle;
+VALUE rb_eFiddleDLError;
 VALUE rb_eFiddleError;
 
 void Init_fiddle_pointer(void);
+void Init_fiddle_pinned(void);
 
 /*
  * call-seq: Fiddle.malloc(size)
@@ -133,11 +135,18 @@ Init_fiddle(void) https://github.com/ruby/ruby/blob/trunk/ext/fiddle/fiddle.c#L135
     mFiddle = rb_define_module("Fiddle");
 
     /*
+     * Document-class: Fiddle::Error
+     *
+     * Generic error class for Fiddle
+     */
+    rb_eFiddleError = rb_define_class_under(mFiddle, "Error", rb_eStandardError);
+
+    /*
      * Document-class: Fiddle::DLError
      *
      * standard dynamic load exception
      */
-    rb_eFiddleError = rb_define_class_under(mFiddle, "DLError", rb_eStandardError);
+    rb_eFiddleDLError = rb_define_class_under(mFiddle, "DLError", rb_eFiddleError);
 
     /* Document-const: TYPE_VOID
      *
@@ -439,5 +448,6 @@ Init_fiddle(void) https://github.com/ruby/ruby/blob/trunk/ext/fiddle/fiddle.c#L448
     Init_fiddle_closure();
     Init_fiddle_handle();
     Init_fiddle_pointer();
+    Init_fiddle_pinned();
 }
 /* vim: set noet sws=4 sw=4: */
diff --git a/ext/fiddle/fiddle.gemspec b/ext/fiddle/fiddle.gemspec
index a92955c..850b12d 100644
--- a/ext/fiddle/fiddle.gemspec
+++ b/ext/fiddle/fiddle.gemspec
@@ -39,6 +39,7 @@ Gem::Specification.new do |spec| https://github.com/ruby/ruby/blob/trunk/ext/fiddle/fiddle.gemspec#L39
     "ext/fiddle/function.c",
     "ext/fiddle/function.h",
     "ext/fiddle/handle.c",
+    "ext/fiddle/pinned.c",
     "ext/fiddle/pointer.c",
     "ext/fiddle/win32/fficonfig.h",
     "ext/fiddle/win32/libffi-3.2.1-mswin.patch",
diff --git a/ext/fiddle/fiddle.h b/ext/fiddle/fiddle.h
index 16f12ca..46f5a1d 100644
--- a/ext/fiddle/fiddle.h
+++ b/ext/fiddle/fiddle.h
@@ -164,7 +164,7 @@ https://github.com/ruby/ruby/blob/trunk/ext/fiddle/fiddle.h#L164
 #define ALIGN_DOUBLE ALIGN_OF(double)
 
 extern VALUE mFiddle;
-extern VALUE rb_eFiddleError;
+extern VALUE rb_eFiddleDLError;
 
 VALUE rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type);
 
diff --git a/ext/fiddle/handle.c b/ext/fiddle/handle.c
index 700924a..c1b2db5 100644
--- a/ext/fiddle/handle.c
+++ b/ext/fiddle/handle.c
@@ -74,14 +74,14 @@ rb_fiddle_handle_close(VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/fiddle/handle.c#L74
 	/* Check dlclose for successful return value */
 	if(ret) {
 #if defined(HAVE_DLERROR)
-	    rb_raise(rb_eFiddleError, "%s", dlerror());
+	    rb_raise(rb_eFiddleDLError, "%s", dlerror());
 #else
-	    rb_raise(rb_eFiddleError, "could not close handle");
+	    rb_raise(rb_eFiddleDLError, "could not close handle");
 #endif
 	}
 	return INT2NUM(ret);
     }
-    rb_raise(rb_eFiddleError, "dlclose() called too many times");
+    rb_raise(rb_eFiddleDLError, "dlclose() called too many times");
 
     UNREACHABLE;
 }
@@ -177,12 +177,12 @@ rb_fiddle_handle_initialize(int argc, VALUE argv[], VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/fiddle/handle.c#L177
 	ptr = dlopen(clib, cflag);
 #if defined(HAVE_DLERROR)
     if( !ptr && (err = dlerror()) ){
-	rb_raise(rb_eFiddleError, "%s", err);
+	rb_raise(rb_eFiddleDLError, "%s", err);
     }
 #else
     if( !ptr ){
 	err = dlerror();
-	rb_raise(rb_eFiddleError, "%s", err);
+	rb_raise(rb_eFiddleDLError, "%s", err);
     }
 #endif
     TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
@@ -278,7 +278,7 @@ rb_fiddle_handle_sym(VALUE self, VALUE sym) https://github.com/ruby/ruby/blob/trunk/ext/fiddle/handle.c#L278
 
     TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
     if( ! fiddle_handle->open ){
-	rb_raise(rb_eFiddleError, "closed handle");
+	rb_raise(rb_eFiddleDLError, "closed handle");
     }
 
     return fiddle_handle_sym(fiddle_handle->ptr, sym);
@@ -366,7 +366,7 @@ fiddle_handle_sym(void *handle, VALUE symbol) https://github.com/ruby/ruby/blob/trunk/ext/fiddle/handle.c#L366
     }
 #endif
     if( !func ){
-	rb_raise(rb_eFiddleError, "unknown symbol \"%"PRIsVALUE"\"", symbol);
+	rb_raise(rb_eFiddleDLError, "unknown symbol \"%"PRIsVALUE"\"", symbol);
     }
 
     return PTR2NUM(func);
diff --git a/ext/fiddle/pinned.c b/ext/fiddle/pinned.c
new file mode 100644
index 0000000..019a302
--- /dev/null
+++ b/ext/fiddle/pinned.c
@@ -0,0 +1,123 @@ https://github.com/ruby/ruby/blob/trunk/ext/fiddle/pinned.c#L1
+#include <fiddle.h>
+
+VALUE rb_cPinned;
+VALUE rb_eFiddleClearedReferenceError;
+
+struct pinned_data {
+    VALUE ptr;
+};
+
+static void
+pinned_mark(void *ptr)
+{
+    struct pinned_data *data = (struct pinned_data*)ptr;
+    /* Ensure reference is pinned */
+    if (data->ptr) {
+        rb_gc_mark(data->ptr);
+    }
+}
+
+static size_t
+pinned_memsize(const void *ptr)
+{
+    return sizeof(struct pinned_data);
+}
+
+static const rb_data_type_t pinned_data_type = {
+    "fiddle/pinned",
+    {pinned_mark, xfree, pinned_memsize, },
+    0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
+};
+
+static VALUE
+allocate(VALUE klass)
+{
+    struct pinned_data *data;
+    VALUE obj = TypedData_Make_Struct(klass, struct pinned_data, &pinned_data_type, data);
+    data->ptr = 0;
+    return obj;
+}
+
+/*
+ * call-seq:
+ *    Fiddle::Pinned.new(object)      => pinned_object
+ *
+ * Create a new pinned object reference.  The Fiddle::Pinned instance will
+ * prevent the GC from moving +object+.
+ */
+static VALUE
+initialize(VALUE self, VALUE ref)
+{
+    struct pinned_data *data;
+    TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data);
+    RB_OBJ_WRITE(self, &data->ptr, ref);
+    return self;
+}
+
+/*
+ * call-seq: ref
+ *
+ * Return the object that this pinned instance references.
+ */
+static VALUE
+ref(VALUE self)
+{
+    struct pinned_data *data;
+    TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data);
+    if (data->ptr) {
+      return data->ptr;
+    } else {
+      rb_raise(rb_eFiddleClearedReferenceError, "`ref` called on a cleared object");
+    }
+}
+
+/*
+ * call-seq: clear
+ *
+ * Clear the reference to the object this is pinning.
+ */
+static VALUE
+clear(VALUE self)
+{
+    struct pinned_data *data;
+    TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data);
+    data->ptr = 0;
+    return self;
+}
+
+/*
+ * call-seq: cleared?
+ *
+ * Returns true if the reference has been cleared, otherwise returns false.
+ */
+static VALUE
+cleared_p(VALUE self)
+{
+    struct pinned_data *data;
+    TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data);
+    if (data->ptr) {
+        return Qfalse;
+    } else {
+        return Qtrue;
+    }
+}
+
+extern VALUE rb_eFiddleError;
+
+void
+Init_fiddle_pinned(void)
+{
+    rb_cPinned = rb_define_class_under(mFiddle, "Pinned", rb_cObject);
+    rb_define_alloc_func(rb_cPinned, allocate);
+    rb_define_method(rb_cPinned, "initialize", initialize, 1);
+    rb_define_method(rb_cPinned, "ref", ref, 0);
+    rb_define_method(rb_cPinned, "clear", clear, 0);
+    rb_define_method(rb_cPinned, "cleared?", cleared_p, 0);
+
+    /*
+     * Document-class: Fiddle::ClearedReferenceError
+     *
+     * Cleared reference exception
+     */
+    rb_eFiddleClearedReferenceError = rb_define_class_under(mFiddle, "ClearedReferenceError", rb_eFiddleError);
+}
diff --git a/ext/fiddle/pointer.c b/ext/fiddle/pointer.c
index 7c60da4..b531bef 100644
--- a/ext/fiddle/pointer.c
+++ b/ext/fiddle/pointer.c
@@ -561,7 +561,7 @@ rb_fiddle_ptr_aref(int argc, VALUE argv[], VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/fiddle/pointer.c#L561
     struct ptr_data *data;
 
     TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
-    if (!data->ptr) rb_raise(rb_eFiddleError, "NULL pointer dereference");
+    if (!data->ptr) rb_raise(rb_eFiddleDLError, "NULL pointer dereference");
     switch( rb_scan_args(argc, argv, "11", &arg0, &arg1) ){
       case 1:
 	offset = NUM2ULONG(arg0);
@@ -599,7 +599,7 @@ rb_fiddle_ptr_aset(int argc, VALUE argv[], VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/fiddle/pointer.c#L599
     struct ptr_data *data;
 
     TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
-    if (!data->ptr) rb_raise(rb_eFiddleError, "NULL pointer dereference");
+    if (!data->ptr) rb_raise(rb_eFiddleDLError, "NULL pointer dereference");
     switch( rb_scan_args(argc, argv, "21", &arg0, &arg1, &arg2) ){
       case 2:
 	offset = NU (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]