[ruby/fiddle] Add Fiddle::Closure.create and Fiddle::Closure.free
GitHub: fix GH-102 It's for freeing a closure explicitly. We can't use Fiddle::Closure before we fork the process. If we do it, the process may be crashed with SELinux. See https://github.com/ruby/fiddle/issues/102#issuecomment-1241763091 for details. Reported by Vít Ondruch. Thanks!!! https://github.com/ruby/fiddle/commit/a0ccc6bb1b
This commit is contained in:
parent
191b91f47a
commit
255e617bc3
@ -224,6 +224,17 @@ allocate(VALUE klass)
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static fiddle_closure *
|
||||||
|
get_raw(VALUE self)
|
||||||
|
{
|
||||||
|
fiddle_closure *closure;
|
||||||
|
TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, closure);
|
||||||
|
if (!closure) {
|
||||||
|
rb_raise(rb_eArgError, "already freed: %+"PRIsVALUE, self);
|
||||||
|
}
|
||||||
|
return closure;
|
||||||
|
}
|
||||||
|
|
||||||
static VALUE
|
static VALUE
|
||||||
initialize(int rbargc, VALUE argv[], VALUE self)
|
initialize(int rbargc, VALUE argv[], VALUE self)
|
||||||
{
|
{
|
||||||
@ -293,14 +304,28 @@ initialize(int rbargc, VALUE argv[], VALUE self)
|
|||||||
static VALUE
|
static VALUE
|
||||||
to_i(VALUE self)
|
to_i(VALUE self)
|
||||||
{
|
{
|
||||||
fiddle_closure * cl;
|
fiddle_closure *closure = get_raw(self);
|
||||||
void *code;
|
return PTR2NUM(closure->code);
|
||||||
|
}
|
||||||
|
|
||||||
TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
|
static VALUE
|
||||||
|
closure_free(VALUE self)
|
||||||
|
{
|
||||||
|
fiddle_closure *closure;
|
||||||
|
TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, closure);
|
||||||
|
if (closure) {
|
||||||
|
dealloc(closure);
|
||||||
|
RTYPEDDATA_DATA(self) = NULL;
|
||||||
|
}
|
||||||
|
return RUBY_Qnil;
|
||||||
|
}
|
||||||
|
|
||||||
code = cl->code;
|
static VALUE
|
||||||
|
closure_freed_p(VALUE self)
|
||||||
return PTR2NUM(code);
|
{
|
||||||
|
fiddle_closure *closure;
|
||||||
|
TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, closure);
|
||||||
|
return closure ? RUBY_Qfalse : RUBY_Qtrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -353,8 +378,24 @@ Init_fiddle_closure(void)
|
|||||||
/*
|
/*
|
||||||
* Document-method: to_i
|
* Document-method: to_i
|
||||||
*
|
*
|
||||||
* Returns the memory address for this closure
|
* Returns the memory address for this closure.
|
||||||
*/
|
*/
|
||||||
rb_define_method(cFiddleClosure, "to_i", to_i, 0);
|
rb_define_method(cFiddleClosure, "to_i", to_i, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Document-method: free
|
||||||
|
*
|
||||||
|
* Free this closure explicitly. You can't use this closure anymore.
|
||||||
|
*
|
||||||
|
* If this closure is already freed, this does nothing.
|
||||||
|
*/
|
||||||
|
rb_define_method(cFiddleClosure, "free", closure_free, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Document-method: freed?
|
||||||
|
*
|
||||||
|
* Whether this closure was freed explicitly.
|
||||||
|
*/
|
||||||
|
rb_define_method(cFiddleClosure, "freed?", closure_freed_p, 0);
|
||||||
}
|
}
|
||||||
/* vim: set noet sw=4 sts=4 */
|
/* vim: set noet sw=4 sts=4 */
|
||||||
|
@ -1,6 +1,31 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
module Fiddle
|
module Fiddle
|
||||||
class Closure
|
class Closure
|
||||||
|
class << self
|
||||||
|
# Create a new closure. If a block is given, the created closure
|
||||||
|
# is automatically freed after the given block is executed.
|
||||||
|
#
|
||||||
|
# The all given arguments are passed to Fiddle::Closure.new. So
|
||||||
|
# using this method without block equals to Fiddle::Closure.new.
|
||||||
|
#
|
||||||
|
# == Example
|
||||||
|
#
|
||||||
|
# Fiddle::Closure.create(TYPE_INT, [TYPE_INT]) do |closure|
|
||||||
|
# # closure is freed automatically when this block is finished.
|
||||||
|
# end
|
||||||
|
def create(*args)
|
||||||
|
if block_given?
|
||||||
|
closure = new(*args)
|
||||||
|
begin
|
||||||
|
yield(closure)
|
||||||
|
ensure
|
||||||
|
closure.free
|
||||||
|
end
|
||||||
|
else
|
||||||
|
new(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# the C type of the return of the FFI closure
|
# the C type of the return of the FFI closure
|
||||||
attr_reader :ctype
|
attr_reader :ctype
|
||||||
|
@ -29,37 +29,40 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_type_symbol
|
def test_type_symbol
|
||||||
closure = Closure.new(:int, [:void])
|
Closure.create(:int, [:void]) do |closure|
|
||||||
assert_equal([
|
assert_equal([
|
||||||
TYPE_INT,
|
TYPE_INT,
|
||||||
[TYPE_VOID],
|
[TYPE_VOID],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
closure.instance_variable_get(:@ctype),
|
closure.instance_variable_get(:@ctype),
|
||||||
closure.instance_variable_get(:@args),
|
closure.instance_variable_get(:@args),
|
||||||
])
|
])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_call
|
def test_call
|
||||||
closure = Class.new(Closure) {
|
closure_class = Class.new(Closure) do
|
||||||
def call
|
def call
|
||||||
10
|
10
|
||||||
end
|
end
|
||||||
}.new(TYPE_INT, [])
|
end
|
||||||
|
closure_class.create(TYPE_INT, []) do |closure|
|
||||||
func = Function.new(closure, [], TYPE_INT)
|
func = Function.new(closure, [], TYPE_INT)
|
||||||
assert_equal 10, func.call
|
assert_equal 10, func.call
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_returner
|
def test_returner
|
||||||
closure = Class.new(Closure) {
|
closure_class = Class.new(Closure) do
|
||||||
def call thing
|
def call thing
|
||||||
thing
|
thing
|
||||||
end
|
end
|
||||||
}.new(TYPE_INT, [TYPE_INT])
|
end
|
||||||
|
closure_class.create(TYPE_INT, [TYPE_INT]) do |closure|
|
||||||
func = Function.new(closure, [TYPE_INT], TYPE_INT)
|
func = Function.new(closure, [TYPE_INT], TYPE_INT)
|
||||||
assert_equal 10, func.call(10)
|
assert_equal 10, func.call(10)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_const_string
|
def test_const_string
|
||||||
@ -69,18 +72,35 @@ module Fiddle
|
|||||||
@return_string
|
@return_string
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
closure = closure_class.new(:const_string, [:const_string])
|
closure_class.create(:const_string, [:const_string]) do |closure|
|
||||||
|
func = Function.new(closure, [:const_string], :const_string)
|
||||||
|
assert_equal("Hello! World!", func.call("World!"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
func = Function.new(closure, [:const_string], :const_string)
|
def test_free
|
||||||
assert_equal("Hello! World!", func.call("World!"))
|
Closure.create(:int, [:void]) do |closure|
|
||||||
|
assert do
|
||||||
|
not closure.freed?
|
||||||
|
end
|
||||||
|
closure.free
|
||||||
|
assert do
|
||||||
|
closure.freed?
|
||||||
|
end
|
||||||
|
closure.free
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_block_caller
|
def test_block_caller
|
||||||
cb = Closure::BlockCaller.new(TYPE_INT, [TYPE_INT]) do |one|
|
cb = Closure::BlockCaller.new(TYPE_INT, [TYPE_INT]) do |one|
|
||||||
one
|
one
|
||||||
end
|
end
|
||||||
func = Function.new(cb, [TYPE_INT], TYPE_INT)
|
begin
|
||||||
assert_equal 11, func.call(11)
|
func = Function.new(cb, [TYPE_INT], TYPE_INT)
|
||||||
|
assert_equal 11, func.call(11)
|
||||||
|
ensure
|
||||||
|
cb.free
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_memsize
|
def test_memsize
|
||||||
@ -97,23 +117,21 @@ module Fiddle
|
|||||||
define_method("test_conversion_#{n.downcase}") do
|
define_method("test_conversion_#{n.downcase}") do
|
||||||
arg = nil
|
arg = nil
|
||||||
|
|
||||||
clos = Class.new(Closure) do
|
closure_class = Class.new(Closure) do
|
||||||
define_method(:call) {|x| arg = x}
|
define_method(:call) {|x| arg = x}
|
||||||
end.new(t, [t])
|
end
|
||||||
|
closure_class.create(t, [t]) do |closure|
|
||||||
|
v = ~(~0 << (8*s))
|
||||||
|
|
||||||
v = ~(~0 << (8*s))
|
arg = nil
|
||||||
|
assert_equal(v, closure.call(v))
|
||||||
|
assert_equal(arg, v, n)
|
||||||
|
|
||||||
arg = nil
|
arg = nil
|
||||||
assert_equal(v, clos.call(v))
|
func = Function.new(closure, [t], t)
|
||||||
assert_equal(arg, v, n)
|
assert_equal(v, func.call(v))
|
||||||
|
assert_equal(arg, v, n)
|
||||||
arg = nil
|
end
|
||||||
func = Function.new(clos, [t], t)
|
|
||||||
assert_equal(v, func.call(v))
|
|
||||||
assert_equal(arg, v, n)
|
|
||||||
ensure
|
|
||||||
clos = nil
|
|
||||||
func = nil
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user