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 7 2010-04-25 Jay Berkenbilt <ejb@ql.org>
2 8  
3 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 1 Next
46 2 ====
47 3  
... ...
include/qpdf/QPDFObject.hh
... ... @@ -12,11 +12,33 @@
12 12  
13 13 #include <string>
14 14  
  15 +class QPDF;
  16 +class QPDFObjectHandle;
  17 +
15 18 class QPDFObject
16 19 {
17 20 public:
18 21 virtual ~QPDFObject() {}
19 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 44 #endif // __QPDFOBJECT_HH__
... ...
include/qpdf/QPDFObjectHandle.hh
... ... @@ -22,6 +22,8 @@
22 22  
23 23 class Pipeline;
24 24 class QPDF;
  25 +class QPDF_Dictionary;
  26 +class QPDF_Array;
25 27  
26 28 class QPDFObjectHandle
27 29 {
... ... @@ -247,6 +249,20 @@ class QPDFObjectHandle
247 249 };
248 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 266 private:
251 267 QPDFObjectHandle(QPDF*, int objid, int generation);
252 268 QPDFObjectHandle(QPDFObject*);
... ... @@ -262,6 +278,7 @@ class QPDFObjectHandle
262 278 void assertPageObject();
263 279 void dereference();
264 280 void makeDirectInternal(std::set<int>& visited);
  281 + void releaseResolved();
265 282  
266 283 bool initialized;
267 284  
... ...
libqpdf/QPDF.cc
... ... @@ -277,6 +277,13 @@ QPDF::QPDF() :
277 277  
278 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 289 void
... ...
libqpdf/QPDFObjectHandle.cc
... ... @@ -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 60 bool
45 61 QPDFObjectHandle::isInitialized() const
46 62 {
... ...
libqpdf/QPDF_Array.cc
... ... @@ -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 23 std::string
14 24 QPDF_Array::unparse()
15 25 {
... ...
libqpdf/QPDF_Dictionary.cc
... ... @@ -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 27 std::string
17 28 QPDF_Dictionary::unparse()
18 29 {
... ...
libqpdf/qpdf/QPDF_Array.hh
... ... @@ -16,6 +16,9 @@ class QPDF_Array: public QPDFObject
16 16 QPDFObjectHandle getItem(int n) const;
17 17 void setItem(int, QPDFObjectHandle const&);
18 18  
  19 + protected:
  20 + virtual void releaseResolved();
  21 +
19 22 private:
20 23 std::vector<QPDFObjectHandle> items;
21 24 };
... ...
libqpdf/qpdf/QPDF_Dictionary.hh
... ... @@ -27,6 +27,9 @@ class QPDF_Dictionary: public QPDFObject
27 27 // Remove key, doing nothing if key does not exist
28 28 void removeKey(std::string const& key);
29 29  
  30 + protected:
  31 + virtual void releaseResolved();
  32 +
30 33 private:
31 34 std::map<std::string, QPDFObjectHandle> items;
32 35 };
... ...