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.cpp1791
1 files changed, 1102 insertions, 689 deletions
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index 615bc63f1d..708b10a75e 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -1,52 +1,18 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 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,29 +22,41 @@
#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>
#include <qtcore_tracepoints_p.h>
#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(lcConnections, "qt.core.qmetaobject.connectslotsbyname")
+Q_LOGGING_CATEGORY(lcConnectSlotsByName, "qt.core.qmetaobject.connectslotsbyname")
+Q_LOGGING_CATEGORY(lcConnect, "qt.core.qobject.connect")
Q_CORE_EXPORT QBasicAtomicPointer<QSignalSpyCallbackSet> qt_signal_spy_callback_set = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
@@ -95,33 +73,37 @@ QAbstractDynamicMetaObject::~QAbstractDynamicMetaObject()
{
}
-static int *queuedConnectionTypes(const QList<QByteArray> &typeNames)
+static int *queuedConnectionTypes(const QMetaMethod &method)
{
- int *types = new int [typeNames.count() + 1];
- Q_CHECK_PTR(types);
- for (int i = 0; i < typeNames.count(); ++i) {
- const QByteArray typeName = typeNames.at(i);
- if (typeName.endsWith('*'))
- types[i] = QMetaType::VoidStar;
+ const auto parameterCount = method.parameterCount();
+ int *typeIds = new int[parameterCount + 1];
+ Q_CHECK_PTR(typeIds);
+ for (int i = 0; i < parameterCount; ++i) {
+ const QMetaType metaType = method.parameterMetaType(i);
+ if (metaType.flags() & QMetaType::IsPointer)
+ typeIds[i] = QMetaType::VoidStar;
else
- types[i] = QMetaType::type(typeName);
-
- if (!types[i]) {
- qWarning("QObject::connect: Cannot queue arguments of type '%s'\n"
- "(Make sure '%s' is registered using qRegisterMetaType().)",
- typeName.constData(), typeName.constData());
- delete [] types;
+ 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,
+ "QObject::connect: Cannot queue arguments of type '%s'\n"
+ "(Make sure '%s' is registered using qRegisterMetaType().)",
+ typeName.constData(), typeName.constData());
+ delete[] typeIds;
return nullptr;
}
}
- types[typeNames.count()] = 0;
+ typeIds[parameterCount] = 0;
- return types;
+ return typeIds;
}
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())
@@ -129,21 +111,22 @@ static int *queuedConnectionTypes(const QArgumentType *argumentTypes, int argc)
else if (type.name().endsWith('*'))
types[i] = QMetaType::VoidStar;
else
- types[i] = QMetaType::type(type.name());
+ types[i] = QMetaType::fromName(type.name()).id();
if (!types[i]) {
- qWarning("QObject::connect: Cannot queue arguments of type '%s'\n"
- "(Make sure '%s' is registered using qRegisterMetaType().)",
- type.name().constData(), type.name().constData());
+ qCWarning(lcConnect,
+ "QObject::connect: Cannot queue arguments of type '%s'\n"
+ "(Make sure '%s' is registered using qRegisterMetaType().)",
+ type.name().constData(), type.name().constData());
return nullptr;
}
}
types[argc] = 0;
- return types.take();
+ return types.release();
}
-static QBasicMutex _q_ObjectMutexPool[131];
+Q_CONSTINIT static QBasicMutex _q_ObjectMutexPool[131];
/**
* \internal
@@ -154,16 +137,7 @@ static inline QBasicMutex *signalSlotLock(const QObject *o)
return &_q_ObjectMutexPool[uint(quintptr(o)) % sizeof(_q_ObjectMutexPool)/sizeof(QBasicMutex)];
}
-#if QT_VERSION < 0x60000
-extern "C" Q_CORE_EXPORT void qt_addObject(QObject *)
-{}
-
-extern "C" Q_CORE_EXPORT void qt_removeObject(QObject *)
-{}
-#endif
-
void (*QAbstractDeclarativeData::destroyed)(QAbstractDeclarativeData *, QObject *) = nullptr;
-void (*QAbstractDeclarativeData::parentChanged)(QAbstractDeclarativeData *, QObject *, QObject *) = nullptr;
void (*QAbstractDeclarativeData::signalEmitted)(QAbstractDeclarativeData *, QObject *, int, void **) = nullptr;
int (*QAbstractDeclarativeData::receivers)(QAbstractDeclarativeData *, const QObject *, int) = nullptr;
bool (*QAbstractDeclarativeData::isSignalConnected)(QAbstractDeclarativeData *, const QObject *, int) = nullptr;
@@ -201,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()
@@ -213,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");
}
@@ -225,7 +203,8 @@ QObjectPrivate::~QObjectPrivate()
thisThreadData->deref();
- if (metaObject) metaObject->objectDestroyed(q_ptr);
+ if (metaObject)
+ metaObject->objectDestroyed(q_ptr);
delete extraData;
}
@@ -248,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()) {
@@ -289,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);
}
/*!
@@ -312,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();
@@ -382,10 +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
- c->nextInOrphanList = orphaned.loadRelaxed();
- orphaned.storeRelaxed(c);
+ 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 {
+ c->nextInOrphanList = o;
+ } while (!orphaned.compare_exchange_strong(o, TaggedSignalVector(c), std::memory_order_release));
#ifndef QT_NO_DEBUG
found = false;
@@ -400,28 +364,39 @@ void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection
}
-void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sender)
+void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy)
{
- ConnectionOrSignalVector *c = nullptr;
+ QBasicMutex *senderMutex = signalSlotLock(sender);
+ TaggedSignalVector c = nullptr;
{
- QBasicMutexLocker l(signalSlotLock(sender));
+ std::unique_lock<QBasicMutex> lock(*senderMutex, std::defer_lock_t{});
+ if (lockPolicy == NeedToLock)
+ lock.lock();
if (ref.loadAcquire() > 1)
return;
// 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.loadRelaxed();
- orphaned.storeRelaxed(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
+ if (lockPolicy == AlreadyLockedAndTemporarilyReleasingLock) {
+ senderMutex->unlock();
+ deleteOrphaned(c);
+ senderMutex->lock();
+ } else {
+ deleteOrphaned(c);
+ }
}
- deleteOrphaned(c);
}
-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 {
@@ -447,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();
@@ -470,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();
@@ -487,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
*/
@@ -506,7 +488,7 @@ inline void QMetaCallEvent::allocArgs()
if (!d.nargs_)
return;
- constexpr size_t each = sizeof(void*) + sizeof(int);
+ constexpr size_t each = sizeof(void*) + sizeof(QMetaType);
void *const memory = d.nargs_ * each > sizeof(prealloc_) ?
calloc(d.nargs_, each) : prealloc_;
@@ -540,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_)
@@ -550,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.
*/
@@ -574,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_)
@@ -584,20 +581,34 @@ 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()
{
if (d.nargs_) {
- int *typeIDs = types();
+ QMetaType *t = types();
for (int i = 0; i < d.nargs_; ++i) {
- if (typeIDs[i] && d.args_[i])
- QMetaType::destroy(typeIDs[i], d.args_[i]);
+ if (t[i].isValid() && d.args_[i])
+ t[i].destroy(d.args_[i]);
}
- if (reinterpret_cast<void*>(d.args_) != reinterpret_cast<void*>(prealloc_))
+ if (reinterpret_cast<void *>(d.args_) != reinterpret_cast<void *>(prealloc_))
free(d.args_);
}
- if (d.slotObj_)
- d.slotObj_->destroyIfLastRef();
}
/*!
@@ -615,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().
@@ -702,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.
@@ -742,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
@@ -825,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.
@@ -917,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);
@@ -931,14 +969,16 @@ QObject::QObject(QObjectPrivate &dd, QObject *parent)
QT_RETHROW;
}
}
-#if QT_VERSION < 0x60000
- qt_addObject(this);
-#endif
if (Q_UNLIKELY(qtHookData[QHooks::AddQObject]))
reinterpret_cast<QHooks::AddQObjectCallback>(qtHookData[QHooks::AddQObject])(this);
Q_TRACE(QObject_ctor, this);
}
+void QObjectPrivate::clearBindingStorage()
+{
+ bindingStorage.clear();
+}
+
/*!
Destroys the object, deleting all its child objects.
@@ -953,8 +993,8 @@ QObject::QObject(QObjectPrivate &dd, QObject *parent)
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
@@ -969,6 +1009,20 @@ 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();
+
QtSharedPointer::ExternalRefCountData *sharedRefcount = d->sharedRefcount.loadRelaxed();
if (sharedRefcount) {
if (sharedRefcount->strongref.loadRelaxed() > 0) {
@@ -982,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();
@@ -997,7 +1051,7 @@ QObject::~QObject()
}
QBasicMutex *signalSlotMutex = signalSlotLock(this);
- QBasicMutexLocker locker(signalSlotMutex);
+ QMutexLocker locker(signalSlotMutex);
// disconnect all receivers
int receiverCount = cd->signalVectorCount();
@@ -1009,7 +1063,7 @@ QObject::~QObject()
QBasicMutex *m = signalSlotLock(c->receiver.loadRelaxed());
bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
- if (c->receiver.loadAcquire()) {
+ if (c == connectionList.first.loadAcquire() && c->receiver.loadAcquire()) {
cd->removeConnection(c);
Q_ASSERT(connectionList.first.loadRelaxed() != c);
}
@@ -1047,14 +1101,29 @@ QObject::~QObject()
}
senderData->removeConnection(node);
+ /*
+ When we unlock, another thread has the chance to delete/modify sender data.
+ Thus we need to call cleanOrphanedConnections before unlocking. We use the
+ variant of the function which assumes that the lock is already held to avoid
+ a deadlock.
+ We need to hold m, the sender lock. Considering that we might execute arbitrary user
+ code, we should already release the signalSlotMutex here – unless they are the same.
+ */
+ const bool locksAreTheSame = signalSlotMutex == m;
+ if (!locksAreTheSame)
+ locker.unlock();
+ senderData->cleanOrphanedConnections(
+ sender,
+ QObjectPrivate::ConnectionData::AlreadyLockedAndTemporarilyReleasingLock
+ );
if (needToUnlock)
m->unlock();
- if (slotObj) {
+ if (locksAreTheSame) // otherwise already unlocked
locker.unlock();
+ if (slotObj)
slotObj->destroyIfLastRef();
- locker.relock();
- }
+ locker.relock();
}
// invalidate all connections on the object and make sure
@@ -1068,9 +1137,6 @@ QObject::~QObject()
if (!d->children.isEmpty())
d->deleteChildren();
-#if QT_VERSION < 0x60000
- qt_removeObject(this);
-#endif
if (Q_UNLIKELY(qtHookData[QHooks::RemoveQObject]))
reinterpret_cast<QHooks::RemoveQObjectCallback>(qtHookData[QHooks::RemoveQObject])(this);
@@ -1080,12 +1146,12 @@ QObject::~QObject()
d->setParent_helper(nullptr);
}
-QObjectPrivate::Connection::~Connection()
+inline QObjectPrivate::Connection::~Connection()
{
if (ownArgumentTypes) {
const int *v = argumentTypes.loadRelaxed();
if (v != &DIRECT_CONNECTION_ONLY)
- delete [] v;
+ delete[] v;
}
if (isSlotObject)
slotObj->destroyIfLastRef();
@@ -1163,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.
@@ -1210,24 +1275,63 @@ 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
+ dd->extraData = new QObjectPrivate::ExtraData(dd);
+ }
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);
+
+ d->ensureExtraData();
+
+ d->extraData->objectName.removeBindingUnlessInWrapper();
+
+ 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);
- if (!d->extraData)
- d->extraData = new QObjectPrivate::ExtraData;
- if (d->extraData->objectName != name) {
- d->extraData->objectName = name;
- emit objectNameChanged(d->extraData->objectName, QPrivateSignal());
+ 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);
+
+ d->ensureExtraData();
+
+ return QBindable<QString>(&d->extraData->objectName);
+}
+
/*! \fn void QObject::objectNameChanged(const QString &objectName)
This signal is emitted after the object's name has been changed. The new object name is passed as \a objectName.
@@ -1254,6 +1358,17 @@ void QObject::setObjectName(const QString &name)
*/
/*!
+ \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.
@@ -1275,28 +1390,31 @@ bool QObject::event(QEvent *e)
{
switch (e->type()) {
case QEvent::Timer:
- timerEvent((QTimerEvent*)e);
+ timerEvent((QTimerEvent *)e);
break;
case QEvent::ChildAdded:
case QEvent::ChildPolished:
case QEvent::ChildRemoved:
- childEvent((QChildEvent*)e);
+ childEvent((QChildEvent *)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;
@@ -1307,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;
@@ -1334,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()
*/
@@ -1475,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
@@ -1514,42 +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 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
@@ -1568,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()
@@ -1581,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) {
@@ -1610,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();
@@ -1645,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
@@ -1669,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
@@ -1688,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;
}
@@ -1721,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;
+
+ 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
@@ -1778,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()));
@@ -1804,7 +1944,6 @@ void QObject::killTimer(int id)
}
}
-
/*!
\fn QObject *QObject::parent() const
@@ -1838,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
@@ -1871,15 +2011,37 @@ 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.
- 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 one
+ only those whose objectName is empty.
The search is performed recursively, unless \a options specifies the
option FindDirectChildrenOnly.
@@ -1896,11 +2058,27 @@ 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()
+*/
+
+/*!
+ \fn template<typename T> QList<T> QObject::findChildren(Qt::FindChildOptions options) const
+ \overload
+ \since 6.3
+
+ Returns all children of this object that can be cast to type T, or
+ an empty list if there are no such objects.
+ The search is performed recursively, unless \a options specifies the
+ option FindDirectChildrenOnly.
+
\sa findChild()
*/
/*!
- \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
@@ -1916,7 +2094,7 @@ void QObject::killTimer(int id)
\fn template<typename T> T qFindChild(const QObject *obj, const QString &name)
\relates QObject
\overload qFindChildren()
- \obsolete
+ \deprecated
This function is equivalent to
\a{obj}->\l{QObject::findChild()}{findChild}<T>(\a name).
@@ -1932,7 +2110,7 @@ void QObject::killTimer(int id)
\fn template<typename T> QList<T> qFindChildren(const QObject *obj, const QString &name)
\relates QObject
\overload qFindChildren()
- \obsolete
+ \deprecated
This function is equivalent to
\a{obj}->\l{QObject::findChildren()}{findChildren}<T>(\a name).
@@ -1944,22 +2122,24 @@ void QObject::killTimer(int id)
\sa QObject::findChildren()
*/
+static bool matches_objectName_non_null(QObject *obj, QAnyStringView name)
+{
+ 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 (!parent || !list)
- return;
- const QObjectList &children = parent->children();
- QObject *obj;
- for (int i = 0; i < children.size(); ++i) {
- obj = children.at(i);
- if (mo.cast(obj)) {
- if (name.isNull() || obj->objectName() == name)
- list->append(obj);
- }
+ Q_ASSERT(parent);
+ Q_ASSERT(list);
+ for (QObject *obj : parent->children()) {
+ if (mo.cast(obj) && (name.isNull() || matches_objectName_non_null(obj, name)))
+ list->append(obj);
if (options & Qt::FindChildrenRecursively)
qt_qFindChildren_helper(obj, name, mo, list, options);
}
@@ -1972,12 +2152,9 @@ void qt_qFindChildren_helper(const QObject *parent, const QString &name,
void qt_qFindChildren_helper(const QObject *parent, const QRegularExpression &re,
const QMetaObject &mo, QList<void*> *list, Qt::FindChildOptions options)
{
- if (!parent || !list)
- return;
- const QObjectList &children = parent->children();
- QObject *obj;
- for (int i = 0; i < children.size(); ++i) {
- obj = children.at(i);
+ Q_ASSERT(parent);
+ Q_ASSERT(list);
+ for (QObject *obj : parent->children()) {
if (mo.cast(obj)) {
QRegularExpressionMatch m = re.match(obj->objectName());
if (m.hasMatch())
@@ -1991,23 +2168,17 @@ 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)
{
- if (!parent)
- return nullptr;
- const QObjectList &children = parent->children();
- QObject *obj;
- int i;
- for (i = 0; i < children.size(); ++i) {
- obj = children.at(i);
- if (mo.cast(obj) && (name.isNull() || obj->objectName() == name))
- return obj;
+ Q_ASSERT(parent);
+ for (QObject *obj : parent->children()) {
+ if (mo.cast(obj) && (name.isNull() || matches_objectName_non_null(obj, name)))
+ return obj;
}
if (options & Qt::FindChildrenRecursively) {
- for (i = 0; i < children.size(); ++i) {
- obj = qt_qFindChild_helper(children.at(i), name, mo, options);
- if (obj)
+ for (QObject *child : parent->children()) {
+ if (QObject *obj = qt_qFindChild_helper(child, name, mo, options))
return obj;
}
}
@@ -2033,9 +2204,9 @@ 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] = 0;
+ children[i] = nullptr;
delete currentChildBeingDeleted;
}
children.clear();
@@ -2076,7 +2247,7 @@ void QObjectPrivate::setParent_helper(QObject *o)
if (index < 0) {
// we're probably recursing into setParent() from a ChildRemoved event, don't do anything
} else if (parentD->isDeletingChildren) {
- parentD->children[index] = 0;
+ parentD->children[index] = nullptr;
} else {
parentD->children.removeAt(index);
if (sendChildEvents && parentD->receiveChildEvents) {
@@ -2086,24 +2257,36 @@ 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;
}
parent->d_func()->children.append(q);
- if(sendChildEvents && parent->d_func()->receiveChildEvents) {
+ if (sendChildEvents && parent->d_func()->receiveChildEvents) {
if (!isWidget) {
QChildEvent e(QEvent::ChildAdded, q);
QCoreApplication::sendEvent(parent, &e);
}
}
}
- if (!wasDeleted && !isDeletingChildren && declarativeData && QAbstractDeclarativeData::parentChanged)
- QAbstractDeclarativeData::parentChanged(declarativeData, q, o);
+
+ if (receiveParentEvents) {
+ Q_ASSERT(!isWidget); // Handled in QWidget
+ QEvent e(QEvent::ParentChange);
+ QCoreApplication::sendEvent(q, &e);
+ }
}
/*!
@@ -2122,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:
@@ -2153,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->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);
}
@@ -2184,14 +2369,15 @@ 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;
+ }
}
}
}
-
/*!
\fn void QObject::destroyed(QObject *obj)
@@ -2216,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.
@@ -2227,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.
- \b{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));
}
/*!
@@ -2248,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:
@@ -2263,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
@@ -2274,43 +2529,31 @@ void QObject::deleteLater()
\sa QCoreApplication::translate(), {Internationalization with Qt}
*/
-/*!
- \fn QString QObject::trUtf8(const char *sourceText, const char *disambiguation, int n)
- \reentrant
- \obsolete
-
- Returns a translated version of \a sourceText, or
- QString::fromUtf8(\a sourceText) if there is no appropriate
- version. It is otherwise identical to tr(\a sourceText, \a
- disambiguation, \a n).
-
- \warning This method is reentrant only if all translators are
- installed \e before calling this method. Installing or removing
- translators while performing translations is not supported. Doing
- so will probably result in crashes or other undesirable behavior.
-
- \warning For portability reasons, we recommend that you use
- escape sequences for specifying non-ASCII characters in string
- literals to trUtf8(). For example:
-
- \snippet code/src_corelib_kernel_qobject.cpp 20
-
- \sa tr(), QCoreApplication::translate(), {Internationalization with Qt}
-*/
-
-
-
-
/*****************************************************************************
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;
}
@@ -2320,9 +2563,9 @@ static int extract_code(const char *member)
return (((int)(*member) - '0') & 0x3);
}
-static const char * extract_location(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')
@@ -2337,27 +2580,29 @@ static bool check_signal_macro(const QObject *sender, const char *signal,
int sigcode = extract_code(signal);
if (sigcode != QSIGNAL_CODE) {
if (sigcode == QSLOT_CODE)
- qWarning("QObject::%s: Attempt to %s non-signal %s::%s",
- func, op, sender->metaObject()->className(), signal+1);
+ qCWarning(lcConnect, "QObject::%s: Attempt to %s non-signal %s::%s", func, op,
+ sender->metaObject()->className(), signal + 1);
else
- qWarning("QObject::%s: Use the SIGNAL macro to %s %s::%s",
- func, op, sender->metaObject()->className(), signal);
+ qCWarning(lcConnect, "QObject::%s: Use the SIGNAL macro to %s %s::%s", func, op,
+ sender->metaObject()->className(), signal);
return false;
}
return true;
}
-static bool check_method_code(int code, const QObject *object,
- const char *method, const char *func)
+static bool check_method_code(int code, const QObject *object, const char *method, const char *func)
{
if (code != QSLOT_CODE && code != QSIGNAL_CODE) {
- qWarning("QObject::%s: Use the SLOT or SIGNAL macro to "
- "%s %s::%s", func, func, object->metaObject()->className(), method);
+ qCWarning(lcConnect,
+ "QObject::%s: Use the SLOT or SIGNAL macro to "
+ "%s %s::%s",
+ func, func, object->metaObject()->className(), method);
return false;
}
return true;
}
+Q_DECL_COLD_FUNCTION
static void err_method_notfound(const QObject *object,
const char *method, const char *func)
{
@@ -2367,28 +2612,23 @@ static void err_method_notfound(const QObject *object,
case QSIGNAL_CODE: type = "signal"; break;
}
const char *loc = extract_location(method);
- if (strchr(method,')') == nullptr) // common typing mistake
- qWarning("QObject::%s: Parentheses expected, %s %s::%s%s%s",
- func, type, object->metaObject()->className(), method+1,
- loc ? " in ": "", loc ? loc : "");
+ if (strchr(method, ')') == nullptr) // common typing mistake
+ qCWarning(lcConnect, "QObject::%s: Parentheses expected, %s %s::%s%s%s", func, type,
+ object->metaObject()->className(), method + 1, loc ? " in " : "", loc ? loc : "");
else
- qWarning("QObject::%s: No such %s %s::%s%s%s",
- func, type, object->metaObject()->className(), method+1,
- loc ? " in ": "", loc ? loc : "");
-
+ qCWarning(lcConnect, "QObject::%s: No such %s %s::%s%s%s", func, type,
+ object->metaObject()->className(), method + 1, loc ? " in " : "", loc ? loc : "");
}
-
-static void err_info_about_objects(const char * func,
- const QObject * sender,
- const QObject * receiver)
+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();
QString b = receiver ? receiver->objectName() : QString();
if (!a.isEmpty())
- qWarning("QObject::%s: (sender name: '%s')", func, a.toLocal8Bit().data());
+ qCWarning(lcConnect, "QObject::%s: (sender name: '%s')", func, a.toLocal8Bit().data());
if (!b.isEmpty())
- qWarning("QObject::%s: (receiver name: '%s')", func, b.toLocal8Bit().data());
+ qCWarning(lcConnect, "QObject::%s: (receiver name: '%s')", func, b.toLocal8Bit().data());
}
/*!
@@ -2417,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;
@@ -2459,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;
@@ -2510,7 +2750,7 @@ int QObject::receivers(const char *signal) const
int signal_index = d->signalIndex(signal);
if (signal_index < 0) {
#ifndef QT_NO_DEBUG
- err_method_notfound(this, signal-1, "receivers");
+ err_method_notfound(this, signal - 1, "receivers");
#endif
return 0;
}
@@ -2518,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) {
@@ -2546,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
{
@@ -2569,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);
}
@@ -2631,13 +2873,13 @@ static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaM
{
if (signal.attributes() & QMetaMethod::Compatibility) {
if (!(method.attributes() & QMetaMethod::Compatibility))
- qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)",
- sender->className(), signal.methodSignature().constData());
- } else if ((method.attributes() & QMetaMethod::Compatibility) &&
- method.methodType() == QMetaMethod::Signal) {
- qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)",
- sender->className(), signal.methodSignature().constData(),
- receiver->className(), method.methodSignature().constData());
+ qCWarning(lcConnect, "QObject::connect: Connecting from COMPAT signal (%s::%s)",
+ sender->className(), signal.methodSignature().constData());
+ } else if ((method.attributes() & QMetaMethod::Compatibility)
+ && method.methodType() == QMetaMethod::Signal) {
+ qCWarning(lcConnect, "QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)",
+ sender->className(), signal.methodSignature().constData(), receiver->className(),
+ method.methodSignature().constData());
}
}
#endif
@@ -2714,15 +2956,15 @@ static inline void check_and_warn_compat(const QMetaObject *sender, const QMetaM
{Differences between String-Based and Functor-Based Connections}
*/
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,
- const QObject *receiver, const char *method,
- Qt::ConnectionType type)
+ const QObject *receiver, const char *method,
+ Qt::ConnectionType type)
{
if (sender == nullptr || receiver == nullptr || signal == nullptr || method == nullptr) {
- qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
- sender ? sender->metaObject()->className() : "(nullptr)",
- (signal && *signal) ? signal+1 : "(nullptr)",
- receiver ? receiver->metaObject()->className() : "(nullptr)",
- (method && *method) ? method+1 : "(nullptr)");
+ qCWarning(lcConnect, "QObject::connect: Cannot connect %s::%s to %s::%s",
+ sender ? sender->metaObject()->className() : "(nullptr)",
+ (signal && *signal) ? signal + 1 : "(nullptr)",
+ receiver ? receiver->metaObject()->className() : "(nullptr)",
+ (method && *method) ? method + 1 : "(nullptr)");
return QMetaObject::Connection(nullptr);
}
QByteArray tmp_signal_name;
@@ -2731,7 +2973,7 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const char *sign
return QMetaObject::Connection(nullptr);
const QMetaObject *smeta = sender->metaObject();
const char *signal_arg = signal;
- ++signal; //skip code
+ ++signal; // skip code
QArgumentTypeArray signalTypes;
Q_ASSERT(QMetaObjectPrivate::get(smeta)->revision >= 7);
QByteArray signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
@@ -2808,10 +3050,11 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const char *sign
if (!QMetaObjectPrivate::checkConnectArgs(signalTypes.size(), signalTypes.constData(),
methodTypes.size(), methodTypes.constData())) {
- qWarning("QObject::connect: Incompatible sender/receiver arguments"
- "\n %s::%s --> %s::%s",
- sender->metaObject()->className(), signal,
- receiver->metaObject()->className(), method);
+ qCWarning(lcConnect,
+ "QObject::connect: Incompatible sender/receiver arguments"
+ "\n %s::%s --> %s::%s",
+ sender->metaObject()->className(), signal, receiver->metaObject()->className(),
+ method);
return QMetaObject::Connection(nullptr);
}
@@ -2859,11 +3102,11 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMetho
|| receiver == nullptr
|| signal.methodType() != QMetaMethod::Signal
|| method.methodType() == QMetaMethod::Constructor) {
- qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
- sender ? sender->metaObject()->className() : "(nullptr)",
- signal.methodSignature().constData(),
- receiver ? receiver->metaObject()->className() : "(nullptr)",
- method.methodSignature().constData() );
+ qCWarning(lcConnect, "QObject::connect: Cannot connect %s::%s to %s::%s",
+ sender ? sender->metaObject()->className() : "(nullptr)",
+ signal.methodSignature().constData(),
+ receiver ? receiver->metaObject()->className() : "(nullptr)",
+ method.methodSignature().constData());
return QMetaObject::Connection(nullptr);
}
@@ -2878,27 +3121,28 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMetho
const QMetaObject *smeta = sender->metaObject();
const QMetaObject *rmeta = receiver->metaObject();
if (signal_index == -1) {
- qWarning("QObject::connect: Can't find signal %s on instance of class %s",
- signal.methodSignature().constData(), smeta->className());
+ qCWarning(lcConnect, "QObject::connect: Can't find signal %s on instance of class %s",
+ signal.methodSignature().constData(), smeta->className());
return QMetaObject::Connection(nullptr);
}
if (method_index == -1) {
- qWarning("QObject::connect: Can't find method %s on instance of class %s",
- method.methodSignature().constData(), rmeta->className());
+ qCWarning(lcConnect, "QObject::connect: Can't find method %s on instance of class %s",
+ method.methodSignature().constData(), rmeta->className());
return QMetaObject::Connection(nullptr);
}
- if (!QMetaObject::checkConnectArgs(signal.methodSignature().constData(), method.methodSignature().constData())) {
- qWarning("QObject::connect: Incompatible sender/receiver arguments"
- "\n %s::%s --> %s::%s",
- smeta->className(), signal.methodSignature().constData(),
- rmeta->className(), method.methodSignature().constData());
+ if (!QMetaObject::checkConnectArgs(signal.methodSignature().constData(),
+ method.methodSignature().constData())) {
+ qCWarning(lcConnect,
+ "QObject::connect: Incompatible sender/receiver arguments"
+ "\n %s::%s --> %s::%s",
+ smeta->className(), signal.methodSignature().constData(), rmeta->className(),
+ method.methodSignature().constData());
return QMetaObject::Connection(nullptr);
}
int *types = nullptr;
- if ((type == Qt::QueuedConnection)
- && !(types = queuedConnectionTypes(signal.parameterTypes())))
+ if ((type == Qt::QueuedConnection) && !(types = queuedConnectionTypes(signal)))
return QMetaObject::Connection(nullptr);
#ifndef QT_NO_DEBUG
@@ -2983,13 +3227,15 @@ QMetaObject::Connection QObject::connect(const QObject *sender, const QMetaMetho
if \a receiver is left out, so you cannot disconnect a
specifically-named slot on all objects.
+ \include includes/qobject.qdocinc disconnect-all
+
\sa connect()
*/
bool QObject::disconnect(const QObject *sender, const char *signal,
const QObject *receiver, const char *method)
{
if (sender == nullptr || (receiver == nullptr && method != nullptr)) {
- qWarning("QObject::disconnect: Unexpected nullptr parameter");
+ qCWarning(lcConnect, "QObject::disconnect: Unexpected nullptr parameter");
return false;
}
@@ -3086,7 +3332,7 @@ bool QObject::disconnect(const QObject *sender, const char *signal,
}
if (res) {
if (!signal)
- const_cast<QObject*>(sender)->disconnectNotify(QMetaMethod());
+ const_cast<QObject *>(sender)->disconnectNotify(QMetaMethod());
}
return res;
}
@@ -3118,39 +3364,33 @@ bool QObject::disconnect(const QObject *sender, const char *signal,
In the same way \nullptr can be used for \a receiver in the meaning "any receiving object".
In this case method should also be QMetaMethod(). \a sender parameter should be never \nullptr.
+ \include includes/qobject.qdocinc disconnect-all
+
\sa disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
*/
bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal,
const QObject *receiver, const QMetaMethod &method)
{
if (sender == nullptr || (receiver == nullptr && method.mobj != nullptr)) {
- qWarning("QObject::disconnect: Unexpected nullptr parameter");
+ qCWarning(lcConnect, "QObject::disconnect: Unexpected nullptr parameter");
return false;
}
if (signal.mobj) {
- if(signal.methodType() != QMetaMethod::Signal) {
- qWarning("QObject::%s: Attempt to %s non-signal %s::%s",
+ if (signal.methodType() != QMetaMethod::Signal) {
+ qCWarning(lcConnect, "QObject::%s: Attempt to %s non-signal %s::%s",
"disconnect","unbind",
sender->metaObject()->className(), signal.methodSignature().constData());
return false;
}
}
if (method.mobj) {
- if(method.methodType() == QMetaMethod::Constructor) {
- qWarning("QObject::disconnect: cannot use constructor as argument %s::%s",
- receiver->metaObject()->className(), method.methodSignature().constData());
+ if (method.methodType() == QMetaMethod::Constructor) {
+ qCWarning(lcConnect, "QObject::disconnect: cannot use constructor as argument %s::%s",
+ receiver->metaObject()->className(), method.methodSignature().constData());
return false;
}
}
- // Reconstructing SIGNAL() macro result for signal.methodSignature() string
- QByteArray signalSignature;
- if (signal.mobj) {
- signalSignature.reserve(signal.methodSignature().size()+1);
- signalSignature.append((char)(QSIGNAL_CODE + '0'));
- signalSignature.append(signal.methodSignature());
- }
-
int signal_index;
int method_index;
{
@@ -3161,14 +3401,14 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal,
// If we are here sender is not nullptr. If signal is not nullptr while signal_index
// is -1 then this signal is not a member of sender.
if (signal.mobj && signal_index == -1) {
- qWarning("QObject::disconnect: signal %s not found on class %s",
- signal.methodSignature().constData(), sender->metaObject()->className());
+ qCWarning(lcConnect, "QObject::disconnect: signal %s not found on class %s",
+ signal.methodSignature().constData(), sender->metaObject()->className());
return false;
}
// If this condition is true then method is not a member of receiver.
if (receiver && method.mobj && method_index == -1) {
- qWarning("QObject::disconnect: method %s not found on class %s",
- method.methodSignature().constData(), receiver->metaObject()->className());
+ qCWarning(lcConnect, "QObject::disconnect: method %s not found on class %s",
+ method.methodSignature().constData(), receiver->metaObject()->className());
return false;
}
@@ -3180,7 +3420,7 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal,
// QMetaObjectPrivate::disconnect() doesn't call disconnectNotify()
// per connection in this case. Call it once now, with an invalid
// QMetaMethod as argument, as documented.
- const_cast<QObject*>(sender)->disconnectNotify(signal);
+ const_cast<QObject *>(sender)->disconnectNotify(signal);
}
return true;
}
@@ -3195,6 +3435,8 @@ bool QObject::disconnect(const QObject *sender, const QMetaMethod &signal,
A signal-slot connection is removed when either of the objects
involved are destroyed.
+
+ \include includes/qobject.qdocinc disconnect-all
*/
/*!
@@ -3226,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()
*/
@@ -3256,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()
@@ -3305,7 +3552,8 @@ static int methodIndexToSignalIndex(const QMetaObject **base, int signal_index)
from the sender to the receiver's slot
*/
QMetaObject::Connection QMetaObject::connect(const QObject *sender, int signal_index,
- const QObject *receiver, int method_index, int type, int *types)
+ const QObject *receiver, int method_index, int type,
+ int *types)
{
const QMetaObject *smeta = sender->metaObject();
signal_index = methodIndexToSignalIndex(&smeta, signal_index);
@@ -3351,14 +3599,20 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
c2 = c2->nextConnectionList.loadRelaxed();
}
}
- type &= Qt::UniqueConnection - 1;
}
+ type &= ~Qt::UniqueConnection;
+
+ const bool isSingleShot = type & Qt::SingleShotConnection;
+ type &= ~Qt::SingleShotConnection;
+
+ Q_ASSERT(type >= 0);
+ Q_ASSERT(type <= 3);
std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection};
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;
@@ -3367,6 +3621,7 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
c->isSlotObject = false;
c->argumentTypes.storeRelaxed(types);
c->callFunction = callFunction;
+ c->isSingleShot = isSingleShot;
QObjectPrivate::get(s)->addConnection(signal_index, c.get());
@@ -3462,9 +3717,9 @@ 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();
+ QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.loadRelaxed();
if (!scd)
return false;
@@ -3554,7 +3809,7 @@ void QMetaObject::connectSlotsByName(QObject *o)
const QMetaObject *mo = o->metaObject();
Q_ASSERT(mo);
const QObjectList list = // list of all objects to look for matching signals including...
- o->findChildren<QObject *>(QString()) // all children of 'o'...
+ o->findChildren<QObject *>() // all children of 'o'...
<< o; // and the object 'o' itself
// for each method/slot of o ...
@@ -3569,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();
@@ -3589,7 +3844,7 @@ void QMetaObject::connectSlotsByName(QObject *o)
// chosen 'at random' (order of declaration in source file)
QList<QByteArray> compatibleSignals;
const QMetaObject *smo = co->metaObject();
- int sigLen = qstrlen(signal) - 1; // ignore the trailing ')'
+ int sigLen = int(qstrlen(signal)) - 1; // ignore the trailing ')'
for (int k = QMetaObjectPrivate::absoluteSignalCount(smo)-1; k >= 0; --k) {
const QMetaMethod method = QMetaObjectPrivate::signal(smo, k);
if (!qstrncmp(method.methodSignature().constData(), signal, sigLen)) {
@@ -3599,7 +3854,7 @@ void QMetaObject::connectSlotsByName(QObject *o)
}
}
if (compatibleSignals.size() > 1)
- qWarning() << "QMetaObject::connectSlotsByName: Connecting slot" << slot
+ qCWarning(lcConnectSlotsByName) << "QMetaObject::connectSlotsByName: Connecting slot" << slot
<< "with the first of the following compatible signals:" << compatibleSignals;
}
@@ -3609,7 +3864,7 @@ void QMetaObject::connectSlotsByName(QObject *o)
// we connect it...
if (Connection(QMetaObjectPrivate::connect(co, sigIndex, smeta, o, i))) {
foundIt = true;
- qCDebug(lcConnections, "%s",
+ qCDebug(lcConnectSlotsByName, "%s",
msgConnect(smeta, coName, QMetaObjectPrivate::signal(smeta, sigIndex), o, i).constData());
// ...and stop looking for further objects with the same name.
// Note: the Designer will make sure each object name is unique in the above
@@ -3621,18 +3876,47 @@ void QMetaObject::connectSlotsByName(QObject *o)
if (foundIt) {
// we found our slot, now skip all overloads
while (mo->method(i + 1).attributes() & QMetaMethod::Cloned)
- ++i;
+ ++i;
} else if (!(mo->method(i).attributes() & QMetaMethod::Cloned)) {
// check if the slot has the following signature: "on_..._...(..."
int iParen = slotSignature.indexOf('(');
- int iLastUnderscore = slotSignature.lastIndexOf('_', iParen-1);
+ int iLastUnderscore = slotSignature.lastIndexOf('_', iParen - 1);
if (iLastUnderscore > 3)
- qWarning("QMetaObject::connectSlotsByName: No matching signal for %s", slot);
+ qCWarning(lcConnectSlotsByName,
+ "QMetaObject::connectSlotsByName: No matching signal for %s", slot);
}
}
}
/*!
+ \internal
+ A small RAII helper for QSlotObjectBase.
+ Calls ref on construction and destroyLastRef in its dtor.
+ Allows construction from a nullptr in which case it does nothing.
+ */
+struct SlotObjectGuard {
+ SlotObjectGuard() = default;
+ // move would be fine, but we do not need it currently
+ Q_DISABLE_COPY_MOVE(SlotObjectGuard)
+ Q_NODISCARD_CTOR explicit SlotObjectGuard(QtPrivate::QSlotObjectBase *slotObject)
+ : m_slotObject(slotObject)
+ {
+ if (m_slotObject)
+ m_slotObject->ref();
+ }
+
+ QtPrivate::QSlotObjectBase const *operator->() const
+ { return m_slotObject.get(); }
+
+ QtPrivate::QSlotObjectBase *operator->()
+ { return m_slotObject.get(); }
+
+ ~SlotObjectGuard() = default;
+private:
+ QtPrivate::SlotObjUniquePtr m_slotObject;
+};
+
+/*!
\internal
\a signal must be in the signal index range (see QObjectPrivate::signalIndex()).
@@ -3642,28 +3926,29 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect
const int *argumentTypes = c->argumentTypes.loadRelaxed();
if (!argumentTypes) {
QMetaMethod m = QMetaObjectPrivate::signal(sender->metaObject(), signal);
- argumentTypes = queuedConnectionTypes(m.parameterTypes());
+ argumentTypes = queuedConnectionTypes(m);
if (!argumentTypes) // cannot queue arguments
argumentTypes = &DIRECT_CONNECTION_ONLY;
if (!c->argumentTypes.testAndSetOrdered(nullptr, argumentTypes)) {
if (argumentTypes != &DIRECT_CONNECTION_ONLY)
- delete [] argumentTypes;
+ delete[] argumentTypes;
argumentTypes = c->argumentTypes.loadRelaxed();
}
}
if (argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate
return;
int nargs = 1; // include return type
- while (argumentTypes[nargs-1])
+ while (argumentTypes[nargs - 1])
++nargs;
- QBasicMutexLocker locker(signalSlotLock(c->receiver.loadRelaxed()));
- if (!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
return;
}
- if (c->isSlotObject)
- c->slotObj->ref();
+
+ SlotObjectGuard slotObjectGuard { c->isSlotObject ? c->slotObj : nullptr };
locker.unlock();
QMetaCallEvent *ev = c->isSlotObject ?
@@ -3671,30 +3956,33 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect
new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal, nargs);
void **args = ev->args();
- int *types = ev->types();
+ QMetaType *types = ev->types();
- types[0] = 0; // return type
+ types[0] = QMetaType(); // return type
args[0] = nullptr; // return value
if (nargs > 1) {
for (int n = 1; n < nargs; ++n)
- types[n] = argumentTypes[n-1];
+ types[n] = QMetaType(argumentTypes[n - 1]);
for (int n = 1; n < nargs; ++n)
- args[n] = QMetaType::create(types[n], argv[n]);
+ args[n] = types[n].create(argv[n]);
+ }
+
+ if (c->isSingleShot && !QObjectPrivate::removeConnection(c)) {
+ delete ev;
+ return;
}
locker.relock();
- if (c->isSlotObject)
- c->slotObj->destroyIfLastRef();
- if (!c->receiver.loadRelaxed()) {
+ if (!c->isSingleShot && !c->receiver.loadRelaxed()) {
// the connection has been disconnected while we were unlocked
locker.unlock();
delete ev;
return;
}
- QCoreApplication::postEvent(c->receiver.loadRelaxed(), ev);
+ QCoreApplication::postEvent(receiver, ev);
}
template <bool callbacks_enabled>
@@ -3734,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;
@@ -3788,10 +4076,14 @@ void doActivate(QObject *sender, int signal_index, void **argv)
sender->metaObject()->className(), sender,
receiver->metaObject()->className(), receiver);
}
+
+ if (c->isSingleShot && !QObjectPrivate::removeConnection(c))
+ continue;
+
QSemaphore semaphore;
{
- QBasicMutexLocker locker(signalSlotLock(sender));
- if (!c->receiver.loadAcquire())
+ QMutexLocker locker(signalSlotLock(receiver));
+ if (!c->isSingleShot && !c->receiver.loadAcquire())
continue;
QMetaCallEvent *ev = c->isSlotObject ?
new QMetaCallEvent(c->slotObj, sender, signal_index, argv, &semaphore) :
@@ -3804,20 +4096,18 @@ void doActivate(QObject *sender, int signal_index, void **argv)
#endif
}
- QObjectPrivate::Sender senderData(receiverInSameThread ? receiver : nullptr, sender, signal_index);
+ if (c->isSingleShot && !QObjectPrivate::removeConnection(c))
+ continue;
- if (c->isSlotObject) {
- c->slotObj->ref();
+ QObjectPrivate::Sender senderData(
+ receiverInSameThread ? receiver : nullptr, sender, signal_index,
+ receiverInSameThread ? QObjectPrivate::get(receiver)->connections.loadAcquire() : nullptr);
- struct Deleter {
- void operator()(QtPrivate::QSlotObjectBase *slot) const {
- if (slot) slot->destroyIfLastRef();
- }
- };
- const std::unique_ptr<QtPrivate::QSlotObjectBase, Deleter> obj{c->slotObj};
+ if (c->isSlotObject) {
+ SlotObjectGuard obj{c->slotObj};
{
- Q_TRACE_SCOPE(QMetaObject_activate_slot_functor, obj.get());
+ Q_TRACE_SCOPE(QMetaObject_activate_slot_functor, c->slotObj);
obj->call(receiver, argv);
}
} else if (c->callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {
@@ -3860,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);
@@ -3892,7 +4182,7 @@ void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_i
doActivate<true>(sender, signal_index, argv);
else
doActivate<false>(sender, signal_index, argv);
- }
+}
/*!
\internal
@@ -3937,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
@@ -3960,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 QMetaObject* meta = metaObject();
+ 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->ensureExtraData();
const int idx = d->extraData->propertyNames.indexOf(name);
@@ -3982,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;
}
}
@@ -4002,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);
}
/*!
@@ -4018,7 +4321,7 @@ bool QObject::setProperty(const char *name, const QVariant &value)
QVariant QObject::property(const char *name) const
{
Q_D(const QObject);
- const QMetaObject* meta = metaObject();
+ const QMetaObject *meta = metaObject();
if (!name || !meta)
return QVariant();
@@ -4052,65 +4355,32 @@ 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);
}
}
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
-/*!
- \overload
- \obsolete
-
- Dumps a tree of children to the debug output.
-
- \sa dumpObjectInfo()
-*/
-
-void QObject::dumpObjectTree()
-{
- const_cast<const QObject *>(this)->dumpObjectTree();
-}
-#endif
/*!
Dumps a tree of children to the debug output.
- \note before Qt 5.9, this function was not const.
+ \note Before Qt 5.9, this function was not const.
\sa dumpObjectInfo()
*/
@@ -4120,28 +4390,11 @@ void QObject::dumpObjectTree() const
dumpRecursive(0, this);
}
-#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
-/*!
- \overload
- \obsolete
-
- Dumps information about signal connections, etc. for this object
- to the debug output.
-
- \sa dumpObjectTree()
-*/
-
-void QObject::dumpObjectInfo()
-{
- const_cast<const QObject *>(this)->dumpObjectInfo();
-}
-#endif
-
/*!
Dumps information about signal connections, etc. for this object
to the debug output.
- \note before Qt 5.9, this function was not const.
+ \note Before Qt 5.9, this function was not const.
\sa dumpObjectTree()
*/
@@ -4152,13 +4405,13 @@ 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");
QObjectPrivate::ConnectionData *cd = d->connections.loadRelaxed();
- if (cd && cd->signalVectorCount()) {
+ if (cd && cd->signalVectorCount() > 0) {
QObjectPrivate::SignalVector *signalVector = cd->signalVector.loadRelaxed();
for (int signal_index = 0; signal_index < signalVector->count(); ++signal_index) {
const QObjectPrivate::Connection *c = signalVector->at(signal_index).first.loadRelaxed();
@@ -4214,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
@@ -4232,20 +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
- the \l{Active Qt}, \l{Qt D-Bus} and \l{Qt QML module}{Qt QML}.
-
- 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 QAxFactory
\sa {Using Qt D-Bus Adaptors}
- \sa {Extending QML}
+ \sa {Defining QML Types from C++}
*/
/*!
@@ -4255,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}
*/
@@ -4297,7 +4551,7 @@ QDebug operator<<(QDebug dbg, const QObject *o)
/*!
\macro Q_ENUMS(...)
\relates QObject
- \obsolete
+ \deprecated
In new code, you should prefer the use of the Q_ENUM() macro, which makes the
type available also to the meta type system.
@@ -4317,7 +4571,7 @@ QDebug operator<<(QDebug dbg, const QObject *o)
/*!
\macro Q_FLAGS(...)
\relates QObject
- \obsolete
+ \deprecated
This macro registers one or several \l{QFlags}{flags types} with the
meta-object system. It is typically used in a class definition to declare
@@ -4340,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:
@@ -4459,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}
*/
@@ -4479,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}
*/
/*!
@@ -4709,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()
@@ -4796,9 +5105,8 @@ void qDeleteInEventHandler(QObject *o)
The signal must be a function declared as a signal in the header.
The slot function can be any function or functor that can be connected
to the signal.
- A function can be connected to a given signal if the signal has at
- least as many argument as the slot. A functor can be connected to a signal
- if they have exactly the same number of arguments. There must exist implicit
+ A slot function can be connected to a given signal if the signal has at
+ least as many arguments as the slot function. There must exist implicit
conversion between the types of the corresponding arguments in the
signal and the slot.
@@ -4814,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.
*/
@@ -4836,9 +5149,8 @@ void qDeleteInEventHandler(QObject *o)
The signal must be a function declared as a signal in the header.
The slot function can be any function or functor that can be connected
to the signal.
- A function can be connected to a given signal if the signal has at
- least as many argument as the slot. A functor can be connected to a signal
- if they have exactly the same number of arguments. There must exist implicit
+ A slot function can be connected to a given signal if the signal has at
+ least as many arguments as the slot function. There must exist implicit
conversion between the types of the corresponding arguments in the
signal and the slot.
@@ -4880,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) {
- qWarning("QObject::connect: invalid nullptr parameter");
- if (slotObj)
- slotObj->destroyIfLastRef();
+ qCWarning(lcConnect, "QObject::connect: invalid nullptr parameter");
return QMetaObject::Connection();
}
@@ -4898,12 +5209,24 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa
break;
}
if (!senderMetaObject) {
- qWarning("QObject::connect: signal not found in %s", sender->metaObject()->className());
- slotObj->destroyIfLastRef();
+ qCWarning(lcConnect, "QObject::connect: signal not found in %s", sender->metaObject()->className());
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,
+ const QMetaObject *senderMetaObject,
+ const QObject *receiver,
+ const char *message)
+{
+ const char *senderString = sender ? sender->metaObject()->className()
+ : senderMetaObject ? senderMetaObject->className()
+ : "Unknown";
+ const char *receiverString = receiver ? receiver->metaObject()->className()
+ : "Unknown";
+ qCWarning(lcConnect, "QObject::connect(%s, %s): %s", senderString, receiverString, message);
}
/*!
@@ -4915,18 +5238,18 @@ QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signa
*/
QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int signal_index,
const QObject *receiver, void **slot,
- QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
+ QtPrivate::QSlotObjectBase *slotObjRaw, int type,
const int *types, const QMetaObject *senderMetaObject)
{
+ QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw);
+
if (!sender || !receiver || !slotObj || !senderMetaObject) {
- const char *senderString = sender ? sender->metaObject()->className()
- : senderMetaObject ? senderMetaObject->className()
- : "Unknown";
- const char *receiverString = receiver ? receiver->metaObject()->className()
- : "Unknown";
- qWarning("QObject::connect(%s, %s): invalid nullptr parameter", senderString, receiverString);
- if (slotObj)
- slotObj->destroyIfLastRef();
+ connectWarning(sender, senderMetaObject, receiver, "invalid nullptr parameter");
+ return QMetaObject::Connection();
+ }
+
+ if (type & Qt::UniqueConnection && !slot) {
+ connectWarning(sender, senderMetaObject, receiver, "unique connections require a pointer to member function of a QObject subclass");
return QMetaObject::Connection();
}
@@ -4936,36 +5259,41 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s
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();
}
}
- type = static_cast<Qt::ConnectionType>(type ^ Qt::UniqueConnection);
}
+ type &= ~Qt::UniqueConnection;
+
+ const bool isSingleShot = type & Qt::SingleShotConnection;
+ type &= ~Qt::SingleShotConnection;
+
+ Q_ASSERT(type >= 0);
+ Q_ASSERT(type <= 3);
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;
}
+ c->isSingleShot = isSingleShot;
QObjectPrivate::get(s)->addConnection(signal_index, c.get());
QMetaObject::Connection ret(c.release());
@@ -4989,39 +5317,12 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s
bool QObject::disconnect(const QMetaObject::Connection &connection)
{
QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(connection.d_ptr);
-
if (!c)
return false;
- QObject *receiver = c->receiver.loadRelaxed();
- if (!receiver)
- return false;
-
- QBasicMutex *senderMutex = signalSlotLock(c->sender);
- QBasicMutex *receiverMutex = signalSlotLock(receiver);
-
- QObjectPrivate::ConnectionData *connections;
- {
- QOrderedMutexLocker locker(senderMutex, receiverMutex);
-
- // load receiver once again and recheck to ensure nobody else has removed the connection in the meantime
- receiver = c->receiver.loadRelaxed();
- if (!receiver)
- return false;
-
- connections = QObjectPrivate::get(c->sender)->connections.loadRelaxed();
- Q_ASSERT(connections);
- connections->removeConnection(c);
- }
-
- connections->cleanOrphanedConnections(c->sender);
-
- c->sender->disconnectNotify(QMetaObjectPrivate::signal(c->sender->metaObject(),
- c->signal_index));
-
+ 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 true;
+ return disconnected;
}
/*! \fn template<typename PointerToMemberFunction> bool QObject::disconnect(const QObject *sender, PointerToMemberFunction signal, const QObject *receiver, PointerToMemberFunction method)
@@ -5067,8 +5368,9 @@ bool QObject::disconnect(const QMetaObject::Connection &connection)
any signal. If not, only the specified signal is disconnected.
If \a receiver is \nullptr, it disconnects anything connected to \a
- signal. If not, slots in objects other than \a receiver are not
- disconnected.
+ signal. If not, only slots in the specified receiver are disconnected.
+ disconnect() with a non-null \a receiver also disconnects slot functions
+ that were connected with \a receiver as their context object.
If \a method is \nullptr, it disconnects anything that is connected to \a
receiver. If not, only slots named \a method will be disconnected,
@@ -5087,7 +5389,7 @@ bool QObject::disconnect(const QMetaObject::Connection &connection)
bool QObject::disconnectImpl(const QObject *sender, void **signal, const QObject *receiver, void **slot, const QMetaObject *senderMetaObject)
{
if (sender == nullptr || (receiver == nullptr && slot != nullptr)) {
- qWarning("QObject::disconnect: Unexpected nullptr parameter");
+ qCWarning(lcConnect, "QObject::disconnect: Unexpected nullptr parameter");
return false;
}
@@ -5100,7 +5402,7 @@ bool QObject::disconnectImpl(const QObject *sender, void **signal, const QObject
break;
}
if (!senderMetaObject) {
- qWarning("QObject::disconnect: signal not found in %s", sender->metaObject()->className());
+ qCWarning(lcConnect, "QObject::disconnect: signal not found in %s", sender->metaObject()->className());
return false;
}
signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject);
@@ -5111,22 +5413,43 @@ bool QObject::disconnectImpl(const QObject *sender, void **signal, const QObject
/*!
\internal
- Used by QML to connect a signal by index to a slot implemented in JavaScript (wrapped in a custom QSlotObjectBase subclass).
+ Used by QML to connect a signal by index to a slot implemented in JavaScript
+ (wrapped in a custom QSlotObjectBase subclass).
+
+ This version of connect assumes that sender and receiver are the same object.
The signal_index is an index relative to the number of methods.
*/
QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signal_index, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type)
{
+ return QObjectPrivate::connect(sender, signal_index, sender, slotObj, type);
+}
+
+/*!
+ \internal
+ Used by QML to connect a signal by index to a slot implemented in JavaScript
+ (wrapped in a custom QSlotObjectBase subclass).
+
+ This is an overload that should be used when \a sender and \a receiver are
+ different objects.
+
+ The signal_index is an index relative to the number of methods.
+ */
+QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signal_index,
+ const QObject *receiver,
+ QtPrivate::QSlotObjectBase *slotObjRaw,
+ Qt::ConnectionType type)
+{
+ QtPrivate::SlotObjUniquePtr slotObj(slotObjRaw);
if (!sender) {
- qWarning("QObject::connect: invalid nullptr parameter");
- if (slotObj)
- slotObj->destroyIfLastRef();
+ qCWarning(lcConnect, "QObject::connect: invalid nullptr parameter");
return QMetaObject::Connection();
}
const QMetaObject *senderMetaObject = sender->metaObject();
signal_index = methodIndexToSignalIndex(&senderMetaObject, signal_index);
- return QObjectPrivate::connectImpl(sender, signal_index, sender, /*slot*/nullptr, slotObj, type, /*types*/nullptr, senderMetaObject);
+ return connectImpl(sender, signal_index, receiver, /*slot*/ nullptr, slotObj.release(),
+ type, /*types*/ nullptr, senderMetaObject);
}
/*!
@@ -5134,13 +5457,104 @@ QMetaObject::Connection QObjectPrivate::connect(const QObject *sender, int signa
Used by QML to disconnect a signal by index that's connected to a slot implemented in JavaScript (wrapped in a custom QSlotObjectBase subclass)
In the QML case the slot is not a pointer to a pointer to the function to disconnect, but instead it is a pointer to an array of internal values
required for the disconnect.
+
+ This version of disconnect assumes that sender and receiver are the same object.
*/
bool QObjectPrivate::disconnect(const QObject *sender, int signal_index, void **slot)
{
+ return QObjectPrivate::disconnect(sender, signal_index, sender, slot);
+}
+
+/*!
+ \internal
+
+ Used by QML to disconnect a signal by index that's connected to a slot
+ implemented in JavaScript (wrapped in a custom QSlotObjectBase subclass) In the
+ QML case the slot is not a pointer to a pointer to the function to disconnect,
+ but instead it is a pointer to an array of internal values required for the
+ disconnect.
+
+ This is an overload that should be used when \a sender and \a receiver are
+ different objects.
+ */
+bool QObjectPrivate::disconnect(const QObject *sender, int signal_index, const QObject *receiver,
+ void **slot)
+{
const QMetaObject *senderMetaObject = sender->metaObject();
signal_index = methodIndexToSignalIndex(&senderMetaObject, signal_index);
- return QMetaObjectPrivate::disconnect(sender, signal_index, senderMetaObject, sender, -1, slot);
+ return QMetaObjectPrivate::disconnect(sender, signal_index, senderMetaObject, receiver, -1,
+ slot);
+}
+
+/*!
+ \internal
+ \threadsafe
+*/
+inline bool QObjectPrivate::removeConnection(QObjectPrivate::Connection *c)
+{
+ if (!c)
+ return false;
+ QObject *receiver = c->receiver.loadRelaxed();
+ if (!receiver)
+ return false;
+
+ QBasicMutex *senderMutex = signalSlotLock(c->sender);
+ QBasicMutex *receiverMutex = signalSlotLock(receiver);
+
+ QObjectPrivate::ConnectionData *connections;
+ {
+ QOrderedMutexLocker locker(senderMutex, receiverMutex);
+
+ // load receiver once again and recheck to ensure nobody else has removed the connection in the meantime
+ receiver = c->receiver.loadRelaxed();
+ if (!receiver)
+ return false;
+
+ connections = QObjectPrivate::get(c->sender)->connections.loadRelaxed();
+ Q_ASSERT(connections);
+ connections->removeConnection(c);
+
+ c->sender->disconnectNotify(QMetaObjectPrivate::signal(c->sender->metaObject(), c->signal_index));
+ // We must not hold the receiver mutex, else we risk dead-locking; we also only need the sender mutex
+ // It is however vital to hold the senderMutex before calling cleanOrphanedConnections, as otherwise
+ // another thread might modify/delete the connection
+ if (receiverMutex != senderMutex) {
+ receiverMutex->unlock();
+ }
+ connections->cleanOrphanedConnections(c->sender, ConnectionData::AlreadyLockedAndTemporarilyReleasingLock);
+ senderMutex->unlock(); // now both sender and receiver mutex have been manually unlocked
+ locker.dismiss(); // so we dismiss the QOrderedMutexLocker
+ }
+
+ 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
@@ -5167,7 +5581,7 @@ QMetaObject::Connection::Connection(const QMetaObject::Connection &other) : d_pt
/*!
Assigns \a other to this connection and returns a reference to this connection.
*/
-QMetaObject::Connection& QMetaObject::Connection::operator=(const QMetaObject::Connection& other)
+QMetaObject::Connection &QMetaObject::Connection::operator=(const QMetaObject::Connection &other)
{
if (other.d_ptr != d_ptr) {
if (d_ptr)
@@ -5216,5 +5630,4 @@ bool QMetaObject::Connection::isConnected_helper() const
QT_END_NAMESPACE
-#include "moc_qnamespace.cpp"
#include "moc_qobject.cpp"