Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion ext/fiddle/fiddle.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#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)
Expand Down Expand Up @@ -132,12 +134,19 @@ Init_fiddle(void)
*/
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
*
Expand Down Expand Up @@ -439,5 +448,6 @@ Init_fiddle(void)
Init_fiddle_closure();
Init_fiddle_handle();
Init_fiddle_pointer();
Init_fiddle_pinned();
}
/* vim: set noet sws=4 sw=4: */
2 changes: 1 addition & 1 deletion ext/fiddle/fiddle.h
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@
#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);

Expand Down
14 changes: 7 additions & 7 deletions ext/fiddle/handle.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,14 @@ rb_fiddle_handle_close(VALUE self)
/* 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;
}
Expand Down Expand Up @@ -177,12 +177,12 @@ rb_fiddle_handle_initialize(int argc, VALUE argv[], VALUE self)
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);
Expand Down Expand Up @@ -278,7 +278,7 @@ rb_fiddle_handle_sym(VALUE self, VALUE sym)

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);
Expand Down Expand Up @@ -366,7 +366,7 @@ fiddle_handle_sym(void *handle, VALUE symbol)
}
#endif
if( !func ){
rb_raise(rb_eFiddleError, "unknown symbol \"%"PRIsVALUE"\"", symbol);
rb_raise(rb_eFiddleDLError, "unknown symbol \"%"PRIsVALUE"\"", symbol);
}

return PTR2NUM(func);
Expand Down
123 changes: 123 additions & 0 deletions ext/fiddle/pinned.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
#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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we want a more explicit API in MRI to pin? You and I understand why this would pin the object, not sure everyone would.

}
}

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);
}
6 changes: 3 additions & 3 deletions ext/fiddle/pointer.c
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ rb_fiddle_ptr_aref(int argc, VALUE argv[], VALUE self)
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);
Expand Down Expand Up @@ -660,7 +660,7 @@ rb_fiddle_ptr_aset(int argc, VALUE argv[], VALUE self)
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 = NUM2ULONG(arg0);
Expand Down Expand Up @@ -741,7 +741,7 @@ rb_fiddle_ptr_s_to_ptr(VALUE self, VALUE val)
wrap = 0;
}
else{
rb_raise(rb_eFiddleError, "to_ptr should return a Fiddle::Pointer object");
rb_raise(rb_eFiddleDLError, "to_ptr should return a Fiddle::Pointer object");
}
}
else{
Expand Down
1 change: 1 addition & 0 deletions fiddle.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ Gem::Specification.new do |spec|
"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",
Expand Down
27 changes: 27 additions & 0 deletions test/fiddle/test_pinned.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# frozen_string_literal: true
begin
require_relative 'helper'
rescue LoadError
end

module Fiddle
class TestPinned < Fiddle::TestCase
def test_pin_object
x = Object.new
pinner = Pinned.new x
assert_same x, pinner.ref
end

def test_clear
pinner = Pinned.new Object.new
refute pinner.cleared?
pinner.clear
assert pinner.cleared?
ex = assert_raise(Fiddle::ClearedReferenceError) do
pinner.ref
end
assert_match "called on", ex.message
end
end
end