зеркало из https://github.com/github/ruby.git
[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:
Родитель
191b91f47a
Коммит
255e617bc3
|
@ -224,6 +224,17 @@ allocate(VALUE klass)
|
|||
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
|
||||
initialize(int rbargc, VALUE argv[], VALUE self)
|
||||
{
|
||||
|
@ -293,14 +304,28 @@ initialize(int rbargc, VALUE argv[], VALUE self)
|
|||
static VALUE
|
||||
to_i(VALUE self)
|
||||
{
|
||||
fiddle_closure * cl;
|
||||
void *code;
|
||||
fiddle_closure *closure = get_raw(self);
|
||||
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;
|
||||
|
||||
return PTR2NUM(code);
|
||||
static VALUE
|
||||
closure_freed_p(VALUE self)
|
||||
{
|
||||
fiddle_closure *closure;
|
||||
TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, closure);
|
||||
return closure ? RUBY_Qfalse : RUBY_Qtrue;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -353,8 +378,24 @@ Init_fiddle_closure(void)
|
|||
/*
|
||||
* 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);
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
|
|
|
@ -1,6 +1,31 @@
|
|||
# frozen_string_literal: true
|
||||
module Fiddle
|
||||
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
|
||||
attr_reader :ctype
|
||||
|
|
|
@ -29,37 +29,40 @@ module Fiddle
|
|||
end
|
||||
|
||||
def test_type_symbol
|
||||
closure = Closure.new(:int, [:void])
|
||||
assert_equal([
|
||||
TYPE_INT,
|
||||
[TYPE_VOID],
|
||||
],
|
||||
[
|
||||
closure.instance_variable_get(:@ctype),
|
||||
closure.instance_variable_get(:@args),
|
||||
])
|
||||
Closure.create(:int, [:void]) do |closure|
|
||||
assert_equal([
|
||||
TYPE_INT,
|
||||
[TYPE_VOID],
|
||||
],
|
||||
[
|
||||
closure.instance_variable_get(:@ctype),
|
||||
closure.instance_variable_get(:@args),
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
def test_call
|
||||
closure = Class.new(Closure) {
|
||||
closure_class = Class.new(Closure) do
|
||||
def call
|
||||
10
|
||||
end
|
||||
}.new(TYPE_INT, [])
|
||||
|
||||
func = Function.new(closure, [], TYPE_INT)
|
||||
assert_equal 10, func.call
|
||||
end
|
||||
closure_class.create(TYPE_INT, []) do |closure|
|
||||
func = Function.new(closure, [], TYPE_INT)
|
||||
assert_equal 10, func.call
|
||||
end
|
||||
end
|
||||
|
||||
def test_returner
|
||||
closure = Class.new(Closure) {
|
||||
closure_class = Class.new(Closure) do
|
||||
def call thing
|
||||
thing
|
||||
end
|
||||
}.new(TYPE_INT, [TYPE_INT])
|
||||
|
||||
func = Function.new(closure, [TYPE_INT], TYPE_INT)
|
||||
assert_equal 10, func.call(10)
|
||||
end
|
||||
closure_class.create(TYPE_INT, [TYPE_INT]) do |closure|
|
||||
func = Function.new(closure, [TYPE_INT], TYPE_INT)
|
||||
assert_equal 10, func.call(10)
|
||||
end
|
||||
end
|
||||
|
||||
def test_const_string
|
||||
|
@ -69,18 +72,35 @@ module Fiddle
|
|||
@return_string
|
||||
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)
|
||||
assert_equal("Hello! World!", func.call("World!"))
|
||||
def test_free
|
||||
Closure.create(:int, [:void]) do |closure|
|
||||
assert do
|
||||
not closure.freed?
|
||||
end
|
||||
closure.free
|
||||
assert do
|
||||
closure.freed?
|
||||
end
|
||||
closure.free
|
||||
end
|
||||
end
|
||||
|
||||
def test_block_caller
|
||||
cb = Closure::BlockCaller.new(TYPE_INT, [TYPE_INT]) do |one|
|
||||
one
|
||||
end
|
||||
func = Function.new(cb, [TYPE_INT], TYPE_INT)
|
||||
assert_equal 11, func.call(11)
|
||||
begin
|
||||
func = Function.new(cb, [TYPE_INT], TYPE_INT)
|
||||
assert_equal 11, func.call(11)
|
||||
ensure
|
||||
cb.free
|
||||
end
|
||||
end
|
||||
|
||||
def test_memsize
|
||||
|
@ -97,23 +117,21 @@ module Fiddle
|
|||
define_method("test_conversion_#{n.downcase}") do
|
||||
arg = nil
|
||||
|
||||
clos = Class.new(Closure) do
|
||||
closure_class = Class.new(Closure) do
|
||||
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
|
||||
assert_equal(v, clos.call(v))
|
||||
assert_equal(arg, v, n)
|
||||
|
||||
arg = nil
|
||||
func = Function.new(clos, [t], t)
|
||||
assert_equal(v, func.call(v))
|
||||
assert_equal(arg, v, n)
|
||||
ensure
|
||||
clos = nil
|
||||
func = nil
|
||||
arg = nil
|
||||
func = Function.new(closure, [t], t)
|
||||
assert_equal(v, func.call(v))
|
||||
assert_equal(arg, v, n)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Загрузка…
Ссылка в новой задаче