8000 [ruby/fiddle] Add Fiddle::Closure.create and Fiddle::Closure.free · ruby/ruby@255e617 · GitHub
[go: up one dir, main page]

Skip to content

Commit 255e617

Browse files
kouhsbt
authored andcommitted
[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 ruby/fiddle#102 (comment) for details. Reported by Vít Ondruch. Thanks!!! ruby/fiddle@a0ccc6bb1b
1 parent 191b91f commit 255e617

File tree

3 files changed

+131
-47
lines changed

3 files changed

+131
-47
lines changed

ext/fiddle/closure.c

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,17 @@ allocate(VALUE klass)
224224
return i;
225225
}
226226

227+
static fiddle_closure *
228+
get_raw(VALUE self)
229+
{
230+
fiddle_closure *closure;
231+
TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, closure);
232+
if (!closure) {
233+
rb_raise(rb_eArgError, "already freed: %+"PRIsVALUE, self);
234+
}
235+
return closure;
236+
}
237+
227238
static VALUE
228239
initialize(int rbargc, VALUE argv[], VALUE self)
229240
{
@@ -293,14 +304,28 @@ initialize(int rbargc, VALUE argv[], VALUE self)
293304
static VALUE
294305
to_i(VALUE self)
295306
{
296-
fiddle_closure * cl;
297-
void *code;
298-
299-
TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, cl);
307+
fiddle_closure *closure = get_raw(self);
308+
return PTR2NUM(closure->code);
309+
}
300310

301-
code = cl->code;
311+
static VALUE
312+
closure_free(VALUE self)
313+
{
314+
fiddle_closure *closure;
315+
TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, closure);
316+
if (closure) {
317+
dealloc(closure);
318+
RTYPEDDATA_DATA(self) = NULL;
319+
}
320+
return RUBY_Qnil;
321+
}
302322

303-
return PTR2NUM(code);
323+
static VALUE
324+
closure_freed_p(VALUE self)
325+
{
326+
fiddle_closure *closure;
327+
TypedData_Get_Struct(self, fiddle_closure, &closure_data_type, closure);
328+
return closure ? RUBY_Qfalse : RUBY_Qtrue;
304329
}
305330

306331
void
@@ -353,8 +378,24 @@ Init_fiddle_closure(void)
353378
/*
354379
* Document-method: to_i
355380
*
356-
* Returns the memory address for this closure
381+
* Returns the memory address for this closure.
357382
*/
358383
rb_define_method(cFiddleClosure, "to_i", to_i, 0);
384+
385+
/*
386+
* Document-method: free
387+
*
388+
* Free this closure explicitly. You can't use this closure anymore.
389+
*
390+
* If this closure is already freed, this does nothing.
391+
*/
392+
rb_define_method(cFiddleClosure, "free", closure_free, 0);
393+
394+
/*
395+
* Document-method: freed?
396+
*
397+
* Whether this closure was freed explicitly.
398+
*/
399+
rb_define_method(cFiddleClosure, "freed?", closure_freed_p, 0);
359400
}
360401
/* vim: set noet sw=4 sts=4 */

ext/fiddle/lib/fiddle/closure.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,31 @@
11
# frozen_string_literal: true
22
module Fiddle
33
class Closure
4+
class << self
5+
# Create a new closure. If a block is given, the created closure
6+
# is automatically freed after the given block is executed.
7+
#
8+
# The all given arguments are passed to Fiddle::Closure.new. So
9+
# using this method without block equals to Fiddle::Closure.new.
10+
#
11+
# == Example
12+
#
13+
# Fiddle::Closure.create(TYPE_INT, [TYPE_INT]) do |closure|
14+
# # closure is freed automatically when this block is finished.
15+
# end
16+
def create(*args)
17+
if block_given?
18+
closure = new(*args)
19+
begin
20+
yield(closure)
21+
ensure
22+
closure.free
23+
end
24+
else
25+
new(*args)
26+
end
27+
end
28+
end
429

530
# the C type of the return of the FFI closure
631
attr_reader :ctype

test/fiddle/test_closure.rb

Lines changed: 58 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -29,37 +29,40 @@ def test_argument_errors
2929
end
3030

3131
def test_type_symbol
32-
closure = Closure.new(:int, [:void])
33-
assert_equal([
34-
TYPE_INT,
35-
[TYPE_VOID],
36-
],
37-
[
38-
closure.instance_variable_get(:@ctype),
39-
closure.instance_variable_get(:@args),
40-
])
32+
Closure.create(:int, [:void]) do |closure|
33+
assert_equal([
34+
TYPE_INT,
35+
[TYPE_VOID],
36+
],
37+
[
38+
closure.instance_variable_get(:@ctype),
39+
closure.instance_variable_get(:@args),
40+
])
41+
end
4142
end
4243

4344
def test_call
44-
closure = Class.new(Closure) {
45+
closure_class = Class.new(Closure) do
4546
def call
4647
10
4748
end
48-
}.new(TYPE_INT, [])
49-
50-
func = Function.new(closure, [], TYPE_INT)
51-
assert_equal 10, func.call
49+
end
50+
closure_class.create(TYPE_INT, []) do |closure|
51+
func = Function.new(closure, [], TYPE_INT)
52+
assert_equal 10, func.call
53+
end
5254
end
5355

5456
def test_returner
55-
closure = Class.new(Closure) {
57+
closure_class = Class.new(Closure) do
5658
def call thing
5759
thing
5860
end
59-
}.new(TYPE_INT, [TYPE_INT])
60-
61-
func = Function.new(closure, [TYPE_INT], TYPE_INT)
62-
assert_equal 10, func.call(10)
61+
end
62+
closure_class.create(TYPE_INT, [TYPE_INT]) do |closure|
63+
func = Function.new(closure, [TYPE_INT], TYPE_INT)
64+
assert_equal 10, func.call(10)
65+
end
6366
end
6467

6568
def test_const_string
@@ -69,18 +72,35 @@ def call(string)
6972
@return_string
7073
end
7174
end
72-
closure = closure_class.new(:const_string, [:const_string])
75+
closure_class.create(:const_string, [:const_string]) do |closure|
76+
func = Function.new(closure, [:const_string], :const_string)
77+
assert_equal("Hello! World!", func.call("World!"))
78+
end
79+
end
7380

74-
func = Function.new(closure, [:const_string], :const_string)
75-
assert_equal("Hello! World!", func.call("World!"))
81+
def test_free
82+
Closure.create(:int, [:void]) do |closure|
83+
assert do
84+
not closure.freed?
85+
end
86+
closure.free
87+
assert do
88+
closure.freed?
89+
end
90+
closure.free
91+
end
7692
end
7793

7894
def test_block_caller
7995
cb = Closure::BlockCaller.new(TYPE_INT, [TYPE_INT]) do |one|
8096
one
8197
end
82-
func = Function.new(cb, [TYPE_INT], TYPE_INT)
83-
assert_equal 11, func.call(11)
98+
begin
99+
func = Function.new(cb, [TYPE_INT], TYPE_INT)
100+
assert_equal 11, func.call(11)
101+
ensure
102+
cb.free
103+
end
84104
end
85105

86106
def test_memsize
@@ -97,23 +117,21 @@ def test_memsize
97117
define_method("test_conversion_#{n.downcase}") do
98118
arg = nil
99119

100-
clos = Class.new(Closure) do
120+
closure_class = Class.new(Closure) do
101121
define_method(:call) {|x| arg = x}
102-
end.new(t, [t])
103-
104-
v = ~(~0 << (8*s))
105-
106-
arg = nil
107-
assert_equal(v, clos.call(v))
108-
assert_equal(arg, v, n)
109-
110-
arg = nil
111-
func = Function.new(clos, [t], t)
112-
assert_equal(v, func.call(v))
113-
assert_equal(arg, v, n)
114-
ensure
115-
clos = nil
116-
func = nil
122+
end
123+
closure_class.create(t, [t]) do |closure|
124+
v = ~(~0 << (8*s))
125+
126+
arg = nil
127+
assert_equal(v, closure.call(v))
128+
assert_equal(arg, v, n)
129+
130+
arg = nil
131+
func = Function.new(closure, [t], t)
132+
assert_equal(v, func.call(v))
133+
assert_equal(arg, v, n)
134+
end
117135
end
118136
end
119137
end

0 commit comments

Comments
 (0)
0