Port QCoreApplication::requestPermission() to SlotObjUniquePtr internally

Well, except that the ownership of the slot object here is actually
shared between the PermissionReceiver and the lambda passed to
QPermission::Private::requestPermission(), which eventually may hand
ownership over to QMetaCallEvent, so we can't really use
SlotObjUniquePtr.

While we can, of course, manually copy and call ref(), even if the
slot-object is stored in a SlotObjUniquePtr, unfortunately, the lambda
is subsequently stored in a std::function, which infamously cannot
hold move-only objects, so we actually need something copyable.

So grasp the nettle and implement a SlotObjSharedPtr.

I was originally planning to just make this a typedef for
QIntrusiveSharedPointer, but that's not in dev, yet, let alone 6.5, to
which we're picking this, and there was always this nagging impedance
mismatch between the QIntrusiveSharedPointer behavior, inherited from
it's Q(Explicitly)SharedPointer roots, on the one hand, to always
ref() in the constructor from raw pointer, and, OTOH, QSlotObjectBase
starting its life with a ref-count of one (1) (not zero (0), like
QSharedData).

I eventually found the (elegant, if I may say so myself) solution to
just not provide a constructor from raw pointer, only one from
SlotObjUniquePtr, which, granted, has the same issue, but which is, by
now, probably, hopefully, more fully grasped by QtCore regulars, and so
we can piggy-back on that for SlotObjSharedPtr's constructor
semantics.

Add a comment nevertheless.

Inside the lambda, we could theoretically move the slotObj into
QMetaCallEvent::create(), after adding such conversion to
SlotObjSharedPtr, but that would require making the lambda mutable,
and seeing as it's stored in a std::function and copied around, I was
not ready to make that change just yet.

As a drive-by, make PermissionReceiver's constructor explicit.

Pick-to: 6.6 6.5
Fixes: QTBUG-115330
Change-Id: I4e0cec13d19a19eeec31e4101ce289d07c92ce46
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Marc Mutz 2023-07-19 21:50:39 +02:00
parent 902063628f
commit f141108887
2 changed files with 39 additions and 9 deletions

View File

@ -2876,12 +2876,13 @@ Qt::PermissionStatus QCoreApplication::checkPermission(const QPermission &permis
Called by the various requestPermission overloads to perform the request.
Calls the functor encapsulated in the \a slotObj in the given \a context
Calls the functor encapsulated in the \a slotObjRaw in the given \a context
(which may be \c nullptr).
*/
void QCoreApplication::requestPermission(const QPermission &requestedPermission,
QtPrivate::QSlotObjectBase *slotObj, const QObject *context)
QtPrivate::QSlotObjectBase *slotObjRaw, const QObject *context)
{
QtPrivate::SlotObjSharedPtr slotObj(QtPrivate::SlotObjUniquePtr{slotObjRaw}); // adopts
if (QThread::currentThread() != QCoreApplicationPrivate::mainThread()) {
qWarning(lcPermissions, "Permissions can only be requested from the GUI (main) thread");
return;
@ -2903,7 +2904,7 @@ void QCoreApplication::requestPermission(const QPermission &requestedPermission,
class PermissionReceiver : public QObject
{
public:
PermissionReceiver(QtPrivate::QSlotObjectBase *slotObject, const QObject *context)
explicit PermissionReceiver(const QtPrivate::SlotObjSharedPtr &slotObject, const QObject *context)
: slotObject(slotObject), context(context)
{}
@ -2916,7 +2917,6 @@ void QCoreApplication::requestPermission(const QPermission &requestedPermission,
// only execute if context object is still alive
if (context)
slotObject->call(const_cast<QObject*>(context.data()), metaCallEvent->args());
slotObject->destroyIfLastRef();
deleteLater();
return true;
@ -2925,7 +2925,7 @@ void QCoreApplication::requestPermission(const QPermission &requestedPermission,
return QObject::event(event);
}
private:
QtPrivate::QSlotObjectBase *slotObject;
QtPrivate::SlotObjSharedPtr slotObject;
QPointer<const QObject> context;
};
PermissionReceiver *receiver = nullptr;
@ -2945,7 +2945,7 @@ void QCoreApplication::requestPermission(const QPermission &requestedPermission,
permission.m_status = status;
if (receiver) {
auto metaCallEvent = QMetaCallEvent::create(slotObj, qApp,
auto metaCallEvent = QMetaCallEvent::create(slotObj.get(), qApp,
PermissionReceivedID, permission);
qApp->postEvent(receiver, metaCallEvent);
} else {
@ -2953,9 +2953,6 @@ void QCoreApplication::requestPermission(const QPermission &requestedPermission,
slotObj->call(const_cast<QObject*>(context), argv);
}
}
if (!receiver)
slotObj->destroyIfLastRef();
});
}

View File

@ -453,6 +453,39 @@ namespace QtPrivate {
using SlotObjUniquePtr = std::unique_ptr<QSlotObjectBase,
QSlotObjectBase::Deleter>;
class SlotObjSharedPtr {
SlotObjUniquePtr obj;
public:
Q_NODISCARD_CTOR Q_IMPLICIT SlotObjSharedPtr() noexcept = default;
Q_NODISCARD_CTOR Q_IMPLICIT SlotObjSharedPtr(std::nullptr_t) noexcept : SlotObjSharedPtr() {}
Q_NODISCARD_CTOR explicit SlotObjSharedPtr(SlotObjUniquePtr o)
: obj(std::move(o))
{
// does NOT ref() (takes unique_ptr by value)
// (that's why (QSlotObjectBase*) ctor doesn't exisit: don't know whether that one _should_)
}
Q_NODISCARD_CTOR SlotObjSharedPtr(const SlotObjSharedPtr &other) noexcept
: obj(other.obj.get())
{
if (obj)
obj->ref();
}
SlotObjSharedPtr &operator=(const SlotObjSharedPtr &other) noexcept
{ auto copy = other; swap(copy); return *this; }
Q_NODISCARD_CTOR SlotObjSharedPtr(SlotObjSharedPtr &&other) noexcept = default;
SlotObjSharedPtr &operator=(SlotObjSharedPtr &&other) noexcept = default;
~SlotObjSharedPtr() = default;
void swap(SlotObjSharedPtr &other) noexcept { obj.swap(other.obj); }
auto get() const noexcept { return obj.get(); }
auto operator->() const noexcept { return get(); }
explicit operator bool() const noexcept { return bool(obj); }
};
// Implementation of QSlotObjectBase for which the slot is a callable (function, PMF, functor, or lambda).
// Args and R are the List of arguments and the return type of the signal to which the slot is connected.
template <typename Func, typename Args, typename R>