Skip to content

Commit ac52d00

Browse files
authored
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.
1 parent 3b4de54 commit ac52d00

File tree

7 files changed

+173
-12
lines changed

7 files changed

+173
-12
lines changed

ext/fiddle/fiddle.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
#include <fiddle.h>
22

33
VALUE mFiddle;
4+
VALUE rb_eFiddleDLError;
45
VALUE rb_eFiddleError;
56

67
void Init_fiddle_pointer(void);
8+
void Init_fiddle_pinned(void);
79

810
/*
911
* call-seq: Fiddle.malloc(size)
@@ -132,12 +134,19 @@ Init_fiddle(void)
132134
*/
133135
mFiddle = rb_define_module("Fiddle");
134136

137+
/*
138+
* Document-class: Fiddle::Error
139+
*
140+
* Generic error class for Fiddle
141+
*/
142+
rb_eFiddleError = rb_define_class_under(mFiddle, "Error", rb_eStandardError);
143+
135144
/*
136145
* Document-class: Fiddle::DLError
137146
*
138147
* standard dynamic load exception
139148
*/
140-
rb_eFiddleError = rb_define_class_under(mFiddle, "DLError", rb_eStandardError);
149+
rb_eFiddleDLError = rb_define_class_under(mFiddle, "DLError", rb_eFiddleError);
141150

142151
/* Document-const: TYPE_VOID
143152
*
@@ -439,5 +448,6 @@ Init_fiddle(void)
439448
Init_fiddle_closure();
440449
Init_fiddle_handle();
441450
Init_fiddle_pointer();
451+
Init_fiddle_pinned();
442452
}
443453
/* vim: set noet sws=4 sw=4: */

ext/fiddle/fiddle.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@
164164
#define ALIGN_DOUBLE ALIGN_OF(double)
165165

166166
extern VALUE mFiddle;
167-
extern VALUE rb_eFiddleError;
167+
extern VALUE rb_eFiddleDLError;
168168

169169
VALUE rb_fiddle_new_function(VALUE address, VALUE arg_types, VALUE ret_type);
170170

ext/fiddle/handle.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,14 @@ rb_fiddle_handle_close(VALUE self)
7474
/* Check dlclose for successful return value */
7575
if(ret) {
7676
#if defined(HAVE_DLERROR)
77-
rb_raise(rb_eFiddleError, "%s", dlerror());
77+
rb_raise(rb_eFiddleDLError, "%s", dlerror());
7878
#else
79-
rb_raise(rb_eFiddleError, "could not close handle");
79+
rb_raise(rb_eFiddleDLError, "could not close handle");
8080
#endif
8181
}
8282
return INT2NUM(ret);
8383
}
84-
rb_raise(rb_eFiddleError, "dlclose() called too many times");
84+
rb_raise(rb_eFiddleDLError, "dlclose() called too many times");
8585

8686
UNREACHABLE;
8787
}
@@ -177,12 +177,12 @@ rb_fiddle_handle_initialize(int argc, VALUE argv[], VALUE self)
177177
ptr = dlopen(clib, cflag);
178178
#if defined(HAVE_DLERROR)
179179
if( !ptr && (err = dlerror()) ){
180-
rb_raise(rb_eFiddleError, "%s", err);
180+
rb_raise(rb_eFiddleDLError, "%s", err);
181181
}
182182
#else
183183
if( !ptr ){
184184
err = dlerror();
185-
rb_raise(rb_eFiddleError, "%s", err);
185+
rb_raise(rb_eFiddleDLError, "%s", err);
186186
}
187187
#endif
188188
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)
278278

279279
TypedData_Get_Struct(self, struct dl_handle, &fiddle_handle_data_type, fiddle_handle);
280280
if( ! fiddle_handle->open ){
281-
rb_raise(rb_eFiddleError, "closed handle");
281+
rb_raise(rb_eFiddleDLError, "closed handle");
282282
}
283283

284284
return fiddle_handle_sym(fiddle_handle->ptr, sym);
@@ -366,7 +366,7 @@ fiddle_handle_sym(void *handle, VALUE symbol)
366366
}
367367
#endif
368368
if( !func ){
369-
rb_raise(rb_eFiddleError, "unknown symbol \"%"PRIsVALUE"\"", symbol);
369+
rb_raise(rb_eFiddleDLError, "unknown symbol \"%"PRIsVALUE"\"", symbol);
370370
}
371371

372372
return PTR2NUM(func);

ext/fiddle/pinned.c

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
#include <fiddle.h>
2+
3+
VALUE rb_cPinned;
4+
VALUE rb_eFiddleClearedReferenceError;
5+
6+
struct pinned_data {
7+
VALUE ptr;
8+
};
9+
10+
static void
11+
pinned_mark(void *ptr)
12+
{
13+
struct pinned_data *data = (struct pinned_data*)ptr;
14+
/* Ensure reference is pinned */
15+
if (data->ptr) {
16+
rb_gc_mark(data->ptr);
17+
}
18+
}
19+
20+
static size_t
21+
pinned_memsize(const void *ptr)
22+
{
23+
return sizeof(struct pinned_data);
24+
}
25+
26+
static const rb_data_type_t pinned_data_type = {
27+
"fiddle/pinned",
28+
{pinned_mark, xfree, pinned_memsize, },
29+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED
30+
};
31+
32+
static VALUE
33+
allocate(VALUE klass)
34+
{
35+
struct pinned_data *data;
36+
VALUE obj = TypedData_Make_Struct(klass, struct pinned_data, &pinned_data_type, data);
37+
data->ptr = 0;
38+
return obj;
39+
}
40+
41+
/*
42+
* call-seq:
43+
* Fiddle::Pinned.new(object) => pinned_object
44+
*
45+
* Create a new pinned object reference. The Fiddle::Pinned instance will
46+
* prevent the GC from moving +object+.
47+
*/
48+
static VALUE
49+
initialize(VALUE self, VALUE ref)
50+
{
51+
struct pinned_data *data;
52+
TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data);
53+
RB_OBJ_WRITE(self, &data->ptr, ref);
54+
return self;
55+
}
56+
57+
/*
58+
* call-seq: ref
59+
*
60+
* Return the object that this pinned instance references.
61+
*/
62+
static VALUE
63+
ref(VALUE self)
64+
{
65+
struct pinned_data *data;
66+
TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data);
67+
if (data->ptr) {
68+
return data->ptr;
69+
} else {
70+
rb_raise(rb_eFiddleClearedReferenceError, "`ref` called on a cleared object");
71+
}
72+
}
73+
74+
/*
75+
* call-seq: clear
76+
*
77+
* Clear the reference to the object this is pinning.
78+
*/
79+
static VALUE
80+
clear(VALUE self)
81+
{
82+
struct pinned_data *data;
83+
TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data);
84+
data->ptr = 0;
85+
return self;
86+
}
87+
88+
/*
89+
* call-seq: cleared?
90+
*
91+
* Returns true if the reference has been cleared, otherwise returns false.
92+
*/
93+
static VALUE
94+
cleared_p(VALUE self)
95+
{
96+
struct pinned_data *data;
97+
TypedData_Get_Struct(self, struct pinned_data, &pinned_data_type, data);
98+
if (data->ptr) {
99+
return Qfalse;
100+
} else {
101+
return Qtrue;
102+
}
103+
}
104+
105+
extern VALUE rb_eFiddleError;
106+
107+
void
108+
Init_fiddle_pinned(void)
109+
{
110+
rb_cPinned = rb_define_class_under(mFiddle, "Pinned", rb_cObject);
111+
rb_define_alloc_func(rb_cPinned, allocate);
112+
rb_define_method(rb_cPinned, "initialize", initialize, 1);
113+
rb_define_method(rb_cPinned, "ref", ref, 0);
114+
rb_define_method(rb_cPinned, "clear", clear, 0);
115+
rb_define_method(rb_cPinned, "cleared?", cleared_p, 0);
116+
117+
/*
118+
* Document-class: Fiddle::ClearedReferenceError
119+
*
120+
* Cleared reference exception
121+
*/
122+
rb_eFiddleClearedReferenceError = rb_define_class_under(mFiddle, "ClearedReferenceError", rb_eFiddleError);
123+
}

ext/fiddle/pointer.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ rb_fiddle_ptr_aref(int argc, VALUE argv[], VALUE self)
622622
struct ptr_data *data;
623623

624624
TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
625-
if (!data->ptr) rb_raise(rb_eFiddleError, "NULL pointer dereference");
625+
if (!data->ptr) rb_raise(rb_eFiddleDLError, "NULL pointer dereference");
626626
switch( rb_scan_args(argc, argv, "11", &arg0, &arg1) ){
627627
case 1:
628628
offset = NUM2ULONG(arg0);
@@ -660,7 +660,7 @@ rb_fiddle_ptr_aset(int argc, VALUE argv[], VALUE self)
660660
struct ptr_data *data;
661661

662662
TypedData_Get_Struct(self, struct ptr_data, &fiddle_ptr_data_type, data);
663-
if (!data->ptr) rb_raise(rb_eFiddleError, "NULL pointer dereference");
663+
if (!data->ptr) rb_raise(rb_eFiddleDLError, "NULL pointer dereference");
664664
switch( rb_scan_args(argc, argv, "21", &arg0, &arg1, &arg2) ){
665665
case 2:
666666
offset = NUM2ULONG(arg0);
@@ -741,7 +741,7 @@ rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE val)
741741
wrap = 0;
742742
}
743743
else{
744-
rb_raise(rb_eFiddleError, "to_ptr should return a Fiddle::Pointer object");
744+
rb_raise(rb_eFiddleDLError, "to_ptr should return a Fiddle::Pointer object");
745745
}
746746
}
747747
else{

fiddle.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ Gem::Specification.new do |spec|
3434
"ext/fiddle/function.c",
3535
"ext/fiddle/function.h",
3636
"ext/fiddle/handle.c",
37+
"ext/fiddle/pinned.c",
3738
"ext/fiddle/pointer.c",
3839
"ext/fiddle/win32/fficonfig.h",
3940
"ext/fiddle/win32/libffi-3.2.1-mswin.patch",

test/fiddle/test_pinned.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# frozen_string_literal: true
2+
begin
3+
require_relative 'helper'
4+
rescue LoadError
5+
end
6+
7+
module Fiddle
8+
class TestPinned < Fiddle::TestCase
9+
def test_pin_object
10+
x = Object.new
11+
pinner = Pinned.new x
12+
assert_same x, pinner.ref
13+
end
14+
15+
def test_clear
16+
pinner = Pinned.new Object.new
17+
refute pinner.cleared?
18+
pinner.clear
19+
assert pinner.cleared?
20+
ex = assert_raise(Fiddle::ClearedReferenceError) do
21+
pinner.ref
22+
end
23+
assert_match "called on", ex.message
24+
end
25+
end
26+
end
27+

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy