MythTV  master
referencecounter.cpp
Go to the documentation of this file.
1 // -*- Mode: c++ -*-
2 
3 #include <QString>
4 
5 #include "referencecounter.h"
6 #include "mythlogging.h"
7 
8 #ifdef EXTRA_DEBUG
9 // Uncomment this to see missing DecrRefs()
10 #define LEAK_DEBUG
11 // Uncoment this to see extra DecrRefs(), no memory will ever be freed..
12 //#define NO_DELETE_DEBUG
13 #endif
14 
15 #ifdef LEAK_DEBUG
16 #include <QReadWriteLock>
17 #include <QMap>
18 #include "../libmythui/mythimage.h"
19 static QReadWriteLock leakLock;
20 struct LeakInfo
21 {
22  LeakInfo() : refCount(0) {}
23  LeakInfo(const QString &n) : name(n), refCount(1) {}
24  QString name;
25  QAtomicInt refCount;
26 };
27 static QMap<ReferenceCounter*,LeakInfo> leakMap;
29 {
30  QReadLocker locker(&leakLock);
31  QList<uint64_t> logType;
32  QStringList logLines;
33 
34  uint cnt = 0;
35  QMap<ReferenceCounter*,LeakInfo>::iterator it = leakMap.begin();
36  for (; it != leakMap.end(); ++it)
37  {
38  if ((*it).refCount.fetchAndAddOrdered(0) == 0)
39  continue;
40  if ((*it).name.startsWith("CommandLineArg"))
41  continue;
42  if (!it.key()->m_logDebug)
43  continue;
44  cnt += 1;
45  }
46  logType += (cnt) ? VB_GENERAL : VB_REFCOUNT;
47  logLines += QString("Leaked %1 reference counted objects").arg(cnt);
48 
49  for (it = leakMap.begin(); it != leakMap.end(); ++it)
50  {
51  if ((*it).refCount.fetchAndAddOrdered(0) == 0)
52  continue;
53  if ((*it).name.startsWith("CommandLineArg"))
54  continue;
55  if (!it.key()->m_logDebug)
56  continue;
57 
58  logType += VB_REFCOUNT;
59  logLines +=
60  QString(" Leaked %1(0x%2) reference count %3")
61  .arg((*it).name)
62  .arg(reinterpret_cast<intptr_t>(it.key()),0,16)
63  .arg((*it).refCount);
64  }
65 
66  locker.unlock();
67 
68  for (uint i = 0; i < (uint)logType.size() && i < (uint)logLines.size(); i++)
69  LOG(logType[i], LOG_INFO, logLines[i]);
70 }
71 #else
73 #endif
74 
75 ReferenceCounter::ReferenceCounter([[maybe_unused]] const QString &debugName,
76  bool logDebug) :
77 #ifdef EXTRA_DEBUG
78  m_debugName(debugName),
79 #endif
80  m_logDebug(logDebug)
81 {
82 #ifdef LEAK_DEBUG
83  QWriteLocker locker(&leakLock);
84  leakMap[this] = LeakInfo(debugName);
85 #endif
86 }
87 
89 {
90  if (m_referenceCount.fetchAndAddRelaxed(0) > 1)
91  {
92  LOG(VB_GENERAL, LOG_ERR,
93  "Object deleted with non-zero or one reference count!");
94  }
95 #ifdef LEAK_DEBUG
96  QWriteLocker locker(&leakLock);
97  leakMap.erase(leakMap.find(this));
98 #endif
99 }
100 
102 {
103  int val = m_referenceCount.fetchAndAddRelease(1) + 1;
104 
105  if (m_logDebug)
106  {
107 #ifdef EXTRA_DEBUG
108  LOG(VB_REFCOUNT, LOG_INFO, QString("%1(0x%2)::IncrRef() -> %3")
109  .arg(m_debugName).arg(reinterpret_cast<intptr_t>(this),0,16)
110  .arg(val));
111 #else
112  LOG(VB_REFCOUNT, LOG_INFO, QString("(0x%2)::IncrRef() -> %3")
113  .arg(reinterpret_cast<intptr_t>(this),0,16).arg(val));
114 #endif
115  }
116 
117 #ifdef LEAK_DEBUG
118  QReadLocker locker(&leakLock);
119  leakMap[this].refCount.fetchAndAddOrdered(1);
120 #endif
121 
122  return val;
123 }
124 
126 {
127  int val = m_referenceCount.fetchAndAddRelaxed(-1) - 1;
128 
129 #ifdef LEAK_DEBUG
130  {
131  QReadLocker locker(&leakLock);
132  leakMap[this].refCount.fetchAndAddOrdered(-1);
133  }
134 #endif
135 
136  if (m_logDebug)
137  {
138 #ifdef EXTRA_DEBUG
139  LOG(VB_REFCOUNT, LOG_INFO, QString("%1(0x%2)::DecrRef() -> %3")
140  .arg(m_debugName).arg(reinterpret_cast<intptr_t>(this),0,16)
141  .arg(val));
142 #else
143  LOG(VB_REFCOUNT, LOG_INFO, QString("(0x%2)::DecrRef() -> %3")
144  .arg(reinterpret_cast<intptr_t>(this),0,16).arg(val));
145 #endif
146  }
147 
148 #ifdef NO_DELETE_DEBUG
149  if (val < 0)
150  {
151  LOG(VB_REFCOUNT, LOG_ERR, QString("(0x%2)::DecrRef() -> %3 !!!")
152  .arg(reinterpret_cast<intptr_t>(this),0,16).arg(val));
153  }
154 #else
155  if (0 == val)
156  {
157  delete this;
158  return val;
159  }
160 #endif
161 
162  return val;
163 }
ReferenceCounter::DecrRef
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
Definition: referencecounter.cpp:125
ReferenceCounter::ReferenceCounter
ReferenceCounter(const QString &debugName, bool logDebug=true)
Creates reference counter with an initial value of 1.
Definition: referencecounter.cpp:75
ReferenceCounter::PrintDebug
static void PrintDebug(void)
Print out any leaks if that level of debugging is enabled.
Definition: referencecounter.cpp:72
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
ReferenceCounter::~ReferenceCounter
virtual ~ReferenceCounter(void)
Called on destruction, will warn if object deleted with references in place.
Definition: referencecounter.cpp:88
mythlogging.h
ReferenceCounter::m_logDebug
bool m_logDebug
This is used to suppress creating LoggingItem classes for LoggingItem reference count changes.
Definition: referencecounter.h:56
uint
unsigned int uint
Definition: compat.h:81
referencecounter.h
ReferenceCounter::m_referenceCount
QAtomicInt m_referenceCount
Definition: referencecounter.h:57
ReferenceCounter::IncrRef
virtual int IncrRef(void)
Increments reference count.
Definition: referencecounter.cpp:101