[ruby/fiddle] Improve documentation on how to correctly free memory and free memory in tests (#33)
https://github.com/ruby/fiddle/commit/e59cfd708a
This commit is contained in:
parent
24b615e82e
commit
3015a7aae7
@ -73,7 +73,12 @@ module Fiddle
|
|||||||
#
|
#
|
||||||
# MyStruct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members)
|
# MyStruct = Fiddle::CStructBuilder.create(Fiddle::CUnion, types, members)
|
||||||
#
|
#
|
||||||
# obj = MyStruct.allocate
|
# obj = MyStruct.malloc
|
||||||
|
# begin
|
||||||
|
# ...
|
||||||
|
# ensure
|
||||||
|
# Fiddle.free obj.to_ptr
|
||||||
|
# end
|
||||||
#
|
#
|
||||||
def create(klass, types, members)
|
def create(klass, types, members)
|
||||||
new_class = Class.new(klass){
|
new_class = Class.new(klass){
|
||||||
@ -112,7 +117,7 @@ module Fiddle
|
|||||||
|
|
||||||
# Allocates a C struct with the +types+ provided.
|
# Allocates a C struct with the +types+ provided.
|
||||||
#
|
#
|
||||||
# When the instance is garbage collected, the C function +func+ is called.
|
# See Fiddle::Pointer.malloc for memory management issues.
|
||||||
def CStructEntity.malloc(types, func = nil)
|
def CStructEntity.malloc(types, func = nil)
|
||||||
addr = Fiddle.malloc(CStructEntity.size(types))
|
addr = Fiddle.malloc(CStructEntity.size(types))
|
||||||
CStructEntity.new(addr, types, func)
|
CStructEntity.new(addr, types, func)
|
||||||
@ -267,7 +272,7 @@ module Fiddle
|
|||||||
|
|
||||||
# Allocates a C union the +types+ provided.
|
# Allocates a C union the +types+ provided.
|
||||||
#
|
#
|
||||||
# When the instance is garbage collected, the C function +func+ is called.
|
# See Fiddle::Pointer.malloc for memory management issues.
|
||||||
def CUnionEntity.malloc(types, func=nil)
|
def CUnionEntity.malloc(types, func=nil)
|
||||||
addr = Fiddle.malloc(CUnionEntity.size(types))
|
addr = Fiddle.malloc(CUnionEntity.size(types))
|
||||||
CUnionEntity.new(addr, types, func)
|
CUnionEntity.new(addr, types, func)
|
||||||
|
@ -193,14 +193,34 @@ rb_fiddle_ptr_initialize(int argc, VALUE argv[], VALUE self)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* call-seq:
|
* call-seq:
|
||||||
*
|
|
||||||
* Fiddle::Pointer.malloc(size, freefunc = nil) => fiddle pointer instance
|
* Fiddle::Pointer.malloc(size, freefunc = nil) => fiddle pointer instance
|
||||||
*
|
*
|
||||||
|
* == Examples
|
||||||
|
*
|
||||||
|
* # Relying on the garbage collector - may lead to unlimited memory allocated before freeing any, but safe
|
||||||
|
* pointer = Fiddle::Pointer.malloc(size, Fiddle::RUBY_FREE)
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* # Manual freeing
|
||||||
|
* pointer = Fiddle::Pointer.malloc(size)
|
||||||
|
* begin
|
||||||
|
* ...
|
||||||
|
* ensure
|
||||||
|
* Fiddle.free pointer
|
||||||
|
* end
|
||||||
|
*
|
||||||
|
* # No free function and no call to free - the native memory will leak if the pointer is garbage collected
|
||||||
|
* pointer = Fiddle::Pointer.malloc(size)
|
||||||
|
* ...
|
||||||
|
*
|
||||||
* Allocate +size+ bytes of memory and associate it with an optional
|
* Allocate +size+ bytes of memory and associate it with an optional
|
||||||
* +freefunc+ that will be called when the pointer is garbage collected.
|
* +freefunc+ that will be called when the pointer is garbage collected.
|
||||||
*
|
|
||||||
* +freefunc+ must be an address pointing to a function or an instance of
|
* +freefunc+ must be an address pointing to a function or an instance of
|
||||||
* Fiddle::Function
|
* +Fiddle::Function+. Using +freefunc+ may lead to unlimited memory being
|
||||||
|
* allocated before any is freed as the native memory the pointer references
|
||||||
|
* does not contribute to triggering the Ruby garbage collector. Consider
|
||||||
|
* manually freeing the memory as illustrated above. You cannot combine
|
||||||
|
* the techniques as this may lead to a double-free.
|
||||||
*/
|
*/
|
||||||
static VALUE
|
static VALUE
|
||||||
rb_fiddle_ptr_s_malloc(int argc, VALUE argv[], VALUE klass)
|
rb_fiddle_ptr_s_malloc(int argc, VALUE argv[], VALUE klass)
|
||||||
|
@ -43,7 +43,7 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_set_ctypes
|
def test_set_ctypes
|
||||||
union = CStructEntity.malloc [TYPE_INT, TYPE_LONG]
|
union = CStructEntity.malloc [TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE
|
||||||
union.assign_names %w[int long]
|
union.assign_names %w[int long]
|
||||||
|
|
||||||
# this test is roundabout because the stored ctypes are not accessible
|
# this test is roundabout because the stored ctypes are not accessible
|
||||||
@ -55,20 +55,20 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_aref_pointer_array
|
def test_aref_pointer_array
|
||||||
team = CStructEntity.malloc([[TYPE_VOIDP, 2]])
|
team = CStructEntity.malloc([[TYPE_VOIDP, 2]], Fiddle::RUBY_FREE)
|
||||||
team.assign_names(["names"])
|
team.assign_names(["names"])
|
||||||
alice = Fiddle::Pointer.malloc(6)
|
alice = Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE)
|
||||||
alice[0, 6] = "Alice\0"
|
alice[0, 6] = "Alice\0"
|
||||||
bob = Fiddle::Pointer.malloc(4)
|
bob = Fiddle::Pointer.malloc(4, Fiddle::RUBY_FREE)
|
||||||
bob[0, 4] = "Bob\0"
|
bob[0, 4] = "Bob\0"
|
||||||
team["names"] = [alice, bob]
|
team["names"] = [alice, bob]
|
||||||
assert_equal(["Alice", "Bob"], team["names"].map(&:to_s))
|
assert_equal(["Alice", "Bob"], team["names"].map(&:to_s))
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_aref_pointer
|
def test_aref_pointer
|
||||||
user = CStructEntity.malloc([TYPE_VOIDP])
|
user = CStructEntity.malloc([TYPE_VOIDP], Fiddle::RUBY_FREE)
|
||||||
user.assign_names(["name"])
|
user.assign_names(["name"])
|
||||||
alice = Fiddle::Pointer.malloc(6)
|
alice = Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE)
|
||||||
alice[0, 6] = "Alice\0"
|
alice[0, 6] = "Alice\0"
|
||||||
user["name"] = alice
|
user["name"] = alice
|
||||||
assert_equal("Alice", user["name"].to_s)
|
assert_equal("Alice", user["name"].to_s)
|
||||||
|
@ -21,7 +21,7 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_set_ctypes
|
def test_set_ctypes
|
||||||
union = CUnionEntity.malloc [TYPE_INT, TYPE_LONG]
|
union = CUnionEntity.malloc [TYPE_INT, TYPE_LONG], Fiddle::RUBY_FREE
|
||||||
union.assign_names %w[int long]
|
union.assign_names %w[int long]
|
||||||
|
|
||||||
# this test is roundabout because the stored ctypes are not accessible
|
# this test is roundabout because the stored ctypes are not accessible
|
||||||
|
@ -57,16 +57,21 @@ module Fiddle
|
|||||||
def test_struct_memory_access()
|
def test_struct_memory_access()
|
||||||
# check memory operations performed directly on struct
|
# check memory operations performed directly on struct
|
||||||
my_struct = Fiddle::Importer.struct(['int id']).malloc
|
my_struct = Fiddle::Importer.struct(['int id']).malloc
|
||||||
|
begin
|
||||||
my_struct[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
|
my_struct[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
|
||||||
assert_equal 0x01010101, my_struct.id
|
assert_equal 0x01010101, my_struct.id
|
||||||
|
|
||||||
my_struct.id = 0
|
my_struct.id = 0
|
||||||
assert_equal "\x00".b * Fiddle::SIZEOF_INT, my_struct[0, Fiddle::SIZEOF_INT]
|
assert_equal "\x00".b * Fiddle::SIZEOF_INT, my_struct[0, Fiddle::SIZEOF_INT]
|
||||||
|
ensure
|
||||||
|
Fiddle.free my_struct.to_ptr
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_struct_ptr_array_subscript_multiarg()
|
def test_struct_ptr_array_subscript_multiarg()
|
||||||
# check memory operations performed on struct#to_ptr
|
# check memory operations performed on struct#to_ptr
|
||||||
struct = Fiddle::Importer.struct([ 'int x' ]).malloc
|
struct = Fiddle::Importer.struct([ 'int x' ]).malloc
|
||||||
|
begin
|
||||||
ptr = struct.to_ptr
|
ptr = struct.to_ptr
|
||||||
|
|
||||||
struct.x = 0x02020202
|
struct.x = 0x02020202
|
||||||
@ -74,18 +79,34 @@ module Fiddle
|
|||||||
|
|
||||||
ptr[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
|
ptr[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
|
||||||
assert_equal 0x01010101, struct.x
|
assert_equal 0x01010101, struct.x
|
||||||
|
ensure
|
||||||
|
Fiddle.free struct.to_ptr
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_malloc()
|
def test_malloc()
|
||||||
s1 = LIBC::Timeval.malloc()
|
s1 = LIBC::Timeval.malloc()
|
||||||
|
begin
|
||||||
s2 = LIBC::Timeval.malloc()
|
s2 = LIBC::Timeval.malloc()
|
||||||
|
begin
|
||||||
refute_equal(s1.to_ptr.to_i, s2.to_ptr.to_i)
|
refute_equal(s1.to_ptr.to_i, s2.to_ptr.to_i)
|
||||||
|
ensure
|
||||||
|
Fiddle.free s2.to_ptr
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
Fiddle.free s1.to_ptr
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_sizeof()
|
def test_sizeof()
|
||||||
assert_equal(SIZEOF_VOIDP, LIBC.sizeof("FILE*"))
|
assert_equal(SIZEOF_VOIDP, LIBC.sizeof("FILE*"))
|
||||||
assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(LIBC::MyStruct))
|
assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(LIBC::MyStruct))
|
||||||
assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(LIBC::MyStruct.malloc()))
|
my_struct = LIBC::MyStruct.malloc()
|
||||||
|
begin
|
||||||
|
assert_equal(LIBC::MyStruct.size(), LIBC.sizeof(my_struct))
|
||||||
|
ensure
|
||||||
|
Fiddle.free my_struct.to_ptr
|
||||||
|
end
|
||||||
assert_equal(SIZEOF_LONG_LONG, LIBC.sizeof("long long")) if defined?(SIZEOF_LONG_LONG)
|
assert_equal(SIZEOF_LONG_LONG, LIBC.sizeof("long long")) if defined?(SIZEOF_LONG_LONG)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -131,6 +152,7 @@ module Fiddle
|
|||||||
|
|
||||||
def test_struct_array_assignment()
|
def test_struct_array_assignment()
|
||||||
instance = Fiddle::Importer.struct(["unsigned int stages[3]"]).malloc
|
instance = Fiddle::Importer.struct(["unsigned int stages[3]"]).malloc
|
||||||
|
begin
|
||||||
instance.stages[0] = 1024
|
instance.stages[0] = 1024
|
||||||
instance.stages[1] = 10
|
instance.stages[1] = 10
|
||||||
instance.stages[2] = 100
|
instance.stages[2] = 100
|
||||||
@ -141,25 +163,40 @@ module Fiddle
|
|||||||
instance.to_ptr[0, 3 * Fiddle::SIZEOF_INT]
|
instance.to_ptr[0, 3 * Fiddle::SIZEOF_INT]
|
||||||
assert_raise(IndexError) { instance.stages[-1] = 5 }
|
assert_raise(IndexError) { instance.stages[-1] = 5 }
|
||||||
assert_raise(IndexError) { instance.stages[3] = 5 }
|
assert_raise(IndexError) { instance.stages[3] = 5 }
|
||||||
|
ensure
|
||||||
|
Fiddle.free instance.to_ptr
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_struct()
|
def test_struct()
|
||||||
s = LIBC::MyStruct.malloc()
|
s = LIBC::MyStruct.malloc()
|
||||||
|
begin
|
||||||
s.num = [0,1,2,3,4]
|
s.num = [0,1,2,3,4]
|
||||||
s.c = ?a.ord
|
s.c = ?a.ord
|
||||||
s.buff = "012345\377"
|
s.buff = "012345\377"
|
||||||
assert_equal([0,1,2,3,4], s.num)
|
assert_equal([0,1,2,3,4], s.num)
|
||||||
assert_equal(?a.ord, s.c)
|
assert_equal(?a.ord, s.c)
|
||||||
assert_equal([?0.ord,?1.ord,?2.ord,?3.ord,?4.ord,?5.ord,?\377.ord], s.buff)
|
assert_equal([?0.ord,?1.ord,?2.ord,?3.ord,?4.ord,?5.ord,?\377.ord], s.buff)
|
||||||
|
ensure
|
||||||
|
Fiddle.free s.to_ptr
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_gettimeofday()
|
def test_gettimeofday()
|
||||||
if( defined?(LIBC.gettimeofday) )
|
if( defined?(LIBC.gettimeofday) )
|
||||||
timeval = LIBC::Timeval.malloc()
|
timeval = LIBC::Timeval.malloc()
|
||||||
|
begin
|
||||||
timezone = LIBC::Timezone.malloc()
|
timezone = LIBC::Timezone.malloc()
|
||||||
|
begin
|
||||||
LIBC.gettimeofday(timeval, timezone)
|
LIBC.gettimeofday(timeval, timezone)
|
||||||
|
ensure
|
||||||
|
Fiddle.free timezone.to_ptr
|
||||||
|
end
|
||||||
cur = Time.now()
|
cur = Time.now()
|
||||||
assert(cur.to_i - 2 <= timeval.tv_sec && timeval.tv_sec <= cur.to_i)
|
assert(cur.to_i - 2 <= timeval.tv_sec && timeval.tv_sec <= cur.to_i)
|
||||||
|
ensure
|
||||||
|
Fiddle.free timeval.to_ptr
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ module Fiddle
|
|||||||
end
|
end
|
||||||
|
|
||||||
def test_to_ptr_io
|
def test_to_ptr_io
|
||||||
buf = Pointer.malloc(10)
|
buf = Pointer.malloc(10, Fiddle::RUBY_FREE)
|
||||||
File.open(__FILE__, 'r') do |f|
|
File.open(__FILE__, 'r') do |f|
|
||||||
ptr = Pointer.to_ptr f
|
ptr = Pointer.to_ptr f
|
||||||
fread = Function.new(@libc['fread'],
|
fread = Function.new(@libc['fread'],
|
||||||
@ -145,7 +145,11 @@ module Fiddle
|
|||||||
|
|
||||||
def test_free
|
def test_free
|
||||||
ptr = Pointer.malloc(4)
|
ptr = Pointer.malloc(4)
|
||||||
|
begin
|
||||||
assert_nil ptr.free
|
assert_nil ptr.free
|
||||||
|
ensure
|
||||||
|
Fiddle.free ptr
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_free=
|
def test_free=
|
||||||
@ -173,15 +177,21 @@ module Fiddle
|
|||||||
|
|
||||||
def test_size
|
def test_size
|
||||||
ptr = Pointer.malloc(4)
|
ptr = Pointer.malloc(4)
|
||||||
|
begin
|
||||||
assert_equal 4, ptr.size
|
assert_equal 4, ptr.size
|
||||||
Fiddle.free ptr.to_i
|
ensure
|
||||||
|
Fiddle.free ptr
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_size=
|
def test_size=
|
||||||
ptr = Pointer.malloc(4)
|
ptr = Pointer.malloc(4)
|
||||||
|
begin
|
||||||
ptr.size = 10
|
ptr.size = 10
|
||||||
assert_equal 10, ptr.size
|
assert_equal 10, ptr.size
|
||||||
Fiddle.free ptr.to_i
|
ensure
|
||||||
|
Fiddle.free ptr
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_aref_aset
|
def test_aref_aset
|
||||||
|
Loading…
x
Reference in New Issue
Block a user