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,
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()) {
qWarning(lcPermissions, "Permissions can only be requested from the GUI (main) thread");
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
{
public:
explicit PermissionReceiver(const QtPrivate::SlotObjSharedPtr &slotObject, const QObject *context)
: slotObject(slotObject), context(context)
{}
protected:
bool event(QEvent *event) override {
if (event->type() == QEvent::MetaCall) {
auto metaCallEvent = static_cast<QMetaCallEvent *>(event);
if (metaCallEvent->id() == PermissionReceivedID) {
Q_ASSERT(slotObject);
// only execute if context object is still alive
if (context)
slotObject->call(const_cast<QObject*>(context.data()), metaCallEvent->args());
deleteLater();
return true;
}
}
return QObject::event(event);
explicit PermissionReceiver(QtPrivate::SlotObjUniquePtr &&slotObject, const QObject *context)
: slotObject(std::move(slotObject)), context(context ? context : this)
{
Q_ASSERT(this->context);
moveToThread(this->context->thread());
}
void finalizePermissionRequest(const QPermission &permission)
{
Q_ASSERT(slotObject);
// only execute if context object is still alive
if (context) {
void *args[] = { nullptr, const_cast<QPermission *>(&permission) };
slotObject->call(const_cast<QObject *>(context.data()), args);
}
deleteLater();
}
private:
QtPrivate::SlotObjSharedPtr slotObject;
QPointer<const QObject> context;
};
PermissionReceiver *receiver = nullptr;
if (context) {
receiver = new PermissionReceiver(slotObj, context);
receiver->moveToThread(context->thread());
}
PermissionReceiver *receiver = new PermissionReceiver(std::move(slotObj), context);
QPermissions::Private::requestPermission(requestedPermission, [=](Qt::PermissionStatus status) {
Q_ASSERT_X(status != Qt::PermissionStatus::Undetermined, "QPermission",
@ -2945,15 +2929,10 @@ void QCoreApplication::requestPermission(const QPermission &requestedPermission,
if (QCoreApplication::self) {
QPermission permission = requestedPermission;
permission.m_status = status;
if (receiver) {
auto metaCallEvent = QMetaCallEvent::create(slotObj.get(), qApp,
PermissionReceivedID, permission);
qApp->postEvent(receiver, metaCallEvent);
} else {
void *argv[] = { nullptr, &permission };
slotObj->call(const_cast<QObject*>(context), argv);
}
QMetaObject::invokeMethod(receiver,
&PermissionReceiver::finalizePermissionRequest,
Qt::QueuedConnection,
permission);
}
});
}