QtBase  v6.3.1
qsharedpointer.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2020 The Qt Company Ltd.
4 ** Copyright (C) 2020 Intel Corporation.
5 ** Copyright (C) 2019 Klarälvdalens Datakonsult AB.
6 ** Contact: https://www.qt.io/licensing/
7 **
8 ** This file is part of the QtCore module of the Qt Toolkit.
9 **
10 ** $QT_BEGIN_LICENSE:LGPL$
11 ** Commercial License Usage
12 ** Licensees holding valid commercial Qt licenses may use this file in
13 ** accordance with the commercial license agreement provided with the
14 ** Software or, alternatively, in accordance with the terms contained in
15 ** a written agreement between you and The Qt Company. For licensing terms
16 ** and conditions see https://www.qt.io/terms-conditions. For further
17 ** information use the contact form at https://www.qt.io/contact-us.
18 **
19 ** GNU Lesser General Public License Usage
20 ** Alternatively, this file may be used under the terms of the GNU Lesser
21 ** General Public License version 3 as published by the Free Software
22 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
23 ** packaging of this file. Please review the following information to
24 ** ensure the GNU Lesser General Public License version 3 requirements
25 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
26 **
27 ** GNU General Public License Usage
28 ** Alternatively, this file may be used under the terms of the GNU
29 ** General Public License version 2.0 or (at your option) the GNU General
30 ** Public license version 3 or any later version approved by the KDE Free
31 ** Qt Foundation. The licenses are as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
33 ** included in the packaging of this file. Please review the following
34 ** information to ensure the GNU General Public License requirements will
35 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
36 ** https://www.gnu.org/licenses/gpl-3.0.html.
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 
1361 #include <qset.h>
1362 #include <qmutex.h>
1363 
1364 #if !defined(QT_NO_QOBJECT)
1365 #include "private/qobject_p.h"
1366 
1368 
1375 {}
1376 
1385 {
1386  if (strongref.loadRelaxed() < 0)
1387  qWarning("QSharedPointer: cannot create a QSharedPointer from a QObject-tracking QWeakPointer");
1388 }
1389 
1391 {
1392  Q_ASSERT(obj);
1393  QObjectPrivate *d = QObjectPrivate::get(const_cast<QObject *>(obj));
1394  Q_ASSERT_X(!d->wasDeleted, "QWeakPointer", "Detected QWeakPointer creation in a QObject being deleted");
1395 
1396  ExternalRefCountData *that = d->sharedRefcount.loadRelaxed();
1397  if (that) {
1398  that->weakref.ref();
1399  return that;
1400  }
1401 
1402  // we can create the refcount data because it doesn't exist
1404  x->strongref.storeRelaxed(-1);
1405  x->weakref.storeRelaxed(2); // the QWeakPointer that called us plus the QObject itself
1406 
1408  if (d->sharedRefcount.testAndSetOrdered(nullptr, x, ret)) { // ought to be release+acquire; this is acq_rel+acquire
1409  ret = x;
1410  } else {
1411  // ~ExternalRefCountData has a Q_ASSERT, so we use this trick to
1412  // only execute this if Q_ASSERTs are enabled
1413  Q_ASSERT((x->weakref.storeRelaxed(0), true));
1414  ::delete x;
1415  ret->weakref.ref();
1416  }
1417  return ret;
1418 }
1419 
1426 {
1428  return *reinterpret_cast<const QSharedPointer<QObject>*>(variant.constData());
1429 }
1430 
1437 {
1440  return *reinterpret_cast<const QWeakPointer<QObject>*>(variant.constData());
1441 }
1442 
1444 
1445 #endif
1446 
1447 
1448 
1449 //# define QT_SHARED_POINTER_BACKTRACE_SUPPORT
1450 # ifdef QT_SHARED_POINTER_BACKTRACE_SUPPORT
1451 # if defined(__GLIBC__) && (__GLIBC__ >= 2) && !defined(__UCLIBC__) && !defined(QT_LINUXBASE)
1452 # define BACKTRACE_SUPPORTED
1453 # elif defined(Q_OS_MAC)
1454 # define BACKTRACE_SUPPORTED
1455 # endif
1456 # endif
1457 
1458 # if defined(BACKTRACE_SUPPORTED)
1459 # include <sys/types.h>
1460 # include <execinfo.h>
1461 # include <stdio.h>
1462 # include <unistd.h>
1463 # include <sys/wait.h>
1464 
1466 
1467 static inline QByteArray saveBacktrace() __attribute__((always_inline));
1468 static inline QByteArray saveBacktrace()
1469 {
1470  static const int maxFrames = 32;
1471 
1472  QByteArray stacktrace;
1473  stacktrace.resize(sizeof(void*) * maxFrames);
1474  int stack_size = backtrace((void**)stacktrace.data(), maxFrames);
1475  stacktrace.resize(sizeof(void*) * stack_size);
1476 
1477  return stacktrace;
1478 }
1479 
1480 static void printBacktrace(QByteArray stacktrace)
1481 {
1482  void *const *stack = (void *const *)stacktrace.constData();
1483  int stack_size = stacktrace.size() / sizeof(void*);
1484  char **stack_symbols = backtrace_symbols(stack, stack_size);
1485 
1486  int filter[2];
1487  pid_t child = -1;
1488  if (pipe(filter) != -1)
1489  child = fork();
1490  if (child == 0) {
1491  // child process
1492  dup2(fileno(stderr), fileno(stdout));
1493  dup2(filter[0], fileno(stdin));
1494  close(filter[0]);
1495  close(filter[1]);
1496  execlp("c++filt", "c++filt", "-n", NULL);
1497 
1498  // execlp failed
1499  execl("/bin/cat", "/bin/cat", NULL);
1500  _exit(127);
1501  }
1502 
1503  // parent process
1504  close(filter[0]);
1505  FILE *output;
1506  if (child == -1) {
1507  // failed forking
1508  close(filter[1]);
1509  output = stderr;
1510  } else {
1511  output = fdopen(filter[1], "w");
1512  }
1513 
1514  fprintf(stderr, "Backtrace of the first creation (most recent frame first):\n");
1515  for (int i = 0; i < stack_size; ++i) {
1516  if (strlen(stack_symbols[i]))
1517  fprintf(output, "#%-2d %s\n", i, stack_symbols[i]);
1518  else
1519  fprintf(output, "#%-2d %p\n", i, stack[i]);
1520  }
1521 
1522  if (child != -1) {
1523  fclose(output);
1524  waitpid(child, 0, 0);
1525  }
1526 }
1527 
1529 
1530 # endif // BACKTRACE_SUPPORTED
1531 
1532 namespace {
1534  struct Data {
1535  const volatile void *pointer;
1536 # ifdef BACKTRACE_SUPPORTED
1537  QByteArray backtrace;
1538 # endif
1539  };
1540 
1542  {
1543  public:
1547  };
1548 }
1549 
1550 Q_GLOBAL_STATIC(KnownPointers, knownPointers)
1551 
1553 
1554 namespace QtSharedPointer {
1556 }
1557 
1561 void QtSharedPointer::internalSafetyCheckAdd(const void *d_ptr, const volatile void *ptr)
1562 {
1563  KnownPointers *const kp = knownPointers();
1564  if (!kp)
1565  return; // end-game: the application is being destroyed already
1566 
1567  if (!ptr) {
1568  // nullptr is allowed to be tracked by more than one QSharedPointer, so we
1569  // need something else to put in our tracking structures
1570  ptr = d_ptr;
1571  }
1572 
1573  QMutexLocker lock(&kp->mutex);
1574  Q_ASSERT(!kp->dPointers.contains(d_ptr));
1575 
1576  //qDebug("Adding d=%p value=%p", d_ptr, ptr);
1577 
1578  const void *other_d_ptr = kp->dataPointers.value(ptr, nullptr);
1579  if (Q_UNLIKELY(other_d_ptr)) {
1580 # ifdef BACKTRACE_SUPPORTED
1581  printBacktrace(knownPointers()->dPointers.value(other_d_ptr).backtrace);
1582 # endif
1583  qFatal("QSharedPointer: internal self-check failed: pointer %p was already tracked "
1584  "by another QSharedPointer object %p", ptr, other_d_ptr);
1585  }
1586 
1587  Data data;
1588  data.pointer = ptr;
1589 # ifdef BACKTRACE_SUPPORTED
1590  data.backtrace = saveBacktrace();
1591 # endif
1592 
1593  kp->dPointers.insert(d_ptr, data);
1594  kp->dataPointers.insert(ptr, d_ptr);
1595  Q_ASSERT(kp->dPointers.size() == kp->dataPointers.size());
1596 }
1597 
1602 {
1603  KnownPointers *const kp = knownPointers();
1604  if (!kp)
1605  return; // end-game: the application is being destroyed already
1606 
1607  QMutexLocker lock(&kp->mutex);
1608 
1609  const auto it = kp->dPointers.constFind(d_ptr);
1610  if (Q_UNLIKELY(it == kp->dPointers.cend())) {
1611  qFatal("QSharedPointer: internal self-check inconsistency: pointer %p was not tracked. "
1612  "To use QT_SHAREDPOINTER_TRACK_POINTERS, you have to enable it throughout "
1613  "in your code.", d_ptr);
1614  }
1615 
1616  const auto it2 = kp->dataPointers.constFind(it->pointer);
1617  Q_ASSERT(it2 != kp->dataPointers.cend());
1618 
1619  //qDebug("Removing d=%p value=%p", d_ptr, it->pointer);
1620 
1621  // remove entries
1622  kp->dataPointers.erase(it2);
1623  kp->dPointers.erase(it);
1624  Q_ASSERT(kp->dPointers.size() == kp->dataPointers.size());
1625 }
1626 
1632 {
1633 # ifdef QT_BUILD_INTERNAL
1634  KnownPointers *const kp = knownPointers();
1635  Q_ASSERT_X(kp, "internalSafetyCheckSelfCheck()", "Called after global statics deletion!");
1636 
1637  if (Q_UNLIKELY(kp->dPointers.size() != kp->dataPointers.size()))
1638  qFatal("Internal consistency error: the number of pointers is not equal!");
1639 
1640  if (Q_UNLIKELY(!kp->dPointers.isEmpty()))
1641  qFatal("Pointer cleaning failed: %d entries remaining", int(kp->dPointers.size()));
1642 # endif
1643 }
1644 
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
bool ref() noexcept
Definition: qbasicatomic.h:101
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:85
char * data()
Definition: qbytearray.h:516
qsizetype size() const noexcept
Definition: qbytearray.h:470
const char * constData() const noexcept
Definition: qbytearray.h:144
void resize(qsizetype size)
The QHash class is a template class that provides a hash-table-based dictionary.
Definition: qhash.h:773
constexpr TypeFlags flags() const
Definition: qmetatype.h:2516
@ SharedPointerToQObject
Definition: qmetatype.h:394
@ WeakPointerToQObject
Definition: qmetatype.h:395
@ TrackingPointerToQObject
Definition: qmetatype.h:396
The QMutex class provides access serialization between threads.
Definition: qmutex.h:285
The QMutexLocker class is a convenience class that simplifies locking and unlocking mutexes.
Definition: qmutex.h:317
The QObject class is the base class of all Qt objects.
Definition: qobject.h:125
static QObjectPrivate * get(QObject *o)
Definition: qobject_p.h:368
The QSharedPointer class holds a strong reference to a shared pointer.
QHash< const void *, Data > dPointers
QHash< const volatile void *, const void * > dataPointers
The QVariant class acts like a union for the most common Qt data types.
Definition: qvariant.h:95
QMetaType metaType() const
Definition: qvariant.cpp:1003
const void * constData() const
Definition: qvariant.h:350
The QWeakPointer class holds a weak reference to a shared pointer.
#define NULL
Definition: ftobjs.h:61
#define __attribute__(x)
Definition: hb.hh:256
#define inline
Definition: md4c.c:45
constexpr Initialization Uninitialized
Definition: qnamespace.h:1613
Q_CORE_EXPORT QWeakPointer< QObject > weakPointerFromVariant_internal(const QVariant &variant)
Q_CORE_EXPORT void internalSafetyCheckRemove(const void *)
Q_CORE_EXPORT void internalSafetyCheckAdd(const void *, const volatile void *)
Q_AUTOTEST_EXPORT void internalSafetyCheckCleanCheck()
Q_CORE_EXPORT QSharedPointer< QObject > sharedPointerFromVariant_internal(const QVariant &variant)
PCRE2_SIZE PRIV() strlen(PCRE2_SPTR str)
#define Q_UNLIKELY(x)
#define Q_AUTOTEST_EXPORT
Definition: qglobal.h:579
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qWarning
Definition: qlogging.h:179
#define qFatal
Definition: qlogging.h:181
GLint GLint GLint GLint GLint x
[0]
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLhandleARB obj
[2]
Definition: qopenglext.h:4164
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
#define Q_ASSERT_X(cond, x, msg)
Definition: qrandom.cpp:85
QVariant variant
[1]
QReadWriteLock lock
[0]
QLayoutItem * child
[0]
QStringList::Iterator it
const volatile void * pointer
Q_CORE_EXPORT void setQObjectShared(const QObject *, bool enable)
Q_CORE_EXPORT void checkQObjectShared(const QObject *)
static Q_CORE_EXPORT ExternalRefCountData * getAndRef(const QObject *)