QCoreApplication::requestPermission: streamline the implementation

Having a context object is the common case, so it's worth optimizing
for it. Remove the routing through a metacall event, which isn't needed,
in favor of a simple queued invokeMethod. Also, always provide a context
object when calling the slot, even if one wasn't provided by the user;
simply use the "sender" object, that is, the internal QObject that is
activating the slot. (This way we match the semantics context-less
connect() overload, which uses the sender object as a context.)

Change-Id: I72051fc48cbda5729145557f15cec4693ffde6ce
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Giuseppe D'Angelo 2023-09-05 00:18:54 +02:00
parent 6add599079
commit 0bd1fc0060

View File

@ -2884,57 +2884,41 @@ Qt::PermissionStatus QCoreApplication::checkPermission(const QPermission &permis
void QCoreApplication::requestPermission(const QPermission &requestedPermission, void QCoreApplication::requestPermission(const QPermission &requestedPermission,
QtPrivate::QSlotObjectBase *slotObjRaw, const QObject *context) QtPrivate::QSlotObjectBase *slotObjRaw, const QObject *context)
{ {
QtPrivate::SlotObjSharedPtr slotObj(QtPrivate::SlotObjUniquePtr{slotObjRaw}); // adopts QtPrivate::SlotObjUniquePtr slotObj{slotObjRaw}; // adopts
Q_ASSERT(slotObj);
if (QThread::currentThread() != QCoreApplicationPrivate::mainThread()) { if (QThread::currentThread() != QCoreApplicationPrivate::mainThread()) {
qWarning(lcPermissions, "Permissions can only be requested from the GUI (main) thread"); qWarning(lcPermissions, "Permissions can only be requested from the GUI (main) thread");
return; return;
} }
Q_ASSERT(slotObj);
// Used as the signalID in the metacall event and only used to
// verify that we are not processing an unrelated event, not to
// emit the right signal. So using a value that can never clash
// with any signal index. Clang doesn't like this to be a static
// member of the PermissionReceiver.
static constexpr ushort PermissionReceivedID = 0xffff;
// If we have a context object, then we dispatch the permission response
// asynchronously through a received object that lives in the same thread
// as the context object. Otherwise we call the functor synchronously when
// we get a response (which might still be asynchronous for the caller).
class PermissionReceiver : public QObject class PermissionReceiver : public QObject
{ {
public: public:
explicit PermissionReceiver(const QtPrivate::SlotObjSharedPtr &slotObject, const QObject *context) explicit PermissionReceiver(QtPrivate::SlotObjUniquePtr &&slotObject, const QObject *context)
: slotObject(slotObject), context(context) : slotObject(std::move(slotObject)), context(context ? context : this)
{} {
Q_ASSERT(this->context);
moveToThread(this->context->thread());
}
protected: void finalizePermissionRequest(const QPermission &permission)
bool event(QEvent *event) override { {
if (event->type() == QEvent::MetaCall) {
auto metaCallEvent = static_cast<QMetaCallEvent *>(event);
if (metaCallEvent->id() == PermissionReceivedID) {
Q_ASSERT(slotObject); Q_ASSERT(slotObject);
// only execute if context object is still alive // only execute if context object is still alive
if (context) if (context) {
slotObject->call(const_cast<QObject*>(context.data()), metaCallEvent->args()); void *args[] = { nullptr, const_cast<QPermission *>(&permission) };
slotObject->call(const_cast<QObject *>(context.data()), args);
}
deleteLater(); deleteLater();
}
return true;
}
}
return QObject::event(event);
}
private: private:
QtPrivate::SlotObjSharedPtr slotObject; QtPrivate::SlotObjSharedPtr slotObject;
QPointer<const QObject> context; QPointer<const QObject> context;
}; };
PermissionReceiver *receiver = nullptr;
if (context) { PermissionReceiver *receiver = new PermissionReceiver(std::move(slotObj), context);
receiver = new PermissionReceiver(slotObj, context);
receiver->moveToThread(context->thread());
}
QPermissions::Private::requestPermission(requestedPermission, [=](Qt::PermissionStatus status) { QPermissions::Private::requestPermission(requestedPermission, [=](Qt::PermissionStatus status) {
Q_ASSERT_X(status != Qt::PermissionStatus::Undetermined, "QPermission", Q_ASSERT_X(status != Qt::PermissionStatus::Undetermined, "QPermission",
@ -2945,15 +2929,10 @@ void QCoreApplication::requestPermission(const QPermission &requestedPermission,
if (QCoreApplication::self) { if (QCoreApplication::self) {
QPermission permission = requestedPermission; QPermission permission = requestedPermission;
permission.m_status = status; permission.m_status = status;
QMetaObject::invokeMethod(receiver,
if (receiver) { &PermissionReceiver::finalizePermissionRequest,
auto metaCallEvent = QMetaCallEvent::create(slotObj.get(), qApp, Qt::QueuedConnection,
PermissionReceivedID, permission); permission);
qApp->postEvent(receiver, metaCallEvent);
} else {
void *argv[] = { nullptr, &permission };
slotObj->call(const_cast<QObject*>(context), argv);
}
} }
}); });
} }