summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qobject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel/qobject.cpp')
-rw-r--r--src/corelib/kernel/qobject.cpp1102
1 files changed, 688 insertions, 414 deletions
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index ec4da51ad9..708b10a75e 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -1,52 +1,18 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qobject.h"
#include "qobject_p.h"
+#include "qobject_p_p.h"
#include "qmetaobject_p.h"
#include "qabstracteventdispatcher.h"
#include "qabstracteventdispatcher_p.h"
#include "qcoreapplication.h"
#include "qcoreapplication_p.h"
+#include "qcoreevent_p.h"
#include "qloggingcategory.h"
#include "qvariant.h"
#include "qmetaobject.h"
@@ -56,14 +22,12 @@
#include <qthread.h>
#include <private/qthread_p.h>
#include <qdebug.h>
-#include <qpair.h>
#include <qvarlengtharray.h>
#include <qscopeguard.h>
#include <qset.h>
#if QT_CONFIG(thread)
#include <qsemaphore.h>
#endif
-#include <qsharedpointer.h>
#include <private/qorderedmutexlocker_p.h>
#include <private/qhooks_p.h>
@@ -71,12 +35,24 @@
#include <new>
#include <mutex>
+#include <memory>
#include <ctype.h>
#include <limits.h>
QT_BEGIN_NAMESPACE
+Q_TRACE_POINT(qtcore, QObject_ctor, QObject *object);
+Q_TRACE_POINT(qtcore, QObject_dtor, QObject *object);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_entry, QObject *sender, int signalIndex);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_exit);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_entry, QObject *receiver, int slotIndex);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_exit);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_functor_entry, void *slotObject);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_functor_exit);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_declarative_signal_entry, QObject *sender, int signalIndex);
+Q_TRACE_POINT(qtcore, QMetaObject_activate_declarative_signal_exit);
+
static int DIRECT_CONNECTION_ONLY = 0;
Q_LOGGING_CATEGORY(lcConnectSlotsByName, "qt.core.qmetaobject.connectslotsbyname")
@@ -108,6 +84,8 @@ static int *queuedConnectionTypes(const QMetaMethod &method)
typeIds[i] = QMetaType::VoidStar;
else
typeIds[i] = metaType.id();
+ if (!typeIds[i] && method.parameterTypeName(i).endsWith('*'))
+ typeIds[i] = QMetaType::VoidStar;
if (!typeIds[i]) {
const QByteArray typeName = method.parameterTypeName(i);
qCWarning(lcConnect,
@@ -125,7 +103,7 @@ static int *queuedConnectionTypes(const QMetaMethod &method)
static int *queuedConnectionTypes(const QArgumentType *argumentTypes, int argc)
{
- QScopedArrayPointer<int> types(new int[argc + 1]);
+ auto types = std::make_unique<int[]>(argc + 1);
for (int i = 0; i < argc; ++i) {
const QArgumentType &type = argumentTypes[i];
if (type.type())
@@ -145,10 +123,10 @@ static int *queuedConnectionTypes(const QArgumentType *argumentTypes, int argc)
}
types[argc] = 0;
- return types.take();
+ return types.release();
}
-static QBasicMutex _q_ObjectMutexPool[131];
+Q_CONSTINIT static QBasicMutex _q_ObjectMutexPool[131];
/**
* \internal
@@ -197,6 +175,10 @@ QObjectPrivate::QObjectPrivate(int version)
metaObject = nullptr;
isWindow = false;
deleteLaterCalled = false;
+ isQuickItem = false;
+ willBeWidget = false;
+ wasWidget = false;
+ receiveParentEvents = false; // If object wants ParentAboutToChange and ParentChange
}
QObjectPrivate::~QObjectPrivate()
@@ -209,8 +191,8 @@ QObjectPrivate::~QObjectPrivate()
thisThreadData->eventDispatcher.loadRelaxed()->unregisterTimers(q_ptr);
// release the timer ids back to the pool
- for (int i = 0; i < extraData->runningTimers.size(); ++i)
- QAbstractEventDispatcherPrivate::releaseTimerId(extraData->runningTimers.at(i));
+ for (auto id : std::as_const(extraData->runningTimers))
+ QAbstractEventDispatcherPrivate::releaseTimerId(id);
} else {
qWarning("QObject::~QObject: Timers cannot be stopped from another thread");
}
@@ -245,32 +227,11 @@ static void computeOffsets(const QMetaObject *metaobject, int *signalOffset, int
}
// Used by QAccessibleWidget
-bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const
-{
- Q_Q(const QObject);
- int signal_index = signalIndex(signal);
- ConnectionData *cd = connections.loadRelaxed();
- if (signal_index < 0 || !cd)
- return false;
- QBasicMutexLocker locker(signalSlotLock(q));
- if (signal_index < cd->signalVectorCount()) {
- const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
-
- while (c) {
- if (c->receiver.loadRelaxed() == receiver)
- return true;
- c = c->nextConnectionList.loadRelaxed();
- }
- }
- return false;
-}
-
-// Used by QAccessibleWidget
QObjectList QObjectPrivate::receiverList(const char *signal) const
{
QObjectList returnValue;
int signal_index = signalIndex(signal);
- ConnectionData *cd = connections.loadRelaxed();
+ ConnectionData *cd = connections.loadAcquire();
if (signal_index < 0 || !cd)
return returnValue;
if (signal_index < cd->signalVectorCount()) {
@@ -286,17 +247,17 @@ QObjectList QObjectPrivate::receiverList(const char *signal) const
return returnValue;
}
-// Used by QAccessibleWidget
-QObjectList QObjectPrivate::senderList() const
+/*!
+ \internal
+ The signalSlotLock() of the sender must be locked while calling this function
+*/
+inline void QObjectPrivate::ensureConnectionData()
{
- QObjectList returnValue;
- ConnectionData *cd = connections.loadRelaxed();
- if (cd) {
- QBasicMutexLocker locker(signalSlotLock(q_func()));
- for (Connection *c = cd->senders; c; c = c->next)
- returnValue << c->sender;
- }
- return returnValue;
+ if (connections.loadRelaxed())
+ return;
+ ConnectionData *cd = new ConnectionData;
+ cd->ref.ref();
+ connections.storeRelease(cd);
}
/*!
@@ -309,7 +270,7 @@ QObjectList QObjectPrivate::senderList() const
Will also add the connection in the sender's list of the receiver.
*/
-void QObjectPrivate::addConnection(int signal, Connection *c)
+inline void QObjectPrivate::addConnection(int signal, Connection *c)
{
Q_ASSERT(c->sender == q_ptr);
ensureConnectionData();
@@ -379,16 +340,16 @@ void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection
c->prevConnectionList->nextConnectionList.storeRelaxed(n);
c->prevConnectionList = nullptr;
- Q_ASSERT(c != orphaned.loadRelaxed());
+ Q_ASSERT(c != static_cast<Connection *>(orphaned.load(std::memory_order_relaxed)));
// add c to orphanedConnections
- Connection *o = nullptr;
+ TaggedSignalVector o = nullptr;
/* No ABA issue here: When adding a node, we only care about the list head, it doesn't
* matter if the tail changes.
*/
+ o = orphaned.load(std::memory_order_acquire);
do {
- o = orphaned.loadRelaxed();
c->nextInOrphanList = o;
- } while (!orphaned.testAndSetRelease(o, c));
+ } while (!orphaned.compare_exchange_strong(o, TaggedSignalVector(c), std::memory_order_release));
#ifndef QT_NO_DEBUG
found = false;
@@ -406,7 +367,7 @@ void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection
void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy)
{
QBasicMutex *senderMutex = signalSlotLock(sender);
- ConnectionOrSignalVector *c = nullptr;
+ TaggedSignalVector c = nullptr;
{
std::unique_lock<QBasicMutex> lock(*senderMutex, std::defer_lock_t{});
if (lockPolicy == NeedToLock)
@@ -417,7 +378,7 @@ void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sende
// Since ref == 1, no activate() is in process since we locked the mutex. That implies,
// that nothing can reference the orphaned connection objects anymore and they can
// be safely deleted
- c = orphaned.fetchAndStoreRelaxed(nullptr);
+ c = orphaned.exchange(nullptr, std::memory_order_relaxed);
}
if (c) {
// Deleting c might run arbitrary user code, so we must not hold the lock
@@ -431,11 +392,11 @@ void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sende
}
}
-void QObjectPrivate::ConnectionData::deleteOrphaned(QObjectPrivate::ConnectionOrSignalVector *o)
+inline void QObjectPrivate::ConnectionData::deleteOrphaned(TaggedSignalVector o)
{
while (o) {
- QObjectPrivate::ConnectionOrSignalVector *next = nullptr;
- if (SignalVector *v = ConnectionOrSignalVector::asSignalVector(o)) {
+ TaggedSignalVector next = nullptr;
+ if (SignalVector *v = static_cast<SignalVector *>(o)) {
next = v->nextInOrphanList;
free(v);
} else {
@@ -461,7 +422,7 @@ bool QObjectPrivate::isSignalConnected(uint signalIndex, bool checkDeclarative)
if (checkDeclarative && isDeclarativeSignalConnected(signalIndex))
return true;
- ConnectionData *cd = connections.loadRelaxed();
+ ConnectionData *cd = connections.loadAcquire();
if (!cd)
return false;
SignalVector *signalVector = cd->signalVector.loadRelaxed();
@@ -484,7 +445,7 @@ bool QObjectPrivate::isSignalConnected(uint signalIndex, bool checkDeclarative)
bool QObjectPrivate::maybeSignalConnected(uint signalIndex) const
{
- ConnectionData *cd = connections.loadRelaxed();
+ ConnectionData *cd = connections.loadAcquire();
if (!cd)
return false;
SignalVector *signalVector = cd->signalVector.loadRelaxed();
@@ -501,6 +462,13 @@ bool QObjectPrivate::maybeSignalConnected(uint signalIndex) const
return false;
}
+void QObjectPrivate::reinitBindingStorageAfterThreadMove()
+{
+ bindingStorage.reinitAfterThreadMove();
+ for (int i = 0; i < children.size(); ++i)
+ children[i]->d_func()->reinitBindingStorageAfterThreadMove();
+}
+
/*!
\internal
*/
@@ -554,7 +522,7 @@ QMetaCallEvent::QMetaCallEvent(QtPrivate::QSlotObjectBase *slotO,
const QObject *sender, int signalId,
void **args, QSemaphore *semaphore)
: QAbstractMetaCallEvent(sender, signalId, semaphore),
- d({slotO, args, nullptr, 0, 0, ushort(-1)}),
+ d({QtPrivate::SlotObjUniquePtr{slotO}, args, nullptr, 0, 0, ushort(-1)}),
prealloc_()
{
if (d.slotObj_)
@@ -564,6 +532,21 @@ QMetaCallEvent::QMetaCallEvent(QtPrivate::QSlotObjectBase *slotO,
/*!
\internal
+ Used for blocking queued connections, just passes \a args through without
+ allocating any memory.
+ */
+QMetaCallEvent::QMetaCallEvent(QtPrivate::SlotObjUniquePtr slotO,
+ const QObject *sender, int signalId,
+ void **args, QSemaphore *semaphore)
+ : QAbstractMetaCallEvent(sender, signalId, semaphore),
+ d{std::move(slotO), args, nullptr, 0, 0, ushort(-1)},
+ prealloc_()
+{
+}
+
+/*!
+ \internal
+
Allocates memory for \a nargs; code creating an event needs to initialize
the void* and int arrays by accessing \a args() and \a types(), respectively.
*/
@@ -588,7 +571,7 @@ QMetaCallEvent::QMetaCallEvent(QtPrivate::QSlotObjectBase *slotO,
const QObject *sender, int signalId,
int nargs)
: QAbstractMetaCallEvent(sender, signalId),
- d({slotO, nullptr, nullptr, nargs, 0, ushort(-1)}),
+ d({QtPrivate::SlotObjUniquePtr(slotO), nullptr, nullptr, nargs, 0, ushort(-1)}),
prealloc_()
{
if (d.slotObj_)
@@ -598,6 +581,22 @@ QMetaCallEvent::QMetaCallEvent(QtPrivate::QSlotObjectBase *slotO,
/*!
\internal
+
+ Allocates memory for \a nargs; code creating an event needs to initialize
+ the void* and int arrays by accessing \a args() and \a types(), respectively.
+ */
+QMetaCallEvent::QMetaCallEvent(QtPrivate::SlotObjUniquePtr slotO,
+ const QObject *sender, int signalId,
+ int nargs)
+ : QAbstractMetaCallEvent(sender, signalId),
+ d{std::move(slotO), nullptr, nullptr, nargs, 0, ushort(-1)},
+ prealloc_()
+{
+ allocArgs();
+}
+
+/*!
+ \internal
*/
QMetaCallEvent::~QMetaCallEvent()
{
@@ -610,8 +609,6 @@ QMetaCallEvent::~QMetaCallEvent()
if (reinterpret_cast<void *>(d.args_) != reinterpret_cast<void *>(prealloc_))
free(d.args_);
}
- if (d.slotObj_)
- d.slotObj_->destroyIfLastRef();
}
/*!
@@ -629,6 +626,25 @@ void QMetaCallEvent::placeMetaCall(QObject *object)
}
}
+QMetaCallEvent* QMetaCallEvent::create_impl(QtPrivate::SlotObjUniquePtr slotObj,
+ const QObject *sender, int signal_index,
+ size_t argc, const void* const argp[],
+ const QMetaType metaTypes[])
+{
+ auto metaCallEvent = std::make_unique<QMetaCallEvent>(std::move(slotObj), sender,
+ signal_index, int(argc));
+
+ void **args = metaCallEvent->args();
+ QMetaType *types = metaCallEvent->types();
+ for (size_t i = 0; i < argc; ++i) {
+ types[i] = metaTypes[i];
+ args[i] = types[i].create(argp[i]);
+ Q_CHECK_PTR(!i || args[i]);
+ }
+
+ return metaCallEvent.release();
+}
+
/*!
\class QSignalBlocker
\brief Exception-safe wrapper around QObject::blockSignals().
@@ -716,6 +732,14 @@ void QMetaCallEvent::placeMetaCall(QObject *object)
*/
/*!
+ \fn void QSignalBlocker::dismiss()
+ \since 6.7
+ Dismisses the QSignalBlocker. It will no longer access the QObject
+ passed to its constructor. unblock(), reblock(), as well as
+ ~QSignalBlocker() will have no effect.
+*/
+
+/*!
\class QObject
\inmodule QtCore
\brief The QObject class is the base class of all Qt objects.
@@ -756,7 +780,7 @@ void QMetaCallEvent::placeMetaCall(QObject *object)
to catch child events.
Last but not least, QObject provides the basic timer support in
- Qt; see QTimer for high-level support for timers.
+ Qt; see QChronoTimer for high-level support for timers.
Notice that the Q_OBJECT macro is mandatory for any object that
implements signals, slots or properties. You also need to run the
@@ -839,20 +863,20 @@ void QMetaCallEvent::placeMetaCall(QObject *object)
\l uic generates code that invokes this function to enable
auto-connection to be performed between widgets on forms created
- with \e{Qt Designer}. More information about using auto-connection with \e{Qt Designer} is
+ with \e{\QD}. More information about using auto-connection with \e{\QD} is
given in the \l{Using a Designer UI File in Your Application} section of
- the \e{Qt Designer} manual.
+ the \l{Qt Widgets Designer Manual}{\QD} manual.
\section1 Dynamic Properties
- From Qt 4.2, dynamic properties can be added to and removed from QObject
+ Dynamic properties can be added to and removed from QObject
instances at run-time. Dynamic properties do not need to be declared at
compile-time, yet they provide the same advantages as static properties
and are manipulated using the same API - using property() to read them
and setProperty() to write them.
- From Qt 4.3, dynamic properties are supported by
- \l{Qt Designer's Widget Editing Mode#The Property Editor}{Qt Designer},
+ Dynamic properties are supported by
+ \l{Qt Widgets Designer's Widget Editing Mode#The Property Editor}{\QD},
and both standard Qt widgets and user-created forms can be given dynamic
properties.
@@ -931,7 +955,7 @@ QObject::QObject(QObjectPrivate &dd, QObject *parent)
QT_TRY {
if (!check_parent_thread(parent, parent ? parent->d_func()->threadData.loadRelaxed() : nullptr, threadData))
parent = nullptr;
- if (d->isWidget) {
+ if (d->willBeWidget) {
if (parent) {
d->parent = parent;
d->parent->d_func()->children.append(this);
@@ -969,8 +993,8 @@ void QObjectPrivate::clearBindingStorage()
outside the parent. If you still do, the destroyed() signal gives
you an opportunity to detect when an object is destroyed.
- \warning Deleting a QObject while pending events are waiting to
- be delivered can cause a crash. You must not delete the QObject
+ \warning Deleting a QObject while it is handling an event
+ delivered to it can cause a crash. You must not delete the QObject
directly if it exists in a different thread than the one currently
executing. Use deleteLater() instead, which will cause the event
loop to delete the object after all pending events have been
@@ -985,6 +1009,16 @@ QObject::~QObject()
d->wasDeleted = true;
d->blockSig = 0; // unblock signals so we always emit destroyed()
+ if (!d->bindingStorage.isValid()) {
+ // this might be the case after an incomplete thread-move
+ // remove this object from the pending list in that case
+ if (QThread *ownThread = thread()) {
+ auto *privThread = static_cast<QThreadPrivate *>(
+ QObjectPrivate::get(ownThread));
+ privThread->removeObjectWithPendingBindingStatusChange(this);
+ }
+ }
+
// If we reached this point, we need to clear the binding data
// as the corresponding properties are no longer useful
d->clearBindingStorage();
@@ -1002,14 +1036,14 @@ QObject::~QObject()
delete sharedRefcount;
}
- if (!d->isWidget && d->isSignalConnected(0)) {
+ if (!d->wasWidget && d->isSignalConnected(0)) {
emit destroyed(this);
}
- if (d->declarativeData && QAbstractDeclarativeData::destroyed)
+ if (!d->isDeletingChildren && d->declarativeData && QAbstractDeclarativeData::destroyed)
QAbstractDeclarativeData::destroyed(d->declarativeData, this);
- QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
+ QObjectPrivate::ConnectionData *cd = d->connections.loadAcquire();
if (cd) {
if (cd->currentSender) {
cd->currentSender->receiverDeleted();
@@ -1017,7 +1051,7 @@ QObject::~QObject()
}
QBasicMutex *signalSlotMutex = signalSlotLock(this);
- QBasicMutexLocker locker(signalSlotMutex);
+ QMutexLocker locker(signalSlotMutex);
// disconnect all receivers
int receiverCount = cd->signalVectorCount();
@@ -1112,7 +1146,7 @@ QObject::~QObject()
d->setParent_helper(nullptr);
}
-QObjectPrivate::Connection::~Connection()
+inline QObjectPrivate::Connection::~Connection()
{
if (ownArgumentTypes) {
const int *v = argumentTypes.loadRelaxed();
@@ -1195,8 +1229,7 @@ QObjectPrivate::Connection::~Connection()
\c dynamic_cast(), with the advantages that it doesn't require
RTTI support and it works across dynamic library boundaries.
- qobject_cast() can also be used in conjunction with interfaces;
- see the \l{tools/plugandpaint/app}{Plug & Paint} example for details.
+ qobject_cast() can also be used in conjunction with interfaces.
\warning If T isn't declared with the Q_OBJECT macro, this
function's return value is undefined.
@@ -1242,6 +1275,10 @@ QObjectPrivate::Connection::~Connection()
QString QObject::objectName() const
{
Q_D(const QObject);
+#if QT_CONFIG(thread)
+ if (QThread::currentThreadId() != d->threadData.loadRelaxed()->threadId.loadRelaxed()) // Unsafe code path
+ return d->extraData ? d->extraData->objectName.valueBypassingBindings() : QString();
+#endif
if (!d->extraData && QtPrivate::isAnyBindingEvaluating()) {
QObjectPrivate *dd = const_cast<QObjectPrivate *>(d);
// extraData is mutable, so this should be safe
@@ -1250,30 +1287,47 @@ QString QObject::objectName() const
return d->extraData ? d->extraData->objectName : QString();
}
-/*
+/*!
+ \fn void QObject::setObjectName(const QString &name)
Sets the object's name to \a name.
*/
-void QObject::setObjectName(const QString &name)
+void QObject::doSetObjectName(const QString &name)
{
Q_D(QObject);
- if (!d->extraData)
- d->extraData = new QObjectPrivate::ExtraData(d);
+ d->ensureExtraData();
d->extraData->objectName.removeBindingUnlessInWrapper();
- if (d->extraData->objectName != name) {
+ if (d->extraData->objectName.valueBypassingBindings() != name) {
d->extraData->objectName.setValueBypassingBindings(name);
d->extraData->objectName.notify(); // also emits a signal
}
}
+/*!
+ \overload
+ \since 6.4
+*/
+void QObject::setObjectName(QAnyStringView name)
+{
+ Q_D(QObject);
+
+ d->ensureExtraData();
+
+ d->extraData->objectName.removeBindingUnlessInWrapper();
+
+ if (d->extraData->objectName.valueBypassingBindings() != name) {
+ d->extraData->objectName.setValueBypassingBindings(name.toString());
+ d->extraData->objectName.notify(); // also emits a signal
+ }
+}
+
QBindable<QString> QObject::bindableObjectName()
{
Q_D(QObject);
- if (!d->extraData)
- d->extraData = new QObjectPrivate::ExtraData(d);
+ d->ensureExtraData();
return QBindable<QString>(&d->extraData->objectName);
}
@@ -1304,6 +1358,17 @@ QBindable<QString> QObject::bindableObjectName()
*/
/*!
+ \fn bool QObject::isQuickItemType() const
+
+ Returns \c true if the object is a QQuickItem; otherwise returns \c false.
+
+ Calling this function is equivalent to calling
+ \c{inherits("QQuickItem")}, except that it is much faster.
+
+ \since 6.4
+*/
+
+/*!
This virtual function receives events to an object and should
return true if the event \a e was recognized and processed.
@@ -1335,18 +1400,21 @@ bool QObject::event(QEvent *e)
break;
case QEvent::DeferredDelete:
- qDeleteInEventHandler(this);
+ qCDebug(lcDeleteLater) << "Deferred deleting" << this;
+ delete this;
break;
case QEvent::MetaCall:
{
QAbstractMetaCallEvent *mce = static_cast<QAbstractMetaCallEvent*>(e);
- if (!d_func()->connections.loadRelaxed()) {
- QBasicMutexLocker locker(signalSlotLock(this));
+ QObjectPrivate::ConnectionData *connections = d_func()->connections.loadAcquire();
+ if (!connections) {
+ QMutexLocker locker(signalSlotLock(this));
d_func()->ensureConnectionData();
+ connections = d_func()->connections.loadRelaxed();
}
- QObjectPrivate::Sender sender(this, const_cast<QObject*>(mce->sender()), mce->signalId());
+ QObjectPrivate::Sender sender(this, const_cast<QObject*>(mce->sender()), mce->signalId(), connections);
mce->placeMetaCall(this);
break;
@@ -1357,12 +1425,20 @@ bool QObject::event(QEvent *e)
QThreadData *threadData = d->threadData.loadRelaxed();
QAbstractEventDispatcher *eventDispatcher = threadData->eventDispatcher.loadRelaxed();
if (eventDispatcher) {
- QList<QAbstractEventDispatcher::TimerInfo> timers = eventDispatcher->registeredTimers(this);
+ QList<QAbstractEventDispatcher::TimerInfoV2> timers = eventDispatcher->timersForObject(this);
if (!timers.isEmpty()) {
+ const bool res = eventDispatcher->unregisterTimers(this);
// do not to release our timer ids back to the pool (since the timer ids are moving to a new thread).
- eventDispatcher->unregisterTimers(this);
- QMetaObject::invokeMethod(this, "_q_reregisterTimers", Qt::QueuedConnection,
- Q_ARG(void*, (new QList<QAbstractEventDispatcher::TimerInfo>(timers))));
+ Q_ASSERT_X(res, Q_FUNC_INFO,
+ "QAbstractEventDispatcher::unregisterTimers() returned false,"
+ " but there are timers associated with this object.");
+ auto reRegisterTimers = [this, timers = std::move(timers)]() {
+ QAbstractEventDispatcher *eventDispatcher =
+ d_func()->threadData.loadRelaxed()->eventDispatcher.loadRelaxed();
+ for (const auto &ti : timers)
+ eventDispatcher->registerTimer(ti.timerId, ti.interval, ti.timerType, this);
+ };
+ QMetaObject::invokeMethod(this, std::move(reRegisterTimers), Qt::QueuedConnection);
}
}
break;
@@ -1384,9 +1460,9 @@ bool QObject::event(QEvent *e)
This event handler can be reimplemented in a subclass to receive
timer events for the object.
- QTimer provides a higher-level interface to the timer
- functionality, and also more general information about timers. The
- timer event is passed in the \a event parameter.
+ QChronoTimer provides higher-level interfaces to the timer functionality,
+ and also more general information about timers. The timer event is passed
+ in the \a event parameter.
\sa startTimer(), killTimer(), event()
*/
@@ -1525,9 +1601,9 @@ QThread *QObject::thread() const
}
/*!
- Changes the thread affinity for this object and its children. The
- object cannot be moved if it has a parent. Event processing will
- continue in the \a targetThread.
+ Changes the thread affinity for this object and its children and
+ returns \c true on success. The object cannot be moved if it has a
+ parent. Event processing will continue in the \a targetThread.
To move an object to the main thread, use QApplication::instance()
to retrieve a pointer to the current application, and then use
@@ -1564,46 +1640,46 @@ QThread *QObject::thread() const
\sa thread()
*/
-void QObject::moveToThread(QThread *targetThread)
+bool QObject::moveToThread(QThread *targetThread QT6_IMPL_NEW_OVERLOAD_TAIL)
{
Q_D(QObject);
if (d->threadData.loadRelaxed()->thread.loadAcquire() == targetThread) {
// object is already in this thread
- return;
+ return true;
}
if (d->parent != nullptr) {
qWarning("QObject::moveToThread: Cannot move objects with a parent");
- return;
+ return false;
}
if (d->isWidget) {
qWarning("QObject::moveToThread: Widgets cannot be moved to a new thread");
- return;
+ return false;
}
if (!d->bindingStorage.isEmpty()) {
qWarning("QObject::moveToThread: Can not move objects that contain bindings or are used in bindings to a new thread.");
- return;
+ return false;
}
QThreadData *currentData = QThreadData::current();
QThreadData *targetData = targetThread ? QThreadData::get2(targetThread) : nullptr;
- QThreadData *thisThreadData = d->threadData.loadRelaxed();
- if (!thisThreadData->thread.loadAcquire() && currentData == targetData) {
+ QThreadData *thisThreadData = d->threadData.loadAcquire();
+ if (!thisThreadData->thread.loadRelaxed() && currentData == targetData) {
// one exception to the rule: we allow moving objects with no thread affinity to the current thread
- currentData = d->threadData;
+ currentData = thisThreadData;
} else if (thisThreadData != currentData) {
qWarning("QObject::moveToThread: Current thread (%p) is not the object's thread (%p).\n"
"Cannot move to target thread (%p)\n",
currentData->thread.loadRelaxed(), thisThreadData->thread.loadRelaxed(), targetData ? targetData->thread.loadRelaxed() : nullptr);
-#ifdef Q_OS_MAC
+#ifdef Q_OS_DARWIN
qWarning("You might be loading two sets of Qt binaries into the same process. "
"Check that all plugins are compiled against the right Qt binaries. Export "
"DYLD_PRINT_LIBRARIES=1 and check that only one set of binaries are being loaded.");
#endif
- return;
+ return false;
}
// prepare to move
@@ -1622,12 +1698,22 @@ void QObject::moveToThread(QThread *targetThread)
currentData->ref();
// move the object
- d_func()->setThreadData_helper(currentData, targetData);
+ auto threadPrivate = targetThread
+ ? static_cast<QThreadPrivate *>(QThreadPrivate::get(targetThread))
+ : nullptr;
+ QBindingStatus *bindingStatus = threadPrivate
+ ? threadPrivate->bindingStatus()
+ : nullptr;
+ if (threadPrivate && !bindingStatus) {
+ bindingStatus = threadPrivate->addObjectWithPendingBindingStatusChange(this);
+ }
+ d_func()->setThreadData_helper(currentData, targetData, bindingStatus);
locker.unlock();
// now currentData can commit suicide if it wants to
currentData->deref();
+ return true;
}
void QObjectPrivate::moveToThread_helper()
@@ -1635,16 +1721,22 @@ void QObjectPrivate::moveToThread_helper()
Q_Q(QObject);
QEvent e(QEvent::ThreadChange);
QCoreApplication::sendEvent(q, &e);
+ bindingStorage.clear();
for (int i = 0; i < children.size(); ++i) {
QObject *child = children.at(i);
child->d_func()->moveToThread_helper();
}
}
-void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData *targetData)
+void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData *targetData, QBindingStatus *status)
{
Q_Q(QObject);
+ if (status) {
+ // the new thread is already running
+ this->bindingStorage.bindingStatus = status;
+ }
+
// move posted events
int eventsMoved = 0;
for (int i = 0; i < currentData->postEventList.size(); ++i) {
@@ -1664,7 +1756,7 @@ void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData
}
// the current emitting thread shouldn't restore currentSender after calling moveToThread()
- ConnectionData *cd = connections.loadRelaxed();
+ ConnectionData *cd = connections.loadAcquire();
if (cd) {
if (cd->currentSender) {
cd->currentSender->receiverDeleted();
@@ -1699,23 +1791,10 @@ void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData
for (int i = 0; i < children.size(); ++i) {
QObject *child = children.at(i);
- child->d_func()->setThreadData_helper(currentData, targetData);
- }
-}
-
-void QObjectPrivate::_q_reregisterTimers(void *pointer)
-{
- Q_Q(QObject);
- QList<QAbstractEventDispatcher::TimerInfo> *timerList = reinterpret_cast<QList<QAbstractEventDispatcher::TimerInfo> *>(pointer);
- QAbstractEventDispatcher *eventDispatcher = threadData.loadRelaxed()->eventDispatcher.loadRelaxed();
- for (int i = 0; i < timerList->size(); ++i) {
- const QAbstractEventDispatcher::TimerInfo &ti = timerList->at(i);
- eventDispatcher->registerTimer(ti.timerId, ti.interval, ti.timerType, q);
+ child->d_func()->setThreadData_helper(currentData, targetData, status);
}
- delete timerList;
}
-
//
// The timer flag hasTimer is set when startTimer is called.
// It is not reset when killing the timer because more than
@@ -1723,13 +1802,34 @@ void QObjectPrivate::_q_reregisterTimers(void *pointer)
//
/*!
+ \fn int QObject::startTimer(int interval, Qt::TimerType timerType)
+
+ This is an overloaded function that will start a timer of type
+ \a timerType and a timeout of \a interval milliseconds. This is
+ equivalent to calling:
+ \code
+ startTimer(std::chrono::milliseconds{interval}, timerType);
+ \endcode
+
+ \sa timerEvent(), killTimer(), QChronoTimer::singleShot()
+*/
+
+int QObject::startTimer(int interval, Qt::TimerType timerType)
+{
+ return startTimer(std::chrono::milliseconds{interval}, timerType);
+}
+
+/*!
+ \since 5.9
+ \overload
+
Starts a timer and returns a timer identifier, or returns zero if
it could not start a timer.
- A timer event will occur every \a interval milliseconds until
- killTimer() is called. If \a interval is 0, then the timer event
- occurs once every time there are no more window system events to
- process.
+ A timer event will occur every \a interval until killTimer()
+ is called. If \a interval is equal to \c{std::chrono::duration::zero()},
+ then the timer event occurs once every time there are no more window
+ system events to process.
The virtual timerEvent() function is called with the QTimerEvent
event parameter class when a timer event occurs. Reimplement this
@@ -1742,26 +1842,41 @@ void QObjectPrivate::_q_reregisterTimers(void *pointer)
\snippet code/src_corelib_kernel_qobject.cpp 8
- Note that QTimer's accuracy depends on the underlying operating system and
- hardware. The \a timerType argument allows you to customize the accuracy of
+ Note that the accuracy of QChronoTimer depends on the underlying operating
+ system and hardware.
+
+ The \a timerType argument allows you to customize the accuracy of
the timer. See Qt::TimerType for information on the different timer types.
Most platforms support an accuracy of 20 milliseconds; some provide more.
If Qt is unable to deliver the requested number of timer events, it will
silently discard some.
- The QTimer class provides a high-level programming interface with
- single-shot timers and timer signals instead of events. There is
- also a QBasicTimer class that is more lightweight than QTimer and
- less clumsy than using timer IDs directly.
+ The QTimer and QChronoTimer classes provide a high-level programming
+ interface with single-shot timers and timer signals instead of
+ events. There is also a QBasicTimer class that is more lightweight than
+ QChronoTimer but less clumsy than using timer IDs directly.
- \sa timerEvent(), killTimer(), QTimer::singleShot()
-*/
+ \sa timerEvent(), killTimer(), QChronoTimer::singleShot()
-int QObject::startTimer(int interval, Qt::TimerType timerType)
+ \note Starting from Qt 6.8 the type of \a interval
+ is \c std::chrono::nanoseconds, prior to that it was \c
+ std::chrono::milliseconds. This change is backwards compatible with
+ older releases of Qt.
+
+ \note In Qt 6.8, QObject was changed to use Qt::TimerId to represent timer
+ IDs. This method converts the TimerId to int for backwards compatibility
+ reasons, however you can use Qt::TimerId to check the value returned by
+ this method, for example:
+ \snippet code/src_corelib_kernel_qobject.cpp invalid-timer-id
+
+*/
+int QObject::startTimer(std::chrono::nanoseconds interval, Qt::TimerType timerType)
{
Q_D(QObject);
- if (Q_UNLIKELY(interval < 0)) {
+ using namespace std::chrono_literals;
+
+ if (Q_UNLIKELY(interval < 0ns)) {
qWarning("QObject::startTimer: Timers cannot have negative intervals");
return 0;
}
@@ -1775,53 +1890,15 @@ int QObject::startTimer(int interval, Qt::TimerType timerType)
qWarning("QObject::startTimer: Timers cannot be started from another thread");
return 0;
}
- int timerId = thisThreadData->eventDispatcher.loadRelaxed()->registerTimer(interval, timerType, this);
- if (!d->extraData)
- d->extraData = new QObjectPrivate::ExtraData(d);
+
+ auto dispatcher = thisThreadData->eventDispatcher.loadRelaxed();
+ Qt::TimerId timerId = dispatcher->registerTimer(interval, timerType, this);
+ d->ensureExtraData();
d->extraData->runningTimers.append(timerId);
- return timerId;
+ return int(timerId);
}
/*!
- \since 5.9
- \overload
- \fn int QObject::startTimer(std::chrono::milliseconds time, Qt::TimerType timerType)
-
- Starts a timer and returns a timer identifier, or returns zero if
- it could not start a timer.
-
- A timer event will occur every \a time interval until killTimer()
- is called. If \a time is equal to \c{std::chrono::duration::zero()},
- then the timer event occurs once every time there are no more window
- system events to process.
-
- The virtual timerEvent() function is called with the QTimerEvent
- event parameter class when a timer event occurs. Reimplement this
- function to get timer events.
-
- If multiple timers are running, the QTimerEvent::timerId() can be
- used to find out which timer was activated.
-
- Example:
-
- \snippet code/src_corelib_kernel_qobject.cpp 8
-
- Note that QTimer's accuracy depends on the underlying operating system and
- hardware. The \a timerType argument allows you to customize the accuracy of
- the timer. See Qt::TimerType for information on the different timer types.
- Most platforms support an accuracy of 20 milliseconds; some provide more.
- If Qt is unable to deliver the requested number of timer events, it will
- silently discard some.
-
- The QTimer class provides a high-level programming interface with
- single-shot timers and timer signals instead of events. There is
- also a QBasicTimer class that is more lightweight than QTimer and
- less clumsy than using timer IDs directly.
-
- \sa timerEvent(), killTimer(), QTimer::singleShot()
-*/
-
-/*!
Kills the timer with timer identifier, \a id.
The timer identifier is returned by startTimer() when a timer
@@ -1832,17 +1909,26 @@ int QObject::startTimer(int interval, Qt::TimerType timerType)
void QObject::killTimer(int id)
{
+ killTimer(Qt::TimerId{id});
+}
+
+/*!
+ \since 6.8
+ \overload
+*/
+void QObject::killTimer(Qt::TimerId id)
+{
Q_D(QObject);
if (Q_UNLIKELY(thread() != QThread::currentThread())) {
qWarning("QObject::killTimer: Timers cannot be stopped from another thread");
return;
}
- if (id) {
+ if (id > Qt::TimerId::Invalid) {
int at = d->extraData ? d->extraData->runningTimers.indexOf(id) : -1;
if (at == -1) {
// timer isn't owned by this object
qWarning("QObject::killTimer(): Error: timer id %d is not valid for object %p (%s, %ls), timer has not been killed",
- id,
+ qToUnderlying(id),
this,
metaObject()->className(),
qUtf16Printable(objectName()));
@@ -1858,7 +1944,6 @@ void QObject::killTimer(int id)
}
}
-
/*!
\fn QObject *QObject::parent() const
@@ -1892,18 +1977,19 @@ void QObject::killTimer(int id)
/*!
- \fn template<typename T> T *QObject::findChild(const QString &name, Qt::FindChildOptions options) const
+ \fn template<typename T> T *QObject::findChild(QAnyStringView name, Qt::FindChildOptions options) const
Returns the child of this object that can be cast into type T and
that is called \a name, or \nullptr if there is no such object.
- Omitting the \a name argument causes all object names to be matched.
+ A null \a name argument causes all objects to be matched. An empty,
+ non-null \a name matches only objects whose \l objectName is empty.
The search is performed recursively, unless \a options specifies the
option FindDirectChildrenOnly.
- If there is more than one child matching the search, the most
- direct ancestor is returned. If there are several direct
- ancestors, it is undefined which one will be returned. In that
- case, findChildren() should be used.
+ If there is more than one child matching the search, the most-direct
+ ancestor is returned. If there are several most-direct ancestors, the
+ first child in children() will be returned. In that case, it's better
+ to use findChildren() to get the complete list of all children.
This example returns a child \c{QPushButton} of \c{parentWidget}
named \c{"button1"}, even if the button isn't a direct child of
@@ -1925,11 +2011,32 @@ void QObject::killTimer(int id)
\snippet code/src_corelib_kernel_qobject.cpp 42
+ \note In Qt versions prior to 6.7, this function took \a name as
+ \c{QString}, not \c{QAnyStringView}.
+
\sa findChildren()
*/
/*!
- \fn template<typename T> QList<T> QObject::findChildren(const QString &name, Qt::FindChildOptions options) const
+ \fn template<typename T> T *QObject::findChild(Qt::FindChildOptions options) const
+ \overload
+ \since 6.7
+
+ Returns the child of this object that can be cast into type T, or
+ \nullptr if there is no such object.
+ The search is performed recursively, unless \a options specifies the
+ option FindDirectChildrenOnly.
+
+ If there is more than one child matching the search, the most-direct ancestor
+ is returned. If there are several most-direct ancestors, the first child in
+ children() will be returned. In that case, it's better to use findChildren()
+ to get the complete list of all children.
+
+ \sa findChildren()
+*/
+
+/*!
+ \fn template<typename T> QList<T> QObject::findChildren(QAnyStringView name, Qt::FindChildOptions options) const
Returns all children of this object with the given \a name that can be
cast to type T, or an empty list if there are no such objects.
@@ -1951,6 +2058,9 @@ void QObject::killTimer(int id)
\snippet code/src_corelib_kernel_qobject.cpp 43
+ \note In Qt versions prior to 6.7, this function took \a name as
+ \c{QString}, not \c{QAnyStringView}.
+
\sa findChild()
*/
@@ -1968,7 +2078,7 @@ void QObject::killTimer(int id)
*/
/*!
- \fn QList<T> QObject::findChildren(const QRegularExpression &re, Qt::FindChildOptions options) const
+ \fn template<typename T> QList<T> QObject::findChildren(const QRegularExpression &re, Qt::FindChildOptions options) const
\overload findChildren()
\since 5.0
@@ -2012,46 +2122,26 @@ void QObject::killTimer(int id)
\sa QObject::findChildren()
*/
-static void qt_qFindChildren_with_name(const QObject *parent, const QString &name,
- const QMetaObject &mo, QList<void *> *list,
- Qt::FindChildOptions options)
+static bool matches_objectName_non_null(QObject *obj, QAnyStringView name)
{
- Q_ASSERT(parent);
- Q_ASSERT(list);
- Q_ASSERT(!name.isNull());
- for (QObject *obj : parent->children()) {
- if (mo.cast(obj) && obj->objectName() == name)
- list->append(obj);
- if (options & Qt::FindChildrenRecursively)
- qt_qFindChildren_with_name(obj, name, mo, list, options);
- }
+ if (auto ext = QObjectPrivate::get(obj)->extraData)
+ return ext ->objectName.valueBypassingBindings() == name;
+ return name.isEmpty();
}
/*!
\internal
*/
-void qt_qFindChildren_helper(const QObject *parent, const QString &name,
+void qt_qFindChildren_helper(const QObject *parent, QAnyStringView name,
const QMetaObject &mo, QList<void*> *list, Qt::FindChildOptions options)
{
- if (name.isNull())
- return qt_qFindChildren_helper(parent, mo, list, options);
- else
- return qt_qFindChildren_with_name(parent, name, mo, list, options);
-}
-
-/*!
- \internal
-*/
-void qt_qFindChildren_helper(const QObject *parent, const QMetaObject &mo,
- QList<void*> *list, Qt::FindChildOptions options)
-{
Q_ASSERT(parent);
Q_ASSERT(list);
for (QObject *obj : parent->children()) {
- if (mo.cast(obj))
+ if (mo.cast(obj) && (name.isNull() || matches_objectName_non_null(obj, name)))
list->append(obj);
if (options & Qt::FindChildrenRecursively)
- qt_qFindChildren_helper(obj, mo, list, options);
+ qt_qFindChildren_helper(obj, name, mo, list, options);
}
}
@@ -2078,13 +2168,13 @@ void qt_qFindChildren_helper(const QObject *parent, const QRegularExpression &re
/*!
\internal
- */
-QObject *qt_qFindChild_helper(const QObject *parent, const QString &name, const QMetaObject &mo, Qt::FindChildOptions options)
+*/
+QObject *qt_qFindChild_helper(const QObject *parent, QAnyStringView name, const QMetaObject &mo, Qt::FindChildOptions options)
{
Q_ASSERT(parent);
for (QObject *obj : parent->children()) {
- if (mo.cast(obj) && (name.isNull() || obj->objectName() == name))
- return obj;
+ if (mo.cast(obj) && (name.isNull() || matches_objectName_non_null(obj, name)))
+ return obj;
}
if (options & Qt::FindChildrenRecursively) {
for (QObject *child : parent->children()) {
@@ -2114,7 +2204,7 @@ void QObjectPrivate::deleteChildren()
// delete children objects
// don't use qDeleteAll as the destructor of the child might
// delete siblings
- for (int i = 0; i < children.count(); ++i) {
+ for (int i = 0; i < children.size(); ++i) {
currentChildBeingDeleted = children.at(i);
children[i] = nullptr;
delete currentChildBeingDeleted;
@@ -2167,10 +2257,18 @@ void QObjectPrivate::setParent_helper(QObject *o)
}
}
}
+
+ if (receiveParentEvents) {
+ Q_ASSERT(!isWidget); // Handled in QWidget
+ QEvent e(QEvent::ParentAboutToChange);
+ QCoreApplication::sendEvent(q, &e);
+ }
+
parent = o;
+
if (parent) {
// object hierarchies are constrained to a single thread
- if (threadData != parent->d_func()->threadData) {
+ if (threadData.loadRelaxed() != parent->d_func()->threadData.loadRelaxed()) {
qWarning("QObject::setParent: Cannot set parent, new parent is in a different thread");
parent = nullptr;
return;
@@ -2183,6 +2281,12 @@ void QObjectPrivate::setParent_helper(QObject *o)
}
}
}
+
+ if (receiveParentEvents) {
+ Q_ASSERT(!isWidget); // Handled in QWidget
+ QEvent e(QEvent::ParentChange);
+ QCoreApplication::sendEvent(q, &e);
+ }
}
/*!
@@ -2201,6 +2305,9 @@ void QObjectPrivate::setParent_helper(QObject *o)
If multiple event filters are installed on a single object, the
filter that was installed last is activated first.
+ If \a filterObj has already been installed for this object,
+ this function moves it so it acts as if it was installed last.
+
Here's a \c KeyPressEater class that eats the key presses of its
monitored objects:
@@ -2232,17 +2339,16 @@ void QObject::installEventFilter(QObject *obj)
Q_D(QObject);
if (!obj)
return;
- if (d->threadData != obj->d_func()->threadData) {
+ if (d->threadData.loadRelaxed() != obj->d_func()->threadData.loadRelaxed()) {
qWarning("QObject::installEventFilter(): Cannot filter events for objects in a different thread.");
return;
}
- if (!d->extraData)
- d->extraData = new QObjectPrivate::ExtraData(d);
+ d->ensureExtraData();
- // clean up unused items in the list
- d->extraData->eventFilters.removeAll((QObject *)nullptr);
- d->extraData->eventFilters.removeAll(obj);
+ // clean up unused items in the list along the way:
+ auto isNullOrEquals = [](auto obj) { return [obj](const auto &p) { return !p || p == obj; }; };
+ d->extraData->eventFilters.removeIf(isNullOrEquals(obj));
d->extraData->eventFilters.prepend(obj);
}
@@ -2263,9 +2369,11 @@ void QObject::removeEventFilter(QObject *obj)
{
Q_D(QObject);
if (d->extraData) {
- for (int i = 0; i < d->extraData->eventFilters.count(); ++i) {
- if (d->extraData->eventFilters.at(i) == obj)
- d->extraData->eventFilters[i] = nullptr;
+ for (auto &filter : d->extraData->eventFilters) {
+ if (filter == obj) {
+ filter = nullptr;
+ break;
+ }
}
}
}
@@ -2294,7 +2402,7 @@ void QObject::removeEventFilter(QObject *obj)
QCoreApplication::exec()), the object will be deleted once the
event loop is started. If deleteLater() is called after the main event loop
has stopped, the object will not be deleted.
- Since Qt 4.8, if deleteLater() is called on an object that lives in a
+ If deleteLater() is called on an object that lives in a
thread with no running event loop, the object will be destroyed when the
thread finishes.
@@ -2305,15 +2413,85 @@ void QObject::removeEventFilter(QObject *obj)
event loop was still running: the Qt event loop will delete those objects
as soon as the new nested event loop starts.
- \note It is safe to call this function more than once; when the
- first deferred deletion event is delivered, any pending events for the
- object are removed from the event queue.
+ In situations where Qt is not driving the event dispatcher via e.g.
+ QCoreApplication::exec() or QEventLoop::exec(), deferred deletes
+ will not be processed automatically. To ensure deferred deletion in
+ this scenario, the following workaround can be used:
+
+ \code
+ const auto *eventDispatcher = QThread::currentThread()->eventDispatcher();
+ QObject::connect(eventDispatcher, &QAbstractEventDispatcher::aboutToBlock,
+ QThread::currentThread(), []{
+ if (QThread::currentThread()->loopLevel() == 0)
+ QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete);
+ }
+ );
+ \endcode
\sa destroyed(), QPointer
*/
void QObject::deleteLater()
{
- QCoreApplication::postEvent(this, new QDeferredDeleteEvent());
+#ifdef QT_DEBUG
+ if (qApp == this)
+ qWarning("You are deferring the delete of QCoreApplication, this may not work as expected.");
+#endif
+
+
+ // De-bounce QDeferredDeleteEvents. Use the post event list mutex
+ // to guard access to deleteLaterCalled, so we don't need a separate
+ // mutex in QObjectData.
+ auto eventListLocker = QCoreApplicationPrivate::lockThreadPostEventList(this);
+ if (!eventListLocker.threadData)
+ return;
+
+ // FIXME: The deleteLaterCalled flag is part of a bit field,
+ // so we likely have data races here, even with the mutex above,
+ // as long as we're not guarding every access to the bit field.
+
+ Q_D(QObject);
+ if (d->deleteLaterCalled) {
+ qCDebug(lcDeleteLater) << "Skipping deleteLater for already deferred object" << this;
+ return;
+ }
+
+ d->deleteLaterCalled = true;
+
+ int loopLevel = 0;
+ int scopeLevel = 0;
+
+ auto *objectThreadData = eventListLocker.threadData;
+ if (objectThreadData == QThreadData::current()) {
+ // Remember the current running eventloop for deleteLater
+ // calls in the object's own thread.
+
+ // Events sent by non-Qt event handlers (such as glib) may not
+ // have the scopeLevel set correctly. The scope level makes sure that
+ // code like this:
+ // foo->deleteLater();
+ // qApp->processEvents(); // without passing QEvent::DeferredDelete
+ // will not cause "foo" to be deleted before returning to the event loop.
+
+ loopLevel = objectThreadData->loopLevel;
+ scopeLevel = objectThreadData->scopeLevel;
+
+ // If the scope level is 0 while loopLevel != 0, we are called from a
+ // non-conformant code path, and our best guess is that the scope level
+ // should be 1. (Loop level 0 is special: it means that no event loops
+ // are running.)
+ if (scopeLevel == 0 && loopLevel != 0) {
+ qCDebug(lcDeleteLater) << "Delete later called with scope level 0"
+ << "but loop level is > 0. Assuming scope is 1";
+ scopeLevel = 1;
+ }
+ }
+
+ qCDebug(lcDeleteLater) << "Posting deferred delete for" << this
+ << "with loop level" << loopLevel << "and scope level" << scopeLevel;
+
+ eventListLocker.unlock();
+ QCoreApplication::postEvent(this,
+ new QDeferredDeleteEvent(loopLevel, scopeLevel));
}
/*!
@@ -2326,13 +2504,12 @@ void QObject::deleteLater()
translated string is available.
Example:
- \snippet ../widgets/mainwindows/sdi/mainwindow.cpp implicit tr context
+ \snippet ../widgets/itemviews/spreadsheet/spreadsheet.cpp implicit tr context
\dots
If the same \a sourceText is used in different roles within the
same context, an additional identifying string may be passed in
- \a disambiguation (\nullptr by default). In Qt 4.4 and earlier, this was
- the preferred way to pass comments to translators.
+ \a disambiguation (\nullptr by default).
Example:
@@ -2341,8 +2518,8 @@ void QObject::deleteLater()
See \l{Writing Source Code for Translation} for a detailed description of
Qt's translation mechanisms in general, and the
- \l{Writing Source Code for Translation#Disambiguation}{Disambiguation}
- section for information on disambiguation.
+ \l{Writing Source Code for Translation#Disambiguate Identical Text}
+ {Disambiguate Identical Text} section for information on disambiguation.
\warning This method is reentrant only if all translators are
installed \e before calling this method. Installing or removing
@@ -2356,11 +2533,27 @@ void QObject::deleteLater()
Signals and slots
*****************************************************************************/
+namespace {
+// This class provides (per-thread) storage for qFlagLocation()
+class FlaggedDebugSignatures
+{
+ uint idx = 0;
+ std::array<const char *, 2> locations = {}; // one for the SIGNAL, one for the SLOT
+
+public:
+ void store(const char* method) noexcept
+ { locations[idx++ % locations.size()] = method; }
+
+ bool contains(const char *method) const noexcept
+ { return std::find(locations.begin(), locations.end(), method) != locations.end(); }
+};
+
+Q_CONSTINIT static thread_local FlaggedDebugSignatures flaggedSignatures = {};
+} // unnamed namespace
+
const char *qFlagLocation(const char *method)
{
- QThreadData *currentThreadData = QThreadData::current(false);
- if (currentThreadData != nullptr)
- currentThreadData->flaggedSignatures.store(method);
+ flaggedSignatures.store(method);
return method;
}
@@ -2372,7 +2565,7 @@ static int extract_code(const char *member)
static const char *extract_location(const char *member)
{
- if (QThreadData::current()->flaggedSignatures.contains(member)) {
+ if (flaggedSignatures.contains(member)) {
// signature includes location information after the first null-terminator
const char *location = member + qstrlen(member) + 1;
if (*location != '\0')
@@ -2409,6 +2602,7 @@ static bool check_method_code(int code, const QObject *object, const char *metho
return true;
}
+Q_DECL_COLD_FUNCTION
static void err_method_notfound(const QObject *object,
const char *method, const char *func)
{
@@ -2426,6 +2620,7 @@ static void err_method_notfound(const QObject *object,
object->metaObject()->className(), method + 1, loc ? " in " : "", loc ? loc : "");
}
+Q_DECL_COLD_FUNCTION
static void err_info_about_objects(const char *func, const QObject *sender, const QObject *receiver)
{
QString a = sender ? sender->objectName() : QString();
@@ -2462,7 +2657,7 @@ QObject *QObject::sender() const
{
Q_D(const QObject);
- QBasicMutexLocker locker(signalSlotLock(this));
+ QMutexLocker locker(signalSlotLock(this));
QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
if (!cd || !cd->currentSender)
return nullptr;
@@ -2504,7 +2699,7 @@ int QObject::senderSignalIndex() const
{
Q_D(const QObject);
- QBasicMutexLocker locker(signalSlotLock(this));
+ QMutexLocker locker(signalSlotLock(this));
QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
if (!cd || !cd->currentSender)
return -1;
@@ -2563,13 +2758,13 @@ int QObject::receivers(const char *signal) const
if (!d->isSignalConnected(signal_index))
return receivers;
- if (d->declarativeData && QAbstractDeclarativeData::receivers) {
+ if (!d->isDeletingChildren && d->declarativeData && QAbstractDeclarativeData::receivers) {
receivers += QAbstractDeclarativeData::receivers(d->declarativeData, this,
signal_index);
}
+ QMutexLocker locker(signalSlotLock(this));
QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
- QBasicMutexLocker locker(signalSlotLock(this));
if (cd && signal_index < cd->signalVectorCount()) {
const QObjectPrivate::Connection *c = cd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
while (c) {
@@ -2591,13 +2786,15 @@ int QObject::receivers(const char *signal) const
\snippet code/src_corelib_kernel_qobject.cpp 49
- As the code snippet above illustrates, you can use this function
- to avoid emitting a signal that nobody listens to.
+ As the code snippet above illustrates, you can use this function to avoid
+ expensive initialization or emitting a signal that nobody listens to.
+ However, in a multithreaded application, connections might change after
+ this function returns and before the signal gets emitted.
\warning This function violates the object-oriented principle of
- modularity. However, it might be useful when you need to perform
- expensive initialization only if something is connected to a
- signal.
+ modularity. In particular, this function must not be called from an
+ override of connectNotify() or disconnectNotify(), as those might get
+ called from any thread.
*/
bool QObject::isSignalConnected(const QMetaMethod &signal) const
{
@@ -2614,7 +2811,7 @@ bool QObject::isSignalConnected(const QMetaMethod &signal) const
signalIndex += QMetaObjectPrivate::signalOffset(signal.mobj);
- QBasicMutexLocker locker(signalSlotLock(this));
+ QMutexLocker locker(signalSlotLock(this));
return d->isSignalConnected(signalIndex, true);
}
@@ -3271,8 +3468,13 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal,
signal.
\warning This function is called from the thread which performs the
- connection, which may be a different thread from the thread in
- which this object lives.
+ connection, which may be a different thread from the thread in which
+ this object lives. This function may also be called with a QObject internal
+ mutex locked. It is therefore not allowed to re-enter any QObject
+ functions, including isSignalConnected(), from your reimplementation. If
+ you lock a mutex in your reimplementation, make sure that you don't call
+ QObject functions with that mutex held in other places or it will result in
+ a deadlock.
\sa connect(), disconnectNotify()
*/
@@ -3301,12 +3503,12 @@ void QObject::connectNotify(const QMetaMethod &signal)
expensive resources.
\warning This function is called from the thread which performs the
- disconnection, which may be a different thread from the thread in
- which this object lives. This function may also be called with a QObject
- internal mutex locked. It is therefore not allowed to re-enter any
- of any QObject functions from your reimplementation and if you lock
- a mutex in your reimplementation, make sure that you don't call QObject
- functions with that mutex held in other places or it will result in
+ disconnection, which may be a different thread from the thread in which
+ this object lives. This function may also be called with a QObject internal
+ mutex locked. It is therefore not allowed to re-enter any QObject
+ functions, including isSignalConnected(), from your reimplementation. If
+ you lock a mutex in your reimplementation, make sure that you don't call
+ QObject functions with that mutex held in other places or it will result in
a deadlock.
\sa disconnect(), connectNotify()
@@ -3410,7 +3612,7 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
c->sender = s;
c->signal_index = signal_index;
c->receiver.storeRelaxed(r);
- QThreadData *td = r->d_func()->threadData;
+ QThreadData *td = r->d_func()->threadData.loadAcquire();
td->ref();
c->receiverThreadData.storeRelaxed(td);
c->method_relative = method_index;
@@ -3515,7 +3717,7 @@ bool QMetaObjectPrivate::disconnect(const QObject *sender,
QObject *s = const_cast<QObject *>(sender);
QBasicMutex *senderMutex = signalSlotLock(sender);
- QBasicMutexLocker locker(senderMutex);
+ QMutexLocker locker(senderMutex);
QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.loadRelaxed();
if (!scd)
@@ -3622,7 +3824,7 @@ void QMetaObject::connectSlotsByName(QObject *o)
// ...we check each object in our list, ...
bool foundIt = false;
- for (int j = 0; j < list.count(); ++j) {
+ for (int j = 0; j < list.size(); ++j) {
const QObject *co = list.at(j);
const QByteArray coName = co->objectName().toLatin1();
@@ -3696,7 +3898,7 @@ struct SlotObjectGuard {
SlotObjectGuard() = default;
// move would be fine, but we do not need it currently
Q_DISABLE_COPY_MOVE(SlotObjectGuard)
- explicit SlotObjectGuard(QtPrivate::QSlotObjectBase *slotObject)
+ Q_NODISCARD_CTOR explicit SlotObjectGuard(QtPrivate::QSlotObjectBase *slotObject)
: m_slotObject(slotObject)
{
if (m_slotObject)
@@ -3704,17 +3906,14 @@ struct SlotObjectGuard {
}
QtPrivate::QSlotObjectBase const *operator->() const
- { return m_slotObject; }
+ { return m_slotObject.get(); }
QtPrivate::QSlotObjectBase *operator->()
- { return m_slotObject; }
+ { return m_slotObject.get(); }
- ~SlotObjectGuard() {
- if (m_slotObject)
- m_slotObject->destroyIfLastRef();
- }
+ ~SlotObjectGuard() = default;
private:
- QtPrivate::QSlotObjectBase *m_slotObject = nullptr;
+ QtPrivate::SlotObjUniquePtr m_slotObject;
};
/*!
@@ -3742,7 +3941,7 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect
while (argumentTypes[nargs - 1])
++nargs;
- QBasicMutexLocker locker(signalSlotLock(c->receiver.loadRelaxed()));
+ QMutexLocker locker(signalSlotLock(c->receiver.loadRelaxed()));
QObject *receiver = c->receiver.loadRelaxed();
if (!receiver) {
// the connection has been disconnected before we got the lock
@@ -3770,7 +3969,7 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect
args[n] = types[n].create(argv[n]);
}
- if (c->isSingleShot && !QObjectPrivate::disconnect(c)) {
+ if (c->isSingleShot && !QObjectPrivate::removeConnection(c)) {
delete ev;
return;
}
@@ -3823,8 +4022,8 @@ void doActivate(QObject *sender, int signal_index, void **argv)
bool senderDeleted = false;
{
- Q_ASSERT(sp->connections.loadAcquire());
- QObjectPrivate::ConnectionDataPointer connections(sp->connections.loadRelaxed());
+ Q_ASSERT(sp->connections.loadRelaxed());
+ QObjectPrivate::ConnectionDataPointer connections(sp->connections.loadAcquire());
QObjectPrivate::SignalVector *signalVector = connections->signalVector.loadRelaxed();
const QObjectPrivate::ConnectionList *list;
@@ -3878,12 +4077,12 @@ void doActivate(QObject *sender, int signal_index, void **argv)
receiver->metaObject()->className(), receiver);
}
- if (c->isSingleShot && !QObjectPrivate::disconnect(c))
+ if (c->isSingleShot && !QObjectPrivate::removeConnection(c))
continue;
QSemaphore semaphore;
{
- QBasicMutexLocker locker(signalSlotLock(receiver));
+ QMutexLocker locker(signalSlotLock(receiver));
if (!c->isSingleShot && !c->receiver.loadAcquire())
continue;
QMetaCallEvent *ev = c->isSlotObject ?
@@ -3897,10 +4096,12 @@ void doActivate(QObject *sender, int signal_index, void **argv)
#endif
}
- if (c->isSingleShot && !QObjectPrivate::disconnect(c))
+ if (c->isSingleShot && !QObjectPrivate::removeConnection(c))
continue;
- QObjectPrivate::Sender senderData(receiverInSameThread ? receiver : nullptr, sender, signal_index);
+ QObjectPrivate::Sender senderData(
+ receiverInSameThread ? receiver : nullptr, sender, signal_index,
+ receiverInSameThread ? QObjectPrivate::get(receiver)->connections.loadAcquire() : nullptr);
if (c->isSlotObject) {
SlotObjectGuard obj{c->slotObj};
@@ -3949,7 +4150,7 @@ void doActivate(QObject *sender, int signal_index, void **argv)
senderDeleted = true;
}
if (!senderDeleted) {
- sp->connections.loadRelaxed()->cleanOrphanedConnections(sender);
+ sp->connections.loadAcquire()->cleanOrphanedConnections(sender);
if (callbacks_enabled && signal_spy_set->signal_end_callback != nullptr)
signal_spy_set->signal_end_callback(sender, signal_index);
@@ -4026,9 +4227,9 @@ int QObjectPrivate::signalIndex(const char *signalName,
Properties
*****************************************************************************/
-#ifndef QT_NO_PROPERTIES
-
/*!
+ \fn bool QObject::setProperty(const char *name, const QVariant &value)
+
Sets the value of the object's \a name property to \a value.
If the property is defined in the class using Q_PROPERTY then
@@ -4049,17 +4250,24 @@ int QObjectPrivate::signalIndex(const char *signalName,
\sa property(), metaObject(), dynamicPropertyNames(), QMetaProperty::write()
*/
-bool QObject::setProperty(const char *name, const QVariant &value)
+
+/*!
+ \fn bool QObject::setProperty(const char *name, QVariant &&value)
+ \since 6.6
+ \overload setProperty
+*/
+
+bool QObject::doSetProperty(const char *name, const QVariant *lvalue, QVariant *rvalue)
{
Q_D(QObject);
+ const auto &value =*lvalue;
const QMetaObject *meta = metaObject();
if (!name || !meta)
return false;
int id = meta->indexOfProperty(name);
if (id < 0) {
- if (!d->extraData)
- d->extraData = new QObjectPrivate::ExtraData(d);
+ d->ensureExtraData();
const int idx = d->extraData->propertyNames.indexOf(name);
@@ -4071,12 +4279,18 @@ bool QObject::setProperty(const char *name, const QVariant &value)
} else {
if (idx == -1) {
d->extraData->propertyNames.append(name);
- d->extraData->propertyValues.append(value);
+ if (rvalue)
+ d->extraData->propertyValues.append(std::move(*rvalue));
+ else
+ d->extraData->propertyValues.append(*lvalue);
} else {
if (value.userType() == d->extraData->propertyValues.at(idx).userType()
&& value == d->extraData->propertyValues.at(idx))
return false;
- d->extraData->propertyValues[idx] = value;
+ if (rvalue)
+ d->extraData->propertyValues[idx] = std::move(*rvalue);
+ else
+ d->extraData->propertyValues[idx] = *lvalue;
}
}
@@ -4091,7 +4305,7 @@ bool QObject::setProperty(const char *name, const QVariant &value)
qWarning("%s::setProperty: Property \"%s\" invalid,"
" read-only or does not exist", metaObject()->className(), name);
#endif
- return p.write(this, value);
+ return rvalue ? p.write(this, std::move(*rvalue)) : p.write(this, *lvalue);
}
/*!
@@ -4141,42 +4355,24 @@ QList<QByteArray> QObject::dynamicPropertyNames() const
return QList<QByteArray>();
}
-#endif // QT_NO_PROPERTIES
-
-
/*****************************************************************************
QObject debugging output routines.
*****************************************************************************/
+std::string QObjectPrivate::flagsForDumping() const
+{
+ return {};
+}
+
static void dumpRecursive(int level, const QObject *object)
{
if (object) {
- QByteArray buf;
- buf.fill(' ', level / 2 * 8);
- if (level % 2)
- buf += " ";
- QString name = object->objectName();
- QString flags = QLatin1String("");
-#if 0
- if (qApp->focusWidget() == object)
- flags += 'F';
- if (object->isWidgetType()) {
- QWidget * w = (QWidget *)object;
- if (w->isVisible()) {
- QString t("<%1,%2,%3,%4>");
- flags += t.arg(w->x()).arg(w->y()).arg(w->width()).arg(w->height());
- } else {
- flags += 'I';
- }
- }
-#endif
- qDebug("%s%s::%s %s", (const char*)buf, object->metaObject()->className(), name.toLocal8Bit().data(),
- flags.toLatin1().data());
- QObjectList children = object->children();
- if (!children.isEmpty()) {
- for (int i = 0; i < children.size(); ++i)
- dumpRecursive(level+1, children.at(i));
- }
+ const int indent = level * 4;
+ qDebug("%*s%s::%ls %s", indent, "", object->metaObject()->className(),
+ qUtf16Printable(object->objectName()),
+ QObjectPrivate::get(object)->flagsForDumping().c_str());
+ for (auto child : object->children())
+ dumpRecursive(level + 1, child);
}
}
@@ -4209,7 +4405,7 @@ void QObject::dumpObjectInfo() const
objectName().isEmpty() ? "unnamed" : objectName().toLocal8Bit().data());
Q_D(const QObject);
- QBasicMutexLocker locker(signalSlotLock(this));
+ QMutexLocker locker(signalSlotLock(this));
// first, look for connections where this object is the sender
qDebug(" SIGNALS OUT");
@@ -4271,15 +4467,23 @@ void QObject::dumpObjectInfo() const
#ifndef QT_NO_DEBUG_STREAM
+void QObjectPrivate::writeToDebugStream(QDebug &dbg) const
+{
+ Q_Q(const QObject);
+ dbg.nospace() << q->metaObject()->className() << '(' << (const void *)q;
+ if (!q->objectName().isEmpty())
+ dbg << ", name = " << q->objectName();
+ dbg << ')';
+}
+
QDebug operator<<(QDebug dbg, const QObject *o)
{
QDebugStateSaver saver(dbg);
if (!o)
return dbg << "QObject(0x0)";
- dbg.nospace() << o->metaObject()->className() << '(' << (const void *)o;
- if (!o->objectName().isEmpty())
- dbg << ", name = " << o->objectName();
- dbg << ')';
+
+ const QObjectPrivate *d = QObjectPrivate::get(o);
+ d->writeToDebugStream(dbg);
return dbg;
}
#endif
@@ -4289,19 +4493,22 @@ QDebug operator<<(QDebug dbg, const QObject *o)
\relates QObject
This macro associates extra information to the class, which is available
- using QObject::metaObject(). Qt makes only limited use of this feature in
- \l{Qt D-Bus} and \l{Qt QML} modules.
-
- The extra information takes the form of a \a Name string and a \a Value
- literal string.
+ using QObject::metaObject(). The extra information takes the form of a
+ \a Name string and a \a Value literal string.
Example:
\snippet code/src_corelib_kernel_qobject.cpp 35
+ Qt makes use of the macro in \l{Qt D-Bus} and \l{Qt Qml} modules.
+ For instance, when defining \l{QML Object Types} in C++, you can
+ designate a property as the \e default one:
+
+ \snippet code/doc_src_properties.cpp 7
+
\sa QMetaObject::classInfo()
\sa {Using Qt D-Bus Adaptors}
- \sa {Extending QML}
+ \sa {Defining QML Types from C++}
*/
/*!
@@ -4311,15 +4518,6 @@ QDebug operator<<(QDebug dbg, const QObject *o)
This macro tells Qt which interfaces the class implements. This
is used when implementing plugins.
- Example:
-
- \snippet ../widgets/tools/plugandpaint/plugins/basictools/basictoolsplugin.h 1
- \dots
- \snippet ../widgets/tools/plugandpaint/plugins/basictools/basictoolsplugin.h 3
-
- See the \l{tools/plugandpaint/plugins/basictools}{Plug & Paint
- Basic Tools} example for details.
-
\sa Q_DECLARE_INTERFACE(), Q_PLUGIN_METADATA(), {How to Create Qt Plugins}
*/
@@ -4396,8 +4594,8 @@ QDebug operator<<(QDebug dbg, const QObject *o)
\since 5.5
This macro registers an enum type with the meta-object system.
- It must be placed after the enum declaration in a class that has the Q_OBJECT or the
- Q_GADGET macro. For namespaces use \l Q_ENUM_NS() instead.
+ It must be placed after the enum declaration in a class that has the Q_OBJECT,
+ Q_GADGET or Q_GADGET_EXPORT macro. For namespaces use \l Q_ENUM_NS() instead.
For example:
@@ -4515,9 +4713,16 @@ QDebug operator<<(QDebug dbg, const QObject *o)
\snippet signalsandslots/signalsandslots.h 3
\note This macro requires the class to be a subclass of QObject. Use
- Q_GADGET instead of Q_OBJECT to enable the meta object system's support
+ Q_GADGET or Q_GADGET_EXPORT instead of Q_OBJECT to enable the meta object system's support
for enums in a class that is not a QObject subclass.
+//! [qobject-macros-private-access-specifier]
+ \note This macro expansion ends with a \c private: access specifier, which makes member
+ declarations immediately after the macro private, too. If you want add public (or protected)
+ members immediately after the macro, you need to use a \c public: (or \c protected:)
+ access specifier.
+//! [qobject-macros-private-access-specifier]
+
\sa {Meta-Object System}, {Signals and Slots}, {Qt's Property System}
*/
@@ -4535,7 +4740,38 @@ QDebug operator<<(QDebug dbg, const QObject *o)
Q_GADGET makes a class member, \c{staticMetaObject}, available.
\c{staticMetaObject} is of type QMetaObject and provides access to the
- enums declared with Q_ENUMS.
+ enums declared with Q_ENUM.
+
+ \include qobject.cpp qobject-macros-private-access-specifier
+
+ \sa Q_GADGET_EXPORT
+*/
+
+/*!
+ \macro Q_GADGET_EXPORT(EXPORT_MACRO)
+ \relates QObject
+ \since 6.3
+
+ The Q_GADGET_EXPORT macro works exactly like the Q_GADGET macro.
+ However, the \c{staticMetaObject} variable that is made available (see
+ Q_GADGET) is declared with the supplied \a EXPORT_MACRO qualifier. This is
+ useful if the object needs to be exported from a dynamic library, but the
+ enclosing class as a whole should not be (e.g. because it consists of mostly
+ inline functions).
+
+ For example:
+
+ \code
+ class Point {
+ Q_GADGET_EXPORT(EXPORT_MACRO)
+ Q_PROPERTY(int x MEMBER x)
+ Q_PROPERTY(int y MEMBER y)
+ ~~~
+ \endcode
+
+ \include qobject.cpp qobject-macros-private-access-specifier
+
+ \sa Q_GADGET, {Creating Shared Libraries}
*/
/*!
@@ -4765,17 +5001,34 @@ QDebug operator<<(QDebug dbg, const QObject *o)
*/
/*!
+ \macro QT_NO_CONTEXTLESS_CONNECT
+ \relates QObject
+ \since 6.7
+
+ Defining this macro will disable the overload of QObject::connect() that
+ connects a signal to a functor, without also specifying a QObject
+ as a receiver/context object (that is, the 3-arguments overload
+ of QObject::connect()).
+
+ Using the context-less overload is error prone, because it is easy
+ to connect to functors that depend on some local state of the
+ receiving end. If such local state gets destroyed, the connection
+ does not get automatically disconnected.
+
+ Moreover, such connections are always direct connections, which may
+ cause issues in multithreaded scenarios (for instance, if the
+ signal is emitted from another thread).
+
+ \sa QObject::connect, Qt::ConnectionType
+*/
+
+/*!
\typedef QObjectList
\relates QObject
Synonym for QList<QObject *>.
*/
-void qDeleteInEventHandler(QObject *o)
-{
- delete o;
-}
-
/*!
\fn template<typename PointerToMemberFunction> QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method, Qt::ConnectionType type)
\overload connect()
@@ -4869,6 +5122,11 @@ void qDeleteInEventHandler(QObject *o)
However, you should take care that any objects used within the functor
are still alive when the signal is emitted.
+ For this reason, it is recommended to use the overload of connect()
+ that also takes a QObject as a receiver/context. It is possible
+ to disable the usage of the context-less overload by defining the
+ \c{QT_NO_CONTEXTLESS_CONNECT} macro.
+
Overloaded functions can be resolved with help of \l qOverload.
*/
@@ -4934,13 +5192,12 @@ void qDeleteInEventHandler(QObject *o)
*/
QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signal,
const QObject *receiver, void **slot,
- QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
+ QtPrivate::QSlotObjectBase *slotObjRaw, Qt::ConnectionType type,
const int *types, const QMetaObject *senderMetaObject)
{
+ QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw);
if (!signal) {
qCWarning(lcConnect, "QObject::connect: invalid nullptr parameter");
- if (slotObj)
- slotObj->destroyIfLastRef();
return QMetaObject::Connection();
}
@@ -4953,11 +5210,10 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa
}
if (!senderMetaObject) {
qCWarning(lcConnect, "QObject::connect: signal not found in %s", sender->metaObject()->className());
- slotObj->destroyIfLastRef();
return QMetaObject::Connection(nullptr);
}
signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject);
- return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj, type, types, senderMetaObject);
+ return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj.release(), type, types, senderMetaObject);
}
static void connectWarning(const QObject *sender,
@@ -4982,14 +5238,10 @@ static void connectWarning(const QObject *sender,
*/
QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int signal_index,
const QObject *receiver, void **slot,
- QtPrivate::QSlotObjectBase *slotObj, int type,
+ QtPrivate::QSlotObjectBase *slotObjRaw, int type,
const int *types, const QMetaObject *senderMetaObject)
{
- auto connectFailureGuard = qScopeGuard([&]()
- {
- if (slotObj)
- slotObj->destroyIfLastRef();
- });
+ QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw);
if (!sender || !receiver || !slotObj || !senderMetaObject) {
connectWarning(sender, senderMetaObject, receiver, "invalid nullptr parameter");
@@ -5001,24 +5253,20 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s
return QMetaObject::Connection();
}
- connectFailureGuard.dismiss();
-
QObject *s = const_cast<QObject *>(sender);
QObject *r = const_cast<QObject *>(receiver);
QOrderedMutexLocker locker(signalSlotLock(sender),
signalSlotLock(receiver));
- if (type & Qt::UniqueConnection && slot && QObjectPrivate::get(s)->connections.loadRelaxed()) {
+ if (type & Qt::UniqueConnection && slot) {
QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.loadRelaxed();
- if (connections->signalVectorCount() > signal_index) {
+ if (connections && connections->signalVectorCount() > signal_index) {
const QObjectPrivate::Connection *c2 = connections->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
while (c2) {
- if (c2->receiver.loadRelaxed() == receiver && c2->isSlotObject && c2->slotObj->compare(slot)) {
- slotObj->destroyIfLastRef();
+ if (c2->receiver.loadRelaxed() == receiver && c2->isSlotObject && c2->slotObj->compare(slot))
return QMetaObject::Connection();
- }
c2 = c2->nextConnectionList.loadRelaxed();
}
}
@@ -5034,13 +5282,13 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s
std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection};
c->sender = s;
c->signal_index = signal_index;
- QThreadData *td = r->d_func()->threadData;
+ QThreadData *td = r->d_func()->threadData.loadAcquire();
td->ref();
c->receiverThreadData.storeRelaxed(td);
c->receiver.storeRelaxed(r);
- c->slotObj = slotObj;
c->connectionType = type;
c->isSlotObject = true;
+ c->slotObj = slotObj.release();
if (types) {
c->argumentTypes.storeRelaxed(types);
c->ownArgumentTypes = false;
@@ -5071,7 +5319,7 @@ bool QObject::disconnect(const QMetaObject::Connection &connection)
QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(connection.d_ptr);
if (!c)
return false;
- const bool disconnected = QObjectPrivate::disconnect(c);
+ const bool disconnected = QObjectPrivate::removeConnection(c);
const_cast<QMetaObject::Connection &>(connection).d_ptr = nullptr;
c->deref(); // has been removed from the QMetaObject::Connection object
return disconnected;
@@ -5189,20 +5437,19 @@ QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signa
*/
QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signal_index,
const QObject *receiver,
- QtPrivate::QSlotObjectBase *slotObj,
+ QtPrivate::QSlotObjectBase *slotObjRaw,
Qt::ConnectionType type)
{
+ QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw);
if (!sender) {
qCWarning(lcConnect, "QObject::connect: invalid nullptr parameter");
- if (slotObj)
- slotObj->destroyIfLastRef();
return QMetaObject::Connection();
}
const QMetaObject *senderMetaObject = sender->metaObject();
signal_index = methodIndexToSignalIndex(&senderMetaObject, signal_index);
- return QObjectPrivate::connectImpl(sender, signal_index, receiver, /*slot*/ nullptr, slotObj,
- type, /*types*/ nullptr, senderMetaObject);
+ return connectImpl(sender, signal_index, receiver, /*slot*/ nullptr, slotObj.release(),
+ type, /*types*/ nullptr, senderMetaObject);
}
/*!
@@ -5244,7 +5491,7 @@ bool QObjectPrivate::disconnect(const QObject *sender, int signal_index, const Q
\internal
\threadsafe
*/
-bool QObjectPrivate::disconnect(QObjectPrivate::Connection *c)
+inline bool QObjectPrivate::removeConnection(QObjectPrivate::Connection *c)
{
if (!c)
return false;
@@ -5283,6 +5530,33 @@ bool QObjectPrivate::disconnect(QObjectPrivate::Connection *c)
return true;
}
+/*!
+ \internal
+
+ Used by QPropertyAdaptorSlotObject to get an existing instance for a property, if available
+ */
+QtPrivate::QPropertyAdaptorSlotObject *
+QObjectPrivate::getPropertyAdaptorSlotObject(const QMetaProperty &property)
+{
+ if (auto conns = connections.loadAcquire()) {
+ Q_Q(QObject);
+ const QMetaObject *metaObject = q->metaObject();
+ int signal_index = methodIndexToSignalIndex(&metaObject, property.notifySignalIndex());
+ if (signal_index >= conns->signalVectorCount())
+ return nullptr;
+ const auto &connectionList = conns->connectionsForSignal(signal_index);
+ for (auto c = connectionList.first.loadRelaxed(); c;
+ c = c->nextConnectionList.loadRelaxed()) {
+ if (c->isSlotObject) {
+ if (auto p = QtPrivate::QPropertyAdaptorSlotObject::cast(c->slotObj,
+ property.propertyIndex()))
+ return p;
+ }
+ }
+ }
+ return nullptr;
+}
+
/*! \class QMetaObject::Connection
\inmodule QtCore
Represents a handle to a signal-slot (or signal-functor) connection.