[前][次][番号順一覧][スレッド一覧]

ruby-changes:61345

From: Chris <ko1@a...>
Date: Sat, 23 May 2020 14:35:17 +0900 (JST)
Subject: [ruby-changes:61345] 3015a7aae7 (master): [ruby/fiddle] Improve documentation on how to correctly free memory and free memory in tests (#33)

https://git.ruby-lang.org/ruby.git/commit/?id=3015a7aae7

From 3015a7aae7ddc9b63149df34b1f12366e07a9563 Mon Sep 17 00:00:00 2001
From: Chris Seaton <chris@c...>
Date: Tue, 19 May 2020 00:12:47 +0100
Subject: [ruby/fiddle] Improve documentation on how to correctly free memory
 and free memory in tests (#33)

https://github.com/ruby/fiddle/commit/e59cfd708a

diff --git a/ext/fiddle/lib/fiddle/struct.rb b/ext/fiddle/lib/fiddle/struct.rb
index 8a48a1c..259903d 100644
--- a/ext/fiddle/lib/fiddle/struct.rb
+++ b/ext/fiddle/lib/fiddle/struct.rb
@@ -73,7 +73,12 @@ module Fiddle https://github.com/ruby/ruby/blob/trunk/ext/fiddle/lib/fiddle/struct.rb#L73
     #
     #   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)
       new_class = Class.new(klass){
@@ -112,7 +117,7 @@ module Fiddle https://github.com/ruby/ruby/blob/trunk/ext/fiddle/lib/fiddle/struct.rb#L117
 
     # 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)
       addr = Fiddle.malloc(CStructEntity.size(types))
       CStructEntity.new(addr, types, func)
@@ -267,7 +272,7 @@ module Fiddle https://github.com/ruby/ruby/blob/trunk/ext/fiddle/lib/fiddle/struct.rb#L272
 
     # 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)
       addr = Fiddle.malloc(CUnionEntity.size(types))
       CUnionEntity.new(addr, types, func)
diff --git a/ext/fiddle/pointer.c b/ext/fiddle/pointer.c
index 117cc9b..7c60da4 100644
--- a/ext/fiddle/pointer.c
+++ b/ext/fiddle/pointer.c
@@ -193,14 +193,34 @@ rb_fiddle_ptr_initialize(int argc, VALUE argv[], VALUE self) https://github.com/ruby/ruby/blob/trunk/ext/fiddle/pointer.c#L193
 
 /*
  * call-seq:
- *
  *    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
  * +freefunc+ that will be called when the pointer is garbage collected.
- *
  * +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
 rb_fiddle_ptr_s_malloc(int argc, VALUE argv[], VALUE klass)
diff --git a/test/fiddle/test_c_struct_entry.rb b/test/fiddle/test_c_struct_entry.rb
index 8ece438..33bfee6 100644
--- a/test/fiddle/test_c_struct_entry.rb
+++ b/test/fiddle/test_c_struct_entry.rb
@@ -43,7 +43,7 @@ module Fiddle https://github.com/ruby/ruby/blob/trunk/test/fiddle/test_c_struct_entry.rb#L43
     end
 
     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]
 
       # this test is roundabout because the stored ctypes are not accessible
@@ -55,20 +55,20 @@ module Fiddle https://github.com/ruby/ruby/blob/trunk/test/fiddle/test_c_struct_entry.rb#L55
     end
 
     def test_aref_pointer_array
-      team = CStructEntity.malloc([[TYPE_VOIDP, 2]])
+      team = CStructEntity.malloc([[TYPE_VOIDP, 2]], Fiddle::RUBY_FREE)
       team.assign_names(["names"])
-      alice = Fiddle::Pointer.malloc(6)
+      alice = Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE)
       alice[0, 6] = "Alice\0"
-      bob = Fiddle::Pointer.malloc(4)
+      bob = Fiddle::Pointer.malloc(4, Fiddle::RUBY_FREE)
       bob[0, 4] = "Bob\0"
       team["names"] = [alice, bob]
       assert_equal(["Alice", "Bob"], team["names"].map(&:to_s))
     end
 
     def test_aref_pointer
-      user = CStructEntity.malloc([TYPE_VOIDP])
+      user = CStructEntity.malloc([TYPE_VOIDP], Fiddle::RUBY_FREE)
       user.assign_names(["name"])
-      alice = Fiddle::Pointer.malloc(6)
+      alice = Fiddle::Pointer.malloc(6, Fiddle::RUBY_FREE)
       alice[0, 6] = "Alice\0"
       user["name"] = alice
       assert_equal("Alice", user["name"].to_s)
diff --git a/test/fiddle/test_c_union_entity.rb b/test/fiddle/test_c_union_entity.rb
index 5727a20..9310084 100644
--- a/test/fiddle/test_c_union_entity.rb
+++ b/test/fiddle/test_c_union_entity.rb
@@ -21,7 +21,7 @@ module Fiddle https://github.com/ruby/ruby/blob/trunk/test/fiddle/test_c_union_entity.rb#L21
     end
 
     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]
 
       # this test is roundabout because the stored ctypes are not accessible
diff --git a/test/fiddle/test_import.rb b/test/fiddle/test_import.rb
index 867f696..fce8b86 100644
--- a/test/fiddle/test_import.rb
+++ b/test/fiddle/test_import.rb
@@ -57,35 +57,56 @@ module Fiddle https://github.com/ruby/ruby/blob/trunk/test/fiddle/test_import.rb#L57
     def test_struct_memory_access()
       # check memory operations performed directly on struct
       my_struct = Fiddle::Importer.struct(['int id']).malloc
-      my_struct[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
-      assert_equal 0x01010101, my_struct.id
-
-      my_struct.id = 0
-      assert_equal "\x00".b * Fiddle::SIZEOF_INT, my_struct[0, Fiddle::SIZEOF_INT]
+      begin
+        my_struct[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
+        assert_equal 0x01010101, my_struct.id
+
+        my_struct.id = 0
+        assert_equal "\x00".b * Fiddle::SIZEOF_INT, my_struct[0, Fiddle::SIZEOF_INT]
+      ensure
+        Fiddle.free my_struct.to_ptr
+      end
     end
 
     def test_struct_ptr_array_subscript_multiarg()
       # check memory operations performed on struct#to_ptr
       struct = Fiddle::Importer.struct([ 'int x' ]).malloc
-      ptr = struct.to_ptr
+      begin
+        ptr = struct.to_ptr
 
-      struct.x = 0x02020202
-      assert_equal("\x02".b * Fiddle::SIZEOF_INT, ptr[0, Fiddle::SIZEOF_INT])
+        struct.x = 0x02020202
+        assert_equal("\x02".b * Fiddle::SIZEOF_INT, ptr[0, Fiddle::SIZEOF_INT])
 
-      ptr[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
-      assert_equal 0x01010101, struct.x
+        ptr[0, Fiddle::SIZEOF_INT] = "\x01".b * Fiddle::SIZEOF_INT
+        assert_equal 0x01010101, struct.x
+      ensure
+        Fiddle.free struct.to_ptr
+      end
     end
 
     def test_malloc()
       s1 = LIBC::Timeval.malloc()
-      s2 = LIBC::Timeval.malloc()
-      refute_equal(s1.to_ptr.to_i, s2.to_ptr.to_i)
+      begin
+        s2 = LIBC::Timeval.malloc()
+        begin
+          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
 
     def test_sizeof()
       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.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)
     end
 
@@ -131,35 +152,51 @@ module Fiddle https://github.com/ruby/ruby/blob/trunk/test/fiddle/test_import.rb#L152
 
     def test_struct_array_assignment()
       instance = Fiddle::Importer.struct(["unsigned int stages[3]"]).malloc
-      instance.stages[0] = 1024
-      instance.stages[1] = 10
-      instance.stages[2] = 100
-      assert_equal 1024, instance.stages[0]
-      assert_equal 10, instance.stages[1]
-      assert_equal 100, instance.stages[2]
-      assert_equal [1024, 10, 100].pack(Fiddle::PackInfo::PACK_MAP[-Fiddle::TYPE_INT] * 3),
-                   instance.to_ptr[0, 3 * Fiddle::SIZEOF_INT]
-      assert_raise(IndexError) { instance.stages[-1] = 5 }
-      assert_raise(IndexError) { instance.stages[3] = 5 }
+      begin
+        instance.stages[0] = 1024
+        instance.stages[1] = 10
+        instance.stages[2] = 100
+        assert_equal 1024, instance.stages[0]
+        assert_equal 10, instance.stages[1]
+        assert_equal 100, instance.stages[2]
+        assert_equal [1024, 10, 100].pack(Fiddle::PackInfo::PACK_MAP[-Fiddle::TYPE_INT] * 3),
+                    instance.to_ptr[0, 3 * Fiddle::SIZEOF_INT]
+        assert_raise(IndexError) { instance.stages[-1] = 5 }
+        assert_raise(IndexError) { instance.stages[3] = 5 }
+      ensure
+        Fiddle.free instance.to_ptr
+      end
     end
 
     def test_struct()
       s = LIBC::MyStruct.malloc()
-      s.num = [0,1,2,3,4]
-      s.c = ?a.ord
-      s.buff = "012345\377"
-      assert_equal([0,1,2,3,4], s.num)
-      assert_equal(?a.ord, s.c)
-      assert_equal([?0.ord,?1.ord,?2.ord,?3.ord,? (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]