diff options
-rw-r--r-- | src/corelib/io/qdirlisting.cpp | 18 | ||||
-rw-r--r-- | src/corelib/kernel/qcoreapplication.cpp | 11 | ||||
-rw-r--r-- | src/corelib/kernel/qmetaobject.cpp | 42 | ||||
-rw-r--r-- | src/corelib/kernel/qobject.cpp | 162 | ||||
-rw-r--r-- | src/corelib/kernel/qobject_p.h | 60 |
5 files changed, 225 insertions, 68 deletions
diff --git a/src/corelib/io/qdirlisting.cpp b/src/corelib/io/qdirlisting.cpp index c626033dcdb..1fec92a01e2 100644 --- a/src/corelib/io/qdirlisting.cpp +++ b/src/corelib/io/qdirlisting.cpp @@ -510,16 +510,14 @@ bool QDirListingPrivate::matchesFilters(QDirEntryInfo &entryInfo) const if (!iteratorFlags.testAnyFlag(F::IncludeHidden) && entryInfo.isHidden()) return false; - if (entryInfo.isSymLink()) { - // With ResolveSymlinks, we look at the type of the link's target, - // and exclude broken symlinks (where the target doesn't exist). - if (iteratorFlags.testAnyFlag(F::ResolveSymlinks)) { - if (!entryInfo.exists()) - return false; - } else if (iteratorFlags.testAnyFlags(F::FilesOnly) - || iteratorFlags.testAnyFlags(F::DirsOnly)) { - return false; // symlink is not a file or dir - } + // With ResolveSymlinks, we look at the type of the link's target, + // and exclude broken symlinks (where the target doesn't exist). + if (iteratorFlags.testAnyFlag(F::ResolveSymlinks)) { + if (entryInfo.isSymLink() && !entryInfo.exists()) + return false; + } else if ((iteratorFlags.testAnyFlags(F::FilesOnly) + || iteratorFlags.testAnyFlags(F::DirsOnly)) && entryInfo.isSymLink()) { + return false; // symlink is not a file or dir } if (iteratorFlags.testAnyFlag(F::ExcludeOther) diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 1b711ba16bd..55d9cea7c6c 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -1618,6 +1618,17 @@ QCoreApplicationPrivate::QPostEventListLocker QCoreApplicationPrivate::lockThrea details. Events with equal \a priority will be processed in the order posted. + \note QObject::deleteLater() schedules the object for deferred + deletion, which is typically handled by the receiver's event + loop. If no event loop is running in the thread, the deletion + will be performed when the thread finishes. A common and safe + pattern is to connect the thread's finished() signal to the + object's deleteLater() slot: + + \code + QObject::connect(thread, &QThread::finished, worker, &QObject::deleteLater); + \endcode + \threadsafe \sa sendEvent(), notify(), sendPostedEvents(), Qt::EventPriority diff --git a/src/corelib/kernel/qmetaobject.cpp b/src/corelib/kernel/qmetaobject.cpp index de5f27f9619..8684a02b727 100644 --- a/src/corelib/kernel/qmetaobject.cpp +++ b/src/corelib/kernel/qmetaobject.cpp @@ -1733,16 +1733,8 @@ bool QMetaObject::invokeMethodImpl(QObject *object, QtPrivate::QSlotObjectBase * "queued connections"); return false; } - auto event = std::make_unique<QMetaCallEvent>(std::move(slot), nullptr, -1, parameterCount); - void **args = event->args(); - QMetaType *types = event->types(); - - for (int i = 1; i < parameterCount; ++i) { - types[i] = QMetaType(metaTypes[i]); - args[i] = types[i].create(argv[i]); - } - - QCoreApplication::postEvent(object, event.release()); + QCoreApplication::postEvent(object, new QQueuedMetaCallEvent(std::move(slot), nullptr, -1, + parameterCount, metaTypes, params)); } else if (type == Qt::BlockingQueuedConnection) { #if QT_CONFIG(thread) if (receiverInSameThread) @@ -2889,31 +2881,27 @@ auto QMetaMethodInvoker::invokeImpl(QMetaMethod self, void *target, return InvokeFailReason::CouldNotQueueParameter; } - auto event = std::make_unique<QMetaCallEvent>(idx_offset, idx_relative, callFunction, nullptr, -1, paramCount); - QMetaType *types = event->types(); - void **args = event->args(); - + QVarLengthArray<const QtPrivate::QMetaTypeInterface *> argTypes; + argTypes.emplace_back(nullptr); // return type // fill in the meta types first for (int i = 1; i < paramCount; ++i) { - types[i] = QMetaType(methodMetaTypes[i - 1]); - if (!types[i].iface() && (!MetaTypesAreOptional || metaTypes)) - types[i] = QMetaType(metaTypes[i]); - if (!types[i].iface()) - types[i] = priv->parameterMetaType(i - 1); - if (!types[i].iface() && typeNames[i]) - types[i] = QMetaType::fromName(typeNames[i]); - if (!types[i].iface()) { + QMetaType type = QMetaType(methodMetaTypes[i - 1]); + if (!type.iface() && (!MetaTypesAreOptional || metaTypes)) + type = QMetaType(metaTypes[i]); + if (!type.iface()) + type = priv->parameterMetaType(i - 1); + if (!type.iface() && typeNames[i]) + type = QMetaType::fromName(typeNames[i]); + if (!type.iface()) { qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'", typeNames[i]); return InvokeFailReason(int(InvokeFailReason::CouldNotQueueParameter) - i); } + argTypes.emplace_back(type.iface()); } - // now create copies of our parameters using those meta types - for (int i = 1; i < paramCount; ++i) - args[i] = types[i].create(parameters[i]); - - QCoreApplication::postEvent(object, event.release()); + QCoreApplication::postEvent(object, new QQueuedMetaCallEvent(idx_offset, idx_relative, callFunction, nullptr, + -1, paramCount, argTypes.data(), parameters)); } else { // blocking queued connection #if QT_CONFIG(thread) if (receiverInSameThread()) { diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 888c71f5a49..790ccc29339 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -599,14 +599,19 @@ QMetaCallEvent::QMetaCallEvent(QtPrivate::SlotObjUniquePtr slotO, /*! \internal */ +QMetaCallEvent::QMetaCallEvent(const QObject *sender, int signalId, Data &&data) + : QAbstractMetaCallEvent(sender, signalId), + d(std::move(data)), + prealloc_() +{ +} + +/*! + \internal + */ QMetaCallEvent::~QMetaCallEvent() { if (d.nargs_) { - QMetaType *t = types(); - for (int i = 0; i < d.nargs_; ++i) { - if (t[i].isValid() && d.args_[i]) - t[i].destroy(d.args_[i]); - } if (reinterpret_cast<void *>(d.args_) != reinterpret_cast<void *>(prealloc_)) free(d.args_); } @@ -628,6 +633,115 @@ void QMetaCallEvent::placeMetaCall(QObject *object) } /*! + \internal + + Constructs a QQueuedMetaCallEvent by copying the argument values using their meta-types. + */ +QQueuedMetaCallEvent::QQueuedMetaCallEvent(ushort method_offset, ushort method_relative, + QObjectPrivate::StaticMetaCallFunction callFunction, + const QObject *sender, int signalId, int argCount, + const QtPrivate::QMetaTypeInterface * const *argTypes, + const void * const *argValues) + : QMetaCallEvent(sender, signalId, {nullptr, nullptr, callFunction, argCount, + method_offset, method_relative}) +{ + allocArgs(); + copyArgValues(argCount, argTypes, argValues); +} + +/*! + \internal + + Constructs a QQueuedMetaCallEvent by copying the argument values using their meta-types. + */ +QQueuedMetaCallEvent::QQueuedMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj, + const QObject *sender, int signalId, int argCount, + const QtPrivate::QMetaTypeInterface * const *argTypes, + const void * const *argValues) + : QMetaCallEvent(sender, signalId, {QtPrivate::SlotObjUniquePtr(slotObj), nullptr, nullptr, argCount, + 0, ushort(-1)}) +{ + if (d.slotObj_) + d.slotObj_->ref(); + allocArgs(); + copyArgValues(argCount, argTypes, argValues); +} + +/*! + \internal + + Constructs a QQueuedMetaCallEvent by copying the argument values using their meta-types. + */ +QQueuedMetaCallEvent::QQueuedMetaCallEvent(QtPrivate::SlotObjUniquePtr slotObj, + const QObject *sender, int signalId, int argCount, + const QtPrivate::QMetaTypeInterface * const *argTypes, + const void * const *argValues) + : QMetaCallEvent(sender, signalId, {std::move(slotObj), nullptr, nullptr, argCount, + 0, ushort(-1)}) +{ + allocArgs(); + copyArgValues(argCount, argTypes, argValues); +} + +/*! + \internal + */ +QQueuedMetaCallEvent::~QQueuedMetaCallEvent() +{ + const QMetaType *t = types(); + int inplaceIndex = 0; + for (int i = 0; i < d.nargs_; ++i) { + if (t[i].isValid() && d.args_[i]) { + if (typeFitsInPlace(t[i]) && inplaceIndex < InplaceValuesCapacity) { + // Only destruct + void *where = &valuesPrealloc_[inplaceIndex++].storage; + t[i].destruct(where); + } else { + // Destruct and deallocate + t[i].destroy(d.args_[i]); + } + } + } +} + +/*! + \internal + */ +inline void QQueuedMetaCallEvent::copyArgValues(int argCount, const QtPrivate::QMetaTypeInterface * const *argTypes, + const void * const *argValues) +{ + void **args = d.args_; + QMetaType *types = this->types(); + int inplaceIndex = 0; + + types[0] = QMetaType(); // return type + args[0] = nullptr; // return value pointer + // no return value + + for (int n = 1; n < argCount; ++n) { + types[n] = QMetaType(argTypes[n]); + if (typeFitsInPlace(types[n]) && inplaceIndex < InplaceValuesCapacity) { + // Copy-construct in place + void *where = &valuesPrealloc_[inplaceIndex++].storage; + types[n].construct(where, argValues[n]); + args[n] = where; + } else { + // Allocate and copy-construct + args[n] = types[n].create(argValues[n]); + } + } +} + +/*! + \internal + */ +inline bool QQueuedMetaCallEvent::typeFitsInPlace(const QMetaType type) +{ + return (q20::cmp_less_equal(type.sizeOf(), sizeof(ArgValueStorage)) && + q20::cmp_less_equal(type.alignOf(), alignof(ArgValueStorage))); +} + +/*! \class QSignalBlocker \brief Exception-safe wrapper around QObject::blockSignals(). \since 5.3 @@ -2406,6 +2520,14 @@ void QObject::removeEventFilter(QObject *obj) thread with no running event loop, the object will be destroyed when the thread finishes. + A common pattern when using a worker \c QObject in a \c QThread + is to connect the thread's \c finished() signal to the worker's + \c deleteLater() slot to ensure it is safely deleted: + + \code + connect(thread, &QThread::finished, worker, &QObject::deleteLater); + \endcode + Note that entering and leaving a new event loop (e.g., by opening a modal dialog) will \e not perform the deferred deletion; for the object to be deleted, the control must return to the event loop from which deleteLater() @@ -4069,26 +4191,19 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect SlotObjectGuard slotObjectGuard { c->isSlotObject ? c->slotObj : nullptr }; locker.unlock(); - QMetaCallEvent *ev = c->isSlotObject ? - new QMetaCallEvent(c->slotObj, sender, signal, nargs) : - new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal, nargs); - - void **args = ev->args(); - QMetaType *types = ev->types(); - - types[0] = QMetaType(); // return type - args[0] = nullptr; // return value - - if (nargs > 1) { - for (int n = 1; n < nargs; ++n) - types[n] = QMetaType(argumentTypes[n - 1]); - - for (int n = 1; n < nargs; ++n) - args[n] = types[n].create(argv[n]); + QVarLengthArray<const QtPrivate::QMetaTypeInterface *> argTypes; + argTypes.emplace_back(nullptr); // return type + for (int n = 1; n < nargs; ++n) { + argTypes.emplace_back(QMetaType(argumentTypes[n - 1]).iface()); // convert type ids to QMetaTypeInterfaces } + auto ev = c->isSlotObject ? + std::make_unique<QQueuedMetaCallEvent>(c->slotObj, + sender, signal, nargs, argTypes.data(), argv) : + std::make_unique<QQueuedMetaCallEvent>(c->method_offset, c->method_relative, c->callFunction, + sender, signal, nargs, argTypes.data(), argv); + if (c->isSingleShot && !QObjectPrivate::removeConnection(c)) { - delete ev; return; } @@ -4096,11 +4211,10 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect if (!c->isSingleShot && !c->receiver.loadRelaxed()) { // the connection has been disconnected while we were unlocked locker.unlock(); - delete ev; return; } - QCoreApplication::postEvent(receiver, ev); + QCoreApplication::postEvent(receiver, ev.release()); } template <bool callbacks_enabled> diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h index dca9c1af932..75aeba73583 100644 --- a/src/corelib/kernel/qobject_p.h +++ b/src/corelib/kernel/qobject_p.h @@ -351,7 +351,7 @@ private: class Q_CORE_EXPORT QMetaCallEvent : public QAbstractMetaCallEvent { public: - // blocking queued with latch - args always owned by caller + // blocking queued with latch - arguments always remain owned by the caller QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction, const QObject *sender, int signalId, @@ -363,14 +363,17 @@ public: const QObject *sender, int signalId, void **args, QLatch *latch); - // queued - args allocated by event, copied by caller + // OLD - queued - args allocated by event, copied by caller + Q_DECL_DEPRECATED_X("Remove this constructor once the qtdeclarative patch is merged. Arguments are now copied by the QQueuedMetaCallEvent constructor and not the caller.") QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction, const QObject *sender, int signalId, int nargs); + Q_DECL_DEPRECATED_X("Remove this constructor once the qtdeclarative patch is merged. Arguments are now copied by the QQueuedMetaCallEvent constructor and not the caller.") QMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj, const QObject *sender, int signalId, int nargs); + Q_DECL_DEPRECATED_X("Remove this constructor once the qtdeclarative patch is merged. Arguments are now copied by the QQueuedMetaCallEvent constructor and not the caller.") QMetaCallEvent(QtPrivate::SlotObjUniquePtr slotObj, const QObject *sender, int signalId, int nargs); @@ -378,14 +381,16 @@ public: ~QMetaCallEvent() override; inline int id() const { return d.method_offset_ + d.method_relative_; } - inline const void * const* args() const { return d.args_; } + Q_DECL_DEPRECATED_X("Remove this function once the qtdeclarative patch is merged. Arguments are now copied by the QQueuedMetaCallEvent constructor and not the caller.") inline void ** args() { return d.args_; } - inline const QMetaType *types() const { return reinterpret_cast<QMetaType *>(d.args_ + d.nargs_); } + Q_DECL_DEPRECATED_X("Remove this function once the qtdeclarative patch is merged. Arguments are now copied by the QQueuedMetaCallEvent constructor and not the caller.") inline QMetaType *types() { return reinterpret_cast<QMetaType *>(d.args_ + d.nargs_); } virtual void placeMetaCall(QObject *object) override; -private: +protected: + // Move to QQueuedMetaCallEvent once the qtdeclarative patch is merged. + // QMetaCallEvent should not alloc anything anymore. inline void allocArgs(); struct Data { @@ -396,9 +401,50 @@ private: ushort method_offset_; ushort method_relative_; } d; - // preallocate enough space for three arguments - alignas(void *) char prealloc_[3 * sizeof(void *) + 3 * sizeof(QMetaType)]; + + inline QMetaCallEvent(const QObject *sender, int signalId, Data &&data); + inline void * const *args() const { return d.args_; } + inline QMetaType const *types() const { return reinterpret_cast<QMetaType *>(d.args_ + d.nargs_); } + + // Space for 5 argument pointers and types (including 1 return arg). + // Contiguous so that we can make one calloc() for both the pointers and the types when necessary. + // Move to QQueuedMetaCallEvent once the qtdeclarative patch is merged. + alignas(void *) char prealloc_[5 * sizeof(void *) + 5 * sizeof(QMetaType)]; +}; + +class Q_CORE_EXPORT QQueuedMetaCallEvent : public QMetaCallEvent +{ +public: + // queued - arguments are allocated and copied from argValues by these constructors + QQueuedMetaCallEvent(ushort method_offset, ushort method_relative, + QObjectPrivate::StaticMetaCallFunction callFunction, + const QObject *sender, int signalId, + int argCount, const QtPrivate::QMetaTypeInterface * const *argTypes, + const void * const *argValues); + QQueuedMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj, + const QObject *sender, int signalId, + int argCount, const QtPrivate::QMetaTypeInterface * const *argTypes, + const void * const *argValues); + QQueuedMetaCallEvent(QtPrivate::SlotObjUniquePtr slotObj, + const QObject *sender, int signalId, + int argCount, const QtPrivate::QMetaTypeInterface * const *argTypes, + const void * const *argValues); + + ~QQueuedMetaCallEvent() override; + +private: + inline void copyArgValues(int argCount, const QtPrivate::QMetaTypeInterface * const *argTypes, + const void * const *argValues); + static inline bool typeFitsInPlace(const QMetaType type); + + struct ArgValueStorage { // size and alignment matching QString, QList, etc + static constexpr size_t MaxSize = 3 * sizeof(void *); + alignas(void *) char storage[MaxSize]; + }; + static constexpr int InplaceValuesCapacity = 3; + ArgValueStorage valuesPrealloc_[InplaceValuesCapacity]; }; +// The total QQueuedMetaCallEvent size is 224 bytes which is a 32-byte multiple, efficient for memory allocators. struct QAbstractDynamicMetaObject; struct Q_CORE_EXPORT QDynamicMetaObjectData |