Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qsharedpointer.cpp
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// Copyright (C) 2020 Intel Corporation.
3// Copyright (C) 2019 Klarälvdalens Datakonsult AB.
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
6#include "qsharedpointer.h"
7
8// to be sure we aren't causing a namespace clash:
9#include "qshareddata.h"
10
1374#include <qset.h>
1375#include <qmutex.h>
1376
1377#if !defined(QT_NO_QOBJECT)
1378#include "private/qobject_p.h"
1379
1381
1388{}
1389
1398{
1399 if (strongref.loadRelaxed() < 0)
1400 qWarning("QSharedPointer: cannot create a QSharedPointer from a QObject-tracking QWeakPointer");
1401}
1402
1404{
1405 Q_ASSERT(obj);
1406 QObjectPrivate *d = QObjectPrivate::get(const_cast<QObject *>(obj));
1407 Q_ASSERT_X(!d->wasDeleted, "QWeakPointer", "Detected QWeakPointer creation in a QObject being deleted");
1408
1409 ExternalRefCountData *that = d->sharedRefcount.loadRelaxed();
1410 if (that) {
1411 that->weakref.ref();
1412 return that;
1413 }
1414
1415 // we can create the refcount data because it doesn't exist
1417 x->strongref.storeRelaxed(-1);
1418 x->weakref.storeRelaxed(2); // the QWeakPointer that called us plus the QObject itself
1419
1421 if (d->sharedRefcount.testAndSetOrdered(nullptr, x, ret)) { // ought to be release+acquire; this is acq_rel+acquire
1422 ret = x;
1423 } else {
1424 // ~ExternalRefCountData has a Q_ASSERT, so we use this trick to
1425 // only execute this if Q_ASSERTs are enabled
1426 Q_ASSERT((x->weakref.storeRelaxed(0), true));
1427 ::delete x;
1428 ret->weakref.ref();
1429 }
1430 return ret;
1431}
1432
1439{
1441 return *reinterpret_cast<const QSharedPointer<QObject>*>(variant.constData());
1442}
1443
1450{
1453 return *reinterpret_cast<const QWeakPointer<QObject>*>(variant.constData());
1454}
1455
1457
1458#endif
1459
1460
1461
1462//# define QT_SHARED_POINTER_BACKTRACE_SUPPORT
1463# ifdef QT_SHARED_POINTER_BACKTRACE_SUPPORT
1464# if defined(__GLIBC__) && (__GLIBC__ >= 2) && !defined(__UCLIBC__) && !defined(QT_LINUXBASE)
1465# define BACKTRACE_SUPPORTED
1466# elif defined(Q_OS_DARWIN)
1467# define BACKTRACE_SUPPORTED
1468# endif
1469# endif
1470
1471# if defined(BACKTRACE_SUPPORTED)
1472# include <sys/types.h>
1473# include <execinfo.h>
1474# include <stdio.h>
1475# include <unistd.h>
1476# include <sys/wait.h>
1477
1479
1480static inline QByteArray saveBacktrace() __attribute__((always_inline));
1481static inline QByteArray saveBacktrace()
1482{
1483 static const int maxFrames = 32;
1484
1485 QByteArray stacktrace;
1486 stacktrace.resize(sizeof(void*) * maxFrames);
1487 int stack_size = backtrace((void**)stacktrace.data(), maxFrames);
1488 stacktrace.resize(sizeof(void*) * stack_size);
1489
1490 return stacktrace;
1491}
1492
1493static void printBacktrace(QByteArray stacktrace)
1494{
1495 void *const *stack = (void *const *)stacktrace.constData();
1496 int stack_size = stacktrace.size() / sizeof(void*);
1497 char **stack_symbols = backtrace_symbols(stack, stack_size);
1498
1499 int filter[2];
1500 pid_t child = -1;
1501 if (pipe(filter) != -1)
1502 child = fork();
1503 if (child == 0) {
1504 // child process
1505 dup2(fileno(stderr), fileno(stdout));
1506 dup2(filter[0], fileno(stdin));
1507 close(filter[0]);
1508 close(filter[1]);
1509 execlp("c++filt", "c++filt", "-n", NULL);
1510
1511 // execlp failed
1512 execl("/bin/cat", "/bin/cat", NULL);
1513 _exit(127);
1514 }
1515
1516 // parent process
1517 close(filter[0]);
1518 FILE *output;
1519 if (child == -1) {
1520 // failed forking
1521 close(filter[1]);
1522 output = stderr;
1523 } else {
1524 output = fdopen(filter[1], "w");
1525 }
1526
1527 fprintf(stderr, "Backtrace of the first creation (most recent frame first):\n");
1528 for (int i = 0; i < stack_size; ++i) {
1529 if (strlen(stack_symbols[i]))
1530 fprintf(output, "#%-2d %s\n", i, stack_symbols[i]);
1531 else
1532 fprintf(output, "#%-2d %p\n", i, stack[i]);
1533 }
1534
1535 if (child != -1) {
1536 fclose(output);
1537 waitpid(child, 0, 0);
1538 }
1539}
1540
1542
1543# endif // BACKTRACE_SUPPORTED
1544
1545namespace {
1547 struct Data {
1548 const volatile void *pointer;
1549# ifdef BACKTRACE_SUPPORTED
1550 QByteArray backtrace;
1551# endif
1552 };
1553
1555 {
1556 public:
1560 };
1561}
1562
1563Q_GLOBAL_STATIC(KnownPointers, knownPointers)
1564
1566
1567namespace QtSharedPointer {
1569}
1570
1574void QtSharedPointer::internalSafetyCheckAdd(const void *d_ptr, const volatile void *ptr)
1575{
1576 KnownPointers *const kp = knownPointers();
1577 if (!kp)
1578 return; // end-game: the application is being destroyed already
1579
1580 if (!ptr) {
1581 // nullptr is allowed to be tracked by more than one QSharedPointer, so we
1582 // need something else to put in our tracking structures
1583 ptr = d_ptr;
1584 }
1585
1586 QMutexLocker lock(&kp->mutex);
1587 Q_ASSERT(!kp->dPointers.contains(d_ptr));
1588
1589 //qDebug("Adding d=%p value=%p", d_ptr, ptr);
1590
1591 const void *other_d_ptr = kp->dataPointers.value(ptr, nullptr);
1592 if (Q_UNLIKELY(other_d_ptr)) {
1593# ifdef BACKTRACE_SUPPORTED
1594 printBacktrace(knownPointers()->dPointers.value(other_d_ptr).backtrace);
1595# endif
1596 qFatal("QSharedPointer: internal self-check failed: pointer %p was already tracked "
1597 "by another QSharedPointer object %p", ptr, other_d_ptr);
1598 }
1599
1600 Data data;
1601 data.pointer = ptr;
1602# ifdef BACKTRACE_SUPPORTED
1603 data.backtrace = saveBacktrace();
1604# endif
1605
1606 kp->dPointers.insert(d_ptr, data);
1607 kp->dataPointers.insert(ptr, d_ptr);
1608 Q_ASSERT(kp->dPointers.size() == kp->dataPointers.size());
1609}
1610
1615{
1616 KnownPointers *const kp = knownPointers();
1617 if (!kp)
1618 return; // end-game: the application is being destroyed already
1619
1620 QMutexLocker lock(&kp->mutex);
1621
1622 const auto it = kp->dPointers.constFind(d_ptr);
1623 if (Q_UNLIKELY(it == kp->dPointers.cend())) {
1624 qFatal("QSharedPointer: internal self-check inconsistency: pointer %p was not tracked. "
1625 "To use QT_SHAREDPOINTER_TRACK_POINTERS, you have to enable it throughout "
1626 "in your code.", d_ptr);
1627 }
1628
1629 const auto it2 = kp->dataPointers.constFind(it->pointer);
1630 Q_ASSERT(it2 != kp->dataPointers.cend());
1631
1632 //qDebug("Removing d=%p value=%p", d_ptr, it->pointer);
1633
1634 // remove entries
1635 kp->dataPointers.erase(it2);
1636 kp->dPointers.erase(it);
1637 Q_ASSERT(kp->dPointers.size() == kp->dataPointers.size());
1638}
1639
1645{
1646# ifdef QT_BUILD_INTERNAL
1647 KnownPointers *const kp = knownPointers();
1648 Q_ASSERT_X(kp, "internalSafetyCheckSelfCheck()", "Called after global statics deletion!");
1649
1650 if (Q_UNLIKELY(kp->dPointers.size() != kp->dataPointers.size()))
1651 qFatal("Internal consistency error: the number of pointers is not equal!");
1652
1653 if (Q_UNLIKELY(!kp->dPointers.isEmpty()))
1654 qFatal("Pointer cleaning failed: %d entries remaining", int(kp->dPointers.size()));
1655# endif
1656}
1657
bool ref() noexcept
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
\inmodule QtCore
Definition qhash.h:818
constexpr TypeFlags flags() const
Definition qmetatype.h:2628
@ SharedPointerToQObject
Definition qmetatype.h:387
@ WeakPointerToQObject
Definition qmetatype.h:388
@ TrackingPointerToQObject
Definition qmetatype.h:389
\inmodule QtCore
Definition qmutex.h:317
\inmodule QtCore
Definition qmutex.h:285
static QObjectPrivate * get(QObject *o)
Definition qobject_p.h:153
\inmodule QtCore
Definition qobject.h:90
value_type * pointer
Definition qset.h:170
const_iterator cend() const noexcept
Definition qset.h:142
const_iterator constFind(const T &value) const
Definition qset.h:161
\inmodule QtCore
QHash< const void *, Data > dPointers
QHash< const volatile void *, const void * > dataPointers
\inmodule QtCore
Definition qvariant.h:64
QMetaType metaType() const
const void * constData() const
Definition qvariant.h:446
\inmodule QtCore
QSet< QString >::iterator it
Combined button and popup list for selecting options.
Q_CORE_EXPORT QWeakPointer< QObject > weakPointerFromVariant_internal(const QVariant &variant)
Q_AUTOTEST_EXPORT void internalSafetyCheckCleanCheck()
Q_CORE_EXPORT void internalSafetyCheckRemove(const void *)
Q_CORE_EXPORT void internalSafetyCheckAdd(const void *, const volatile void *)
Q_CORE_EXPORT QSharedPointer< QObject > sharedPointerFromVariant_internal(const QVariant &variant)
constexpr Initialization Uninitialized
#define Q_UNLIKELY(x)
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
#define qWarning
Definition qlogging.h:162
#define qFatal
Definition qlogging.h:164
return ret
static ControlElement< T > * ptr(QWidget *widget)
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]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
#define Q_AUTOTEST_EXPORT
QT_BEGIN_NAMESPACE typedef uchar * output
QVariant variant
[1]
QReadWriteLock lock
[0]
QLayoutItem * child
[0]
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 *)