Commit 9496b2cb20bfd0551e9510b6ccb41ca950d2c8ee

Authored by Jay Berkenbilt
1 parent 64dc7388

fix memory leak

git-svn-id: svn+q:///qpdf/trunk@976 71b93d88-0707-0410-a8cf-f5a4172ac649
ChangeLog
  1 +2010-06-06 Jay Berkenbilt <ejb@ql.org>
  2 +
  3 + * Fix memory leak for QPDF objects whose underlying PDF objects
  4 + contain circular references. Thanks to Jian Ma
  5 + <stronghorse@tom.com> for calling my attention to the memory leak.
  6 +
1 2010-04-25 Jay Berkenbilt <ejb@ql.org> 7 2010-04-25 Jay Berkenbilt <ejb@ql.org>
2 8
3 * 2.1.5: release 9 * 2.1.5: release
1 -Bug  
2 -===  
3 -  
4 - * There is a memory leak that happens whenever object A refers to  
5 - object B which refers back to object A. We have the following  
6 - pattern:  
7 -  
8 - #include "PointerHolder.hh"  
9 -  
10 - class A  
11 - {  
12 - public:  
13 - PointerHolder<A> a;  
14 - };  
15 -  
16 - int main()  
17 - {  
18 - A a1;  
19 - a1.a = new A();  
20 - a1.a.getPointer()->a = new A();  
21 - a1.a.getPointer()->a.getPointer()->a = a1.a;  
22 - return 0;  
23 - }  
24 -  
25 - In order to fix this, we have to explicitly break circular  
26 - references, but we have to do this at a time that doesn't  
27 - completely destroy performance.  
28 -  
29 - To debug, configure with  
30 -  
31 - ./configure --disable-shared --disable-test-compare-images \  
32 - CFLAGS=-g CXXFLAGS=-g  
33 -  
34 - Current code causes test failures. linearize segfaults on  
35 - hybrid-xref.pdf, and the test suite fails in various places. Maybe  
36 - because we don't do any kind of loop detection?  
37 -  
38 - Use valgrind --leak-check=full to see leak details.  
39 -  
40 - The file memory-leak.pdf is a minimal case that shows the problem.  
41 -  
42 - Files in trace having tracing to show which objects haven't been  
43 - allocated.  
44 -  
45 Next 1 Next
46 ==== 2 ====
47 3
include/qpdf/QPDFObject.hh
@@ -12,11 +12,33 @@ @@ -12,11 +12,33 @@
12 12
13 #include <string> 13 #include <string>
14 14
  15 +class QPDF;
  16 +class QPDFObjectHandle;
  17 +
15 class QPDFObject 18 class QPDFObject
16 { 19 {
17 public: 20 public:
18 virtual ~QPDFObject() {} 21 virtual ~QPDFObject() {}
19 virtual std::string unparse() = 0; 22 virtual std::string unparse() = 0;
  23 +
  24 + // Accessor to give specific access to non-public methods
  25 + class ObjAccessor
  26 + {
  27 + friend class QPDF;
  28 + friend class QPDFObjectHandle;
  29 + private:
  30 + static void releaseResolved(QPDFObject* o)
  31 + {
  32 + if (o)
  33 + {
  34 + o->releaseResolved();
  35 + }
  36 + }
  37 + };
  38 + friend class ObjAccessor;
  39 +
  40 + protected:
  41 + virtual void releaseResolved() {}
20 }; 42 };
21 43
22 #endif // __QPDFOBJECT_HH__ 44 #endif // __QPDFOBJECT_HH__
include/qpdf/QPDFObjectHandle.hh
@@ -22,6 +22,8 @@ @@ -22,6 +22,8 @@
22 22
23 class Pipeline; 23 class Pipeline;
24 class QPDF; 24 class QPDF;
  25 +class QPDF_Dictionary;
  26 +class QPDF_Array;
25 27
26 class QPDFObjectHandle 28 class QPDFObjectHandle
27 { 29 {
@@ -247,6 +249,20 @@ class QPDFObjectHandle @@ -247,6 +249,20 @@ class QPDFObjectHandle
247 }; 249 };
248 friend class ObjAccessor; 250 friend class ObjAccessor;
249 251
  252 + // Provide access to specific classes for recursive
  253 + // reverseResolved().
  254 + class ReleaseResolver
  255 + {
  256 + friend class QPDF_Dictionary;
  257 + friend class QPDF_Array;
  258 + private:
  259 + static void releaseResolved(QPDFObjectHandle& o)
  260 + {
  261 + o.releaseResolved();
  262 + }
  263 + };
  264 + friend class ReleaseResolver;
  265 +
250 private: 266 private:
251 QPDFObjectHandle(QPDF*, int objid, int generation); 267 QPDFObjectHandle(QPDF*, int objid, int generation);
252 QPDFObjectHandle(QPDFObject*); 268 QPDFObjectHandle(QPDFObject*);
@@ -262,6 +278,7 @@ class QPDFObjectHandle @@ -262,6 +278,7 @@ class QPDFObjectHandle
262 void assertPageObject(); 278 void assertPageObject();
263 void dereference(); 279 void dereference();
264 void makeDirectInternal(std::set<int>& visited); 280 void makeDirectInternal(std::set<int>& visited);
  281 + void releaseResolved();
265 282
266 bool initialized; 283 bool initialized;
267 284
libqpdf/QPDF.cc
@@ -277,6 +277,13 @@ QPDF::QPDF() : @@ -277,6 +277,13 @@ QPDF::QPDF() :
277 277
278 QPDF::~QPDF() 278 QPDF::~QPDF()
279 { 279 {
  280 + this->xref_table.clear();
  281 + for (std::map<ObjGen, ObjCache>::iterator iter = this->obj_cache.begin();
  282 + iter != obj_cache.end(); ++iter)
  283 + {
  284 + QPDFObject::ObjAccessor::releaseResolved(
  285 + (*iter).second.object.getPointer());
  286 + }
280 } 287 }
281 288
282 void 289 void
libqpdf/QPDFObjectHandle.cc
@@ -41,6 +41,22 @@ QPDFObjectHandle::QPDFObjectHandle(QPDFObject* data) : @@ -41,6 +41,22 @@ QPDFObjectHandle::QPDFObjectHandle(QPDFObject* data) :
41 { 41 {
42 } 42 }
43 43
  44 +void
  45 +QPDFObjectHandle::releaseResolved()
  46 +{
  47 + if (isIndirect())
  48 + {
  49 + if (this->obj.getPointer())
  50 + {
  51 + this->obj = 0;
  52 + }
  53 + }
  54 + else
  55 + {
  56 + QPDFObject::ObjAccessor::releaseResolved(this->obj.getPointer());
  57 + }
  58 +}
  59 +
44 bool 60 bool
45 QPDFObjectHandle::isInitialized() const 61 QPDFObjectHandle::isInitialized() const
46 { 62 {
libqpdf/QPDF_Array.cc
@@ -10,6 +10,16 @@ QPDF_Array::~QPDF_Array() @@ -10,6 +10,16 @@ QPDF_Array::~QPDF_Array()
10 { 10 {
11 } 11 }
12 12
  13 +void
  14 +QPDF_Array::releaseResolved()
  15 +{
  16 + for (std::vector<QPDFObjectHandle>::iterator iter = this->items.begin();
  17 + iter != this->items.end(); ++iter)
  18 + {
  19 + QPDFObjectHandle::ReleaseResolver::releaseResolved(*iter);
  20 + }
  21 +}
  22 +
13 std::string 23 std::string
14 QPDF_Array::unparse() 24 QPDF_Array::unparse()
15 { 25 {
libqpdf/QPDF_Dictionary.cc
@@ -13,6 +13,17 @@ QPDF_Dictionary::~QPDF_Dictionary() @@ -13,6 +13,17 @@ QPDF_Dictionary::~QPDF_Dictionary()
13 { 13 {
14 } 14 }
15 15
  16 +void
  17 +QPDF_Dictionary::releaseResolved()
  18 +{
  19 + for (std::map<std::string, QPDFObjectHandle>::iterator iter =
  20 + this->items.begin();
  21 + iter != this->items.end(); ++iter)
  22 + {
  23 + QPDFObjectHandle::ReleaseResolver::releaseResolved((*iter).second);
  24 + }
  25 +}
  26 +
16 std::string 27 std::string
17 QPDF_Dictionary::unparse() 28 QPDF_Dictionary::unparse()
18 { 29 {
libqpdf/qpdf/QPDF_Array.hh
@@ -16,6 +16,9 @@ class QPDF_Array: public QPDFObject @@ -16,6 +16,9 @@ class QPDF_Array: public QPDFObject
16 QPDFObjectHandle getItem(int n) const; 16 QPDFObjectHandle getItem(int n) const;
17 void setItem(int, QPDFObjectHandle const&); 17 void setItem(int, QPDFObjectHandle const&);
18 18
  19 + protected:
  20 + virtual void releaseResolved();
  21 +
19 private: 22 private:
20 std::vector<QPDFObjectHandle> items; 23 std::vector<QPDFObjectHandle> items;
21 }; 24 };
libqpdf/qpdf/QPDF_Dictionary.hh
@@ -27,6 +27,9 @@ class QPDF_Dictionary: public QPDFObject @@ -27,6 +27,9 @@ class QPDF_Dictionary: public QPDFObject
27 // Remove key, doing nothing if key does not exist 27 // Remove key, doing nothing if key does not exist
28 void removeKey(std::string const& key); 28 void removeKey(std::string const& key);
29 29
  30 + protected:
  31 + virtual void releaseResolved();
  32 +
30 private: 33 private:
31 std::map<std::string, QPDFObjectHandle> items; 34 std::map<std::string, QPDFObjectHandle> items;
32 }; 35 };