Qt 4.8
qsharedpointer.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qsharedpointer.h"
43 
44 // to be sure we aren't causing a namespace clash:
45 #include "qshareddata.h"
46 
1346 #include <qset.h>
1347 #include <qmutex.h>
1348 
1349 #if !defined(QT_NO_QOBJECT)
1350 #include "private/qobject_p.h"
1351 
1353 
1367 void QtSharedPointer::ExternalRefCountData::setQObjectShared(const QObject *obj, bool)
1368 {
1369  Q_ASSERT(obj);
1370  QObjectPrivate *d = QObjectPrivate::get(const_cast<QObject *>(obj));
1371 
1372  if (d->sharedRefcount)
1373  qFatal("QSharedPointer: pointer %p already has reference counting", obj);
1374  d->sharedRefcount = this;
1375 
1376  // QObject decreases the refcount too, so increase it up
1377  weakref.ref();
1378 }
1379 
1380 QtSharedPointer::ExternalRefCountData *QtSharedPointer::ExternalRefCountData::getAndRef(const QObject *obj)
1381 {
1382  Q_ASSERT(obj);
1383  QObjectPrivate *d = QObjectPrivate::get(const_cast<QObject *>(obj));
1384  Q_ASSERT_X(!d->wasDeleted, "QWeakPointer", "Detected QWeakPointer creation in a QObject being deleted");
1385 
1386  ExternalRefCountData *that = d->sharedRefcount;
1387  if (that) {
1388  that->weakref.ref();
1389  return that;
1390  }
1391 
1392  // we can create the refcount data because it doesn't exist
1393  ExternalRefCountData *x = new ExternalRefCountData(Qt::Uninitialized);
1394  x->strongref = -1;
1395  x->weakref = 2; // the QWeakPointer that called us plus the QObject itself
1396  if (!d->sharedRefcount.testAndSetRelease(0, x)) {
1397  delete x;
1398  d->sharedRefcount->weakref.ref();
1399  }
1400  return d->sharedRefcount;
1401 }
1402 
1404 
1405 #endif
1406 
1407 
1408 
1409 //# define QT_SHARED_POINTER_BACKTRACE_SUPPORT
1410 # ifdef QT_SHARED_POINTER_BACKTRACE_SUPPORT
1411 # if defined(__GLIBC__) && (__GLIBC__ >= 2) && !defined(__UCLIBC__) && !defined(QT_LINUXBASE)
1412 # define BACKTRACE_SUPPORTED
1413 # elif defined(Q_OS_MACX)
1414 # define BACKTRACE_SUPPORTED
1415 # endif
1416 # endif
1417 
1418 # if defined(BACKTRACE_SUPPORTED)
1419 # include <sys/types.h>
1420 # include <execinfo.h>
1421 # include <stdio.h>
1422 # include <unistd.h>
1423 # include <sys/wait.h>
1424 
1426 
1427 static inline QByteArray saveBacktrace() __attribute__((always_inline));
1428 static inline QByteArray saveBacktrace()
1429 {
1430  static const int maxFrames = 32;
1431 
1432  QByteArray stacktrace;
1433  stacktrace.resize(sizeof(void*) * maxFrames);
1434  int stack_size = backtrace((void**)stacktrace.data(), maxFrames);
1435  stacktrace.resize(sizeof(void*) * stack_size);
1436 
1437  return stacktrace;
1438 }
1439 
1440 static void printBacktrace(QByteArray stacktrace)
1441 {
1442  void *const *stack = (void *const *)stacktrace.constData();
1443  int stack_size = stacktrace.size() / sizeof(void*);
1444  char **stack_symbols = backtrace_symbols(stack, stack_size);
1445 
1446  int filter[2];
1447  pid_t child = -1;
1448  if (pipe(filter) != -1)
1449  child = fork();
1450  if (child == 0) {
1451  // child process
1452  dup2(fileno(stderr), fileno(stdout));
1453  dup2(filter[0], fileno(stdin));
1454  close(filter[0]);
1455  close(filter[1]);
1456  execlp("c++filt", "c++filt", "-n", NULL);
1457 
1458  // execlp failed
1459  execl("/bin/cat", "/bin/cat", NULL);
1460  _exit(127);
1461  }
1462 
1463  // parent process
1464  close(filter[0]);
1465  FILE *output;
1466  if (child == -1) {
1467  // failed forking
1468  close(filter[1]);
1469  output = stderr;
1470  } else {
1471  output = fdopen(filter[1], "w");
1472  }
1473 
1474  fprintf(stderr, "Backtrace of the first creation (most recent frame first):\n");
1475  for (int i = 0; i < stack_size; ++i) {
1476  if (strlen(stack_symbols[i]))
1477  fprintf(output, "#%-2d %s\n", i, stack_symbols[i]);
1478  else
1479  fprintf(output, "#%-2d %p\n", i, stack[i]);
1480  }
1481 
1482  if (child != -1) {
1483  fclose(output);
1484  waitpid(child, 0, 0);
1485  }
1486 }
1487 
1489 
1490 # endif // BACKTRACE_SUPPORTED
1491 
1492 namespace {
1494  struct Data {
1495  const volatile void *pointer;
1496 # ifdef BACKTRACE_SUPPORTED
1497  QByteArray backtrace;
1498 # endif
1499  };
1500 
1501  class KnownPointers
1502  {
1503  public:
1504  QMutex mutex;
1505  QHash<const void *, Data> dPointers;
1507  };
1508 }
1509 
1510 Q_GLOBAL_STATIC(KnownPointers, knownPointers)
1511 
1513 
1514 namespace QtSharedPointer {
1515  Q_CORE_EXPORT void internalSafetyCheckAdd(const volatile void *);
1516  Q_CORE_EXPORT void internalSafetyCheckRemove(const volatile void *);
1518 }
1519 
1523 void QtSharedPointer::internalSafetyCheckAdd(const volatile void *)
1524 {
1525  // Qt 4.5 compatibility
1526  // this function is broken by design, so it was replaced with internalSafetyCheckAdd2
1527  //
1528  // it's broken because we tracked the pointers added and
1529  // removed from QSharedPointer, converted to void*.
1530  // That is, this is supposed to track the "top-of-object" pointer in
1531  // case of multiple inheritance.
1532  //
1533  // However, it doesn't work well in some compilers:
1534  // if you create an object with a class of type A and the last reference
1535  // is dropped of type B, then the value passed to internalSafetyCheckRemove could
1536  // be different than was added. That would leave dangling addresses.
1537  //
1538  // So instead, we track the pointer by the d-pointer instead.
1539 }
1540 
1545 {
1546  // Qt 4.5 compatibility
1547  // see comments above
1548 }
1549 
1553 void QtSharedPointer::internalSafetyCheckAdd2(const void *d_ptr, const volatile void *ptr)
1554 {
1555  // see comments above for the rationale for this function
1556  KnownPointers *const kp = knownPointers();
1557  if (!kp)
1558  return; // end-game: the application is being destroyed already
1559 
1560  QMutexLocker lock(&kp->mutex);
1561  Q_ASSERT(!kp->dPointers.contains(d_ptr));
1562 
1563  //qDebug("Adding d=%p value=%p", d_ptr, ptr);
1564 
1565  const void *other_d_ptr = kp->dataPointers.value(ptr, 0);
1566  if (other_d_ptr) {
1567 # ifdef BACKTRACE_SUPPORTED
1568  printBacktrace(knownPointers()->dPointers.value(other_d_ptr).backtrace);
1569 # endif
1570  qFatal("QSharedPointer: internal self-check failed: pointer %p was already tracked "
1571  "by another QSharedPointer object %p", ptr, other_d_ptr);
1572  }
1573 
1574  Data data;
1575  data.pointer = ptr;
1576 # ifdef BACKTRACE_SUPPORTED
1577  data.backtrace = saveBacktrace();
1578 # endif
1579 
1580  kp->dPointers.insert(d_ptr, data);
1581  kp->dataPointers.insert(ptr, d_ptr);
1582  Q_ASSERT(kp->dPointers.size() == kp->dataPointers.size());
1583 }
1584 
1588 void QtSharedPointer::internalSafetyCheckRemove2(const void *d_ptr)
1589 {
1590  KnownPointers *const kp = knownPointers();
1591  if (!kp)
1592  return; // end-game: the application is being destroyed already
1593 
1594  QMutexLocker lock(&kp->mutex);
1595 
1596  QHash<const void *, Data>::iterator it = kp->dPointers.find(d_ptr);
1597  if (it == kp->dPointers.end()) {
1598  qFatal("QSharedPointer: internal self-check inconsistency: pointer %p was not tracked. "
1599  "To use QT_SHAREDPOINTER_TRACK_POINTERS, you have to enable it throughout "
1600  "in your code.", d_ptr);
1601  }
1602 
1603  QHash<const volatile void *, const void *>::iterator it2 = kp->dataPointers.find(it->pointer);
1604  Q_ASSERT(it2 != kp->dataPointers.end());
1605 
1606  //qDebug("Removing d=%p value=%p", d_ptr, it->pointer);
1607 
1608  // remove entries
1609  kp->dataPointers.erase(it2);
1610  kp->dPointers.erase(it);
1611  Q_ASSERT(kp->dPointers.size() == kp->dataPointers.size());
1612 }
1613 
1619 {
1620 # ifdef QT_BUILD_INTERNAL
1621  KnownPointers *const kp = knownPointers();
1622  Q_ASSERT_X(kp, "internalSafetyCheckSelfCheck()", "Called after global statics deletion!");
1623 
1624  if (kp->dPointers.size() != kp->dataPointers.size())
1625  qFatal("Internal consistency error: the number of pointers is not equal!");
1626 
1627  if (!kp->dPointers.isEmpty())
1628  qFatal("Pointer cleaning failed: %d entries remaining", kp->dPointers.size());
1629 # endif
1630 }
1631 
double d
Definition: qnumeric_p.h:62
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
The QMutex class provides access serialization between threads.
Definition: qmutex.h:60
char * data()
Returns a pointer to the data stored in the byte array.
Definition: qbytearray.h:429
#define it(className, varName)
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:135
uint wasDeleted
Definition: qobject.h:98
The QHash class is a template class that provides a hash-table-based dictionary.
Definition: qdatastream.h:66
Q_AUTOTEST_EXPORT void internalSafetyCheckCleanCheck()
#define Q_ASSERT(cond)
Definition: qglobal.h:1823
The QObject class is the base class of all Qt objects.
Definition: qobject.h:111
static QObjectPrivate * get(QObject *o)
Definition: qobject_p.h:177
Q_CORE_EXPORT void internalSafetyCheckRemove(const volatile void *)
bool testAndSetRelease(T *expectedValue, T *newValue)
Atomic test-and-set.
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
#define Q_GLOBAL_STATIC(TYPE, NAME)
Declares a global static variable with the given type and name.
Definition: qglobal.h:1968
static const char * data(const QByteArray &arr)
const T * ptr(const T &t)
const char * constData() const
Returns a pointer to the data stored in the byte array.
Definition: qbytearray.h:433
Q_CORE_EXPORT void qFatal(const char *,...)
#define Q_ASSERT_X(cond, where, what)
Definition: qglobal.h:1837
The QMutexLocker class is a convenience class that simplifies locking and unlocking mutexes...
Definition: qmutex.h:101
#define Q_CORE_EXPORT
Definition: qglobal.h:1449
void resize(int size)
Sets the size of the byte array to size bytes.
The QHash::iterator class provides an STL-style non-const iterator for QHash and QMultiHash.
Definition: qhash.h:330
static QReadWriteLock lock
Definition: proxyconf.cpp:399
#define Q_AUTOTEST_EXPORT
Definition: qglobal.h:1510
QFuture< void > filter(Sequence &sequence, FilterFunction filterFunction)
int size() const
Returns the number of bytes in this byte array.
Definition: qbytearray.h:402
#define QT_USE_NAMESPACE
This macro expands to using QT_NAMESPACE if QT_NAMESPACE is defined and nothing otherwise.
Definition: qglobal.h:88
Q_CORE_EXPORT void internalSafetyCheckAdd(const volatile void *)
#define fdopen(a, b)
QAtomicPointer< QtSharedPointer::ExternalRefCountData > sharedRefcount
Definition: qobject_p.h:219