8000 [ruby/fiddle] Improve documentation on how to correctly free memory a… · github/ruby@3015a7a · GitHub
[go: up one dir, main page]

Skip to content

Commit 3015a7a

Browse files
chrisseatonnobu
authored andcommitted
[ruby/fiddle] Improve documentation on how to correctly free memory and free memory in tests (#33)
ruby/fiddle@e59cfd708a
1 parent 24b615e commit 3015a7a

File tree

6 files changed

+125
-53
lines changed

6 files changed

+125
-53
lines changed

ext/fiddle/lib/fiddle/struct.rb

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,12 @@ module CStructBuilder
7373
#
7474
# MyStruct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members)
7575
#
76-
# obj = MyStruct.allocate
76+
# obj = MyStruct.malloc
77+
# begin
78+
# ...
79+
# ensure
80+
# Fiddle.free obj.to_ptr
81+
# end
7782
#
7883
def create(klass, types, members)
7984
new_class = Class.new(klass){
@@ -112,7 +117,7 @@ class CStructEntity < Fiddle::Pointer
112117

113118
# Allocates a C struct with the +types+ provided.
114119
#
115-
# When the instance is garbage collected, the C function +func+ is called.
120+
# See Fiddle::Pointer.malloc for memory management issues.
116121
def CStructEntity.malloc(types, func = nil)
117122
addr = Fiddle.malloc(CStructEntity.size(types))
118123
CStructEntity.new(addr, types, func)
@@ -267,7 +272,7 @@ class CUnionEntity < CStructEntity
267272

268273
# Allocates a C union the +types+ provided.
269274
#
270-
# When the instance is garbage collected, the C function +func+ is called.
275+
# See Fiddle::Pointer.malloc for memory management issues.
271276
def CUnionEntity.malloc(types, func=nil)
272277
addr = Fiddle.malloc(CUnionEntity.size(types))
273278
CUnionEntity.new(addr, types, func)

ext/fiddle/pointer.c

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -193,14 +193,34 @@ rb_fiddle_ptr_initialize(int argc, VALUE argv[], VALUE self)
193193

194194
/*
195195
* call-seq:
196-
*
197196
* Fiddle::Pointer.malloc(size, freefunc = nil) => fiddle pointer instance
198197
*
198+
* == Examples
199+
*
200+
* # Relying on the garbage collector - may lead to unlimited memory allocated before freeing any, but safe
201+
* pointer = Fiddle::Pointer.malloc(size, Fiddle::RUBY_FREE)
202+
* ...
203+
*
204+
* # Manual freeing
205+
* pointer = Fiddle::Pointer.malloc(size)
206+
* begin
207+
* ...
208+
* ensure
209+
* Fiddle.free pointer
210+
* end
211+
*
212+
* # No free function and no call to free - the native memory will leak if the pointer is garbage collected
213+
* pointer = Fiddle::Pointer.malloc(size)
214+
* ...
215+
*
199216
* Allocate +size+ bytes of memory and associate it with an optional
200217
* +freefunc+ that will be called when the pointer is garbage collected.
201-
*
202218
* +freefunc+ must be an address pointing to a function or an instance of
203-
* Fiddle::Function
219+
* +Fiddle::Function+. Using +freefunc+ may lead to unlimited memory being
220+
* allocated before any is freed as the native memory the pointer references
221+
* does not contribute to triggering the Ruby garbage collector. Consider
222+
* manually freeing the memory as illustrated above. You cannot combine
223+
* the techniques as this may lead to a double-free.
204224
*/
205225
static VALUE
206226
rb_fiddle_ptr_s_malloc(int argc, VALUE argv[], VALUE klass)

test/fiddle/test_c_struct_entry.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def test_class_size_with_count
4343
end
4444

4545
def test_set_ctypes
46-
union = CStructEntity.malloc [TYPE_INT, TYPE_LONG]
46+
union = CStructEntity.malloc [TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE
4747
union.assign_names %w[int long]
4848

4949
# this test is roundabout because the stored ctypes are not accessible
@@ -55,20 +55,20 @@ def test_set_ctypes
5555
end
5656

5757
def test_aref_pointer_array
58-
team = CStructEntity.malloc([[TYPE_VOIDP, 2]])
58+
team = CStructEntity.malloc([[TYPE_VOIDP, 2]], Fiddle::RUBY_FREE)
5959
team.assign_names(["names"])
60-
alice = Fiddle::Pointer.malloc(6)
60+
alice = Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE)
6161
alice[0, 6] = "Alice\0"
62-
bob = Fiddle::Pointer.malloc(4)
62+
bob = Fiddle::Pointer.malloc(4, Fiddle::RUBY_FREE)
6363
bob[0, 4] = "Bob\0"
6464
team["names"] = [alice, bob]
6565
assert_equal(["Alice", "Bob"], team["names"].map(&:to_s))
6666
end
6767

6868
def test_aref_pointer
69-
user = CStructEntity.malloc([TYPE_VOIDP])
69+
user = CStructEntity.malloc([TYPE_VOIDP], Fiddle::RUBY_FREE)
7070
user.assign_names(["name"])
71-
alice = Fiddle::Pointer.malloc(6)
71+
alice = Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE)
7272
alice[0, 6] = "Alice\0"
7373
user["name"] = alice
7474
assert_equal("Alice", user["name"].to_s)

test/fiddle/test_c_union_entity.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def test_class_size_with_count
2121
end
2222

2323
def test_set_ctypes
24-
union = CUnionEntity.malloc [TYPE_INT, TYPE_LONG]
24+
union = CUnionEntity.malloc [TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE
2525
union.assign_names %w[int long]
2626

2727
# this test is roundabout because the stored ctypes are not accessible

test/fiddle/test_import.rb

Lines changed: 70 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -57,35 +57,56 @@ def test_ensure_call_dlload
5757
def test_struct_memory_access()
5858
# check memory operations performed directly on struct
5959
my_struct = Fiddle::Importer.struct(['int id']).malloc
60-
my_struct[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
61-
assert_equal 0x01010101, my_struct.id
62-
63-
my_struct.id = 0
64-
assert_equal "\x00".b * Fiddle::SIZEOF_INT, my_struct[0, Fiddle::SIZEOF_INT]
60+
begin
61+
my_struct[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
62+
assert_equal 0x01010101, my_struct.id
63+
64+
my_struct.id = 0
65+
assert_equal "\x00".b * Fiddle::SIZEOF_INT, my_struct[0, Fiddle::SIZEOF_INT]
66+
ensure
67+
Fiddle.free my_struct.to_ptr
68+
end
6569
end
6670

6771
def test_struct_ptr_array_subscript_multiarg()
6872
# check memory operations performed on struct#to_ptr
6973
struct = Fiddle::Importer.struct([ 'int x' ]).malloc
70-
ptr = struct.to_ptr
74+
begin
75+
ptr = struct.to_ptr
7176

72-
struct.x = 0x02020202
73-
assert_equal("\x02".b * Fiddle::SIZEOF_INT, ptr[0, Fiddle::SIZEOF_INT])
77+
struct.x = 0x02020202
78+
assert_equal("\x02".b * Fiddle::SIZEOF_INT, ptr[0, Fiddle::SIZEOF_INT])
7479

75-
ptr[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
76-
assert_equal 0x01010101, struct.x
80+
ptr[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
81+
assert_equal 0x01010101, struct.x
82+
ensure
83+
Fiddle.free struct.to_ptr
84+
end
7785
end
7886

7987
def test_malloc()
8088
s1 = LIBC::Timeval.malloc()
81-
s2 = LIBC::Timeval.malloc()
82-
refute_equal(s1.to_ptr.to_i, s2.to_ptr.to_i)
89+
begin
90+
s2 = LIBC::Timeval.malloc()
91+
begin
92+
refute_equal(s1.to_ptr.to_i, s2.to_ptr.to_i)
93+
ensure
94+
Fiddle.free s2.to_ptr
95+
end
96+
ensure
97+
Fiddle.free s1.to_ptr
98+
end
8399
end
84100

85101
def test_sizeof()
86102
assert_equal(SIZEOF_VOIDP, LIBC.sizeof("FILE*"))
87103
assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(LIBC::MyStruct))
88-
assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(LIBC::MyStruct.malloc()))
104+
my_struct = LIBC::MyStruct.malloc()
105+
begin
106+
assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(my_struct))
107+
ensure
108+
Fiddle.free my_struct.to_ptr
109+
end
89110
assert_equal(SIZEOF_LONG_LONG, LIBC.sizeof("long long")) if defined?(SIZEOF_LONG_LONG)
90111
end
91112

@@ -131,35 +152,51 @@ def test_value()
131152

132153
def test_struct_array_assignment()
133154
instance = Fiddle::Importer.struct(["unsigned int stages[3]"]).malloc
134-
instance.stages[0] = 1024
135-
instance.stages[1] = 10
136-
instance.stages[2] = 100
137-
assert_equal 1024, instance.stages[0]
138-
assert_equal 10, instance.stages[1]
139-
assert_equal 100, instance.stages[2]
140-
assert_equal [1024, 10, 100].pack(Fiddle::PackInfo::PACK_MAP[-Fiddle::TYPE_INT] * 3),
141-
instance.to_ptr[0, 3 * Fiddle::SIZEOF_INT]
142-
assert_raise(IndexError) { instance.stages[-1] = 5 }
143-
assert_raise(IndexError) { instance.stages[3] = 5 }
155+
begin
156+
instance.stages[0] = 1024
157+
instance.stages[1] = 10
158+
instance.stages[2] = 100
159+
assert_equal 1024, instance.stages[0]
160+
assert_equal 10, instance.stages[1]
161+
assert_equal 100, instance.stages[2]
162+
assert_equal [1024, 10, 100].pack(Fiddle::PackInfo::PACK_MAP[-Fiddle::TYPE_INT] * 3),
163+
instance.to_ptr[0, 3 * Fiddle::SIZEOF_INT]
164+
assert_raise(IndexError) { instance.stages[-1] = 5 }
165+
assert_raise(IndexError) { instance.stages[3] = 5 }
166+
ensure
167+
Fiddle.free instance.to_ptr
168+
end
144169
end
145170

146171
def test_struct()
147172
s = LIBC::MyStruct.malloc()
148-
s.num = [0,1,2,3,4]
149-
s.c = ?a.ord
150-
s.buff = "012345\377"
151-
assert_equal([0,1,2,3,4], s.num)
152-
assert_equal(?a.ord, s.c)
153-
assert_equal([?0.ord,?1.ord,?2.ord,?3.ord,?4.ord,?5.ord,?\377.ord], s.buff)
173+
begin
174+
s.num = [0,1,2,3,4]
175+
s.c = ?a.ord
176+
s.buff = "012345\377"
177+
assert_equal([0,1,2,3,4], s.num)
178+
assert_equal(?a.ord, s.c)
179+
assert_equal([?0.ord,?1.ord,?2.ord,?3.ord,?4.ord,?5.ord,?\377.ord], s.buff)
180+
ensure
181+
Fiddle.free s.to_ptr
182+
end
154183
end
155184

156185
def test_gettimeofday()
157186
if( defined?(LIBC.gettimeofday) )
158187
timeval = LIBC::Timeval.malloc()
159-
timezone = LIBC::Timezone.malloc()
160-
LIBC.gettimeofday(timeval, timezone)
161-
cur = Time.now()
162-
assert(cur.to_i - 2 <= timeval.tv_sec && timeval.tv_sec <= cur.to_i)
188+
begin
189+
timezone = LIBC::Timezone.malloc()
190+
begin
191+
LIBC.gettimeofday(timeval, timezone)
192+
ensure
193+
Fiddle.free timezone.to_ptr
194+
end
195+
cur = Time.now()
196+
assert(cur.to_i - 2 <= timeval.tv_sec && timeval.tv_sec <= cur.to_i)
197+
ensure
198+
Fiddle.free timeval.to_ptr
199+
end
163200
end
164201
end
165202

test/fiddle/test_pointer.rb

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def test_to_ptr_string
8484
end
8585

8686
def test_to_ptr_io
87-
buf = Pointer.malloc(10)
87+
buf = Pointer.malloc(10, Fiddle::RUBY_FREE)
8888
File.open(__FILE__, 'r') do |f|
8989
ptr = Pointer.to_ptr f
9090
fread = Function.new(@libc['fread'],
@@ -145,7 +145,11 @@ def test_to_value
145145

146146
def test_free
147147
ptr = Pointer.malloc(4)
148-
assert_nil ptr.free
148+
begin
149+
assert_nil ptr.free
150+
ensure
151+
Fiddle.free ptr
152+
end
149153
end
150154

151155
def test_free=
@@ -173,15 +177,21 @@ def test_null?
173177

174178
def test_size
175179
ptr = Pointer.malloc(4)
176-
assert_equal 4, ptr.size
177-
Fiddle.free ptr.to_i
180+
begin
181+
assert_equal 4, ptr.size
182+
ensure
183+
Fiddle.free ptr
184+
end
178185
end
179186

180187
def test_size=
181188
ptr = Pointer.malloc(4)
182-
ptr.size = 10
183-
assert_equal 10, ptr.size
184-
Fiddle.free ptr.to_i
189+
begin
190+
ptr.size = 10
191+
assert_equal 10, ptr.size
192+
ensure
193+
Fiddle.free ptr
194+
end
185195
end
186196

187197
def test_aref_aset

0 commit comments

Comments
 (0)
0