Use QBasicMutex instead of QMutex in the signalSlockLock()

Add a simple private QBasicMutexLocker class, and let the
QOrderedMutexLocker operate on a QBasicMutex.

This allows the compiler to inline more things when handling
connections and speeds up activate() a bit more.

                       without change    with change
string based connect:  3621              3368
pointer based connect: 4341              3919
not connected:          433               437
disconnected:           551               538

Change-Id: If979337891178aaeb0b3340b6d4f68b6f86b0260
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
bb10
Lars Knoll 2019-01-09 11:00:01 +01:00
parent 0e534bf8b7
commit aea500d5d7
3 changed files with 81 additions and 32 deletions

View File

@ -54,6 +54,7 @@
#include <QtCore/qglobal.h>
#include <QtCore/qobjectdefs.h>
#include <QtCore/qmutex.h>
#ifndef QT_NO_QOBJECT
#include <private/qobject_p.h> // For QObjectPrivate::Connection
#endif
@ -168,7 +169,6 @@ Q_DECLARE_TYPEINFO(QArgumentType, Q_MOVABLE_TYPE);
typedef QVarLengthArray<QArgumentType, 10> QArgumentTypeArray;
class QMetaMethodPrivate;
class QMutex;
struct QMetaObjectPrivate
{
@ -234,7 +234,7 @@ struct QMetaObjectPrivate
DisconnectType = DisconnectAll);
static inline bool disconnectHelper(QObjectPrivate::Connection *c,
const QObject *receiver, int method_index, void **slot,
QMutex *senderMutex, DisconnectType = DisconnectAll);
QBasicMutex *senderMutex, DisconnectType = DisconnectAll);
#endif
};

View File

@ -152,10 +152,9 @@ static QBasicMutex _q_ObjectMutexPool[131];
* \internal
* mutex to be locked when accessing the connectionlists or the senders list
*/
static inline QMutex *signalSlotLock(const QObject *o)
static inline QBasicMutex *signalSlotLock(const QObject *o)
{
return static_cast<QMutex *>(&_q_ObjectMutexPool[
uint(quintptr(o)) % sizeof(_q_ObjectMutexPool)/sizeof(QBasicMutex)]);
return &_q_ObjectMutexPool[uint(quintptr(o)) % sizeof(_q_ObjectMutexPool)/sizeof(QBasicMutex)];
}
#if QT_VERSION < 0x60000
@ -265,7 +264,7 @@ bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const
ConnectionData *cd = connections.load();
if (signal_index < 0 || !cd)
return false;
QMutexLocker locker(signalSlotLock(q));
QBasicMutexLocker locker(signalSlotLock(q));
if (signal_index < cd->signalVector.count()) {
const QObjectPrivate::Connection *c = cd->signalVector.at(signal_index).first;
@ -287,7 +286,7 @@ QObjectList QObjectPrivate::receiverList(const char *signal) const
ConnectionData *cd = connections.load();
if (signal_index < 0 || !cd)
return returnValue;
QMutexLocker locker(signalSlotLock(q));
QBasicMutexLocker locker(signalSlotLock(q));
if (signal_index < cd->signalVector.count()) {
const QObjectPrivate::Connection *c = cd->signalVector.at(signal_index).first;
@ -306,7 +305,7 @@ QObjectList QObjectPrivate::senderList() const
QObjectList returnValue;
ConnectionData *cd = connections.load();
if (cd) {
QMutexLocker locker(signalSlotLock(q_func()));
QBasicMutexLocker locker(signalSlotLock(q_func()));
for (Connection *c = cd->senders; c; c = c->next)
returnValue << c->sender;
}
@ -881,8 +880,8 @@ QObject::~QObject()
cd->currentSender = nullptr;
}
QMutex *signalSlotMutex = signalSlotLock(this);
QMutexLocker locker(signalSlotMutex);
QBasicMutex *signalSlotMutex = signalSlotLock(this);
QBasicMutexLocker locker(signalSlotMutex);
// disconnect all receivers
int receiverCount = cd->signalVector.count();
@ -896,7 +895,7 @@ QObject::~QObject()
continue;
}
QMutex *m = signalSlotLock(c->receiver);
QBasicMutex *m = signalSlotLock(c->receiver);
bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
if (c->receiver) {
@ -937,7 +936,7 @@ QObject::~QObject()
// This ensures any eventual destructor of sender will block on getting receiver's lock
// and not finish until we release it.
sender->disconnectNotify(QMetaObjectPrivate::signal(sender->metaObject(), node->signal_index));
QMutex *m = signalSlotLock(sender);
QBasicMutex *m = signalSlotLock(sender);
node->prev = &node;
bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
//the node has maybe been removed while the mutex was unlocked in relock?
@ -1205,7 +1204,7 @@ bool QObject::event(QEvent *e)
QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(e);
if (!d_func()->connections.load()) {
QMutexLocker locker(signalSlotLock(this));
QBasicMutexLocker locker(signalSlotLock(this));
d_func()->ensureConnectionData();
}
QObjectPrivate::Sender sender(this, const_cast<QObject*>(mce->sender()), mce->signalId());
@ -2323,7 +2322,7 @@ QObject *QObject::sender() const
{
Q_D(const QObject);
QMutexLocker locker(signalSlotLock(this));
QBasicMutexLocker locker(signalSlotLock(this));
QObjectPrivate::ConnectionData *cd = d->connections.load();
if (!cd || !cd->currentSender)
return nullptr;
@ -2365,7 +2364,7 @@ int QObject::senderSignalIndex() const
{
Q_D(const QObject);
QMutexLocker locker(signalSlotLock(this));
QBasicMutexLocker locker(signalSlotLock(this));
QObjectPrivate::ConnectionData *cd = d->connections.load();
if (!cd || !cd->currentSender)
return -1;
@ -2430,7 +2429,7 @@ int QObject::receivers(const char *signal) const
signal_index);
}
QMutexLocker locker(signalSlotLock(this));
QBasicMutexLocker locker(signalSlotLock(this));
if (signal_index < cd->signalVector.count()) {
const QObjectPrivate::Connection *c =
cd->signalVector.at(signal_index).first;
@ -2476,7 +2475,7 @@ bool QObject::isSignalConnected(const QMetaMethod &signal) const
signalIndex += QMetaObjectPrivate::signalOffset(signal.mobj);
QMutexLocker locker(signalSlotLock(this));
QBasicMutexLocker locker(signalSlotLock(this));
return d->isSignalConnected(signalIndex, true);
}
@ -3318,7 +3317,7 @@ bool QMetaObject::disconnectOne(const QObject *sender, int signal_index,
*/
bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::Connection *c,
const QObject *receiver, int method_index, void **slot,
QMutex *senderMutex, DisconnectType disconnectType)
QBasicMutex *senderMutex, DisconnectType disconnectType)
{
bool success = false;
while (c) {
@ -3327,7 +3326,7 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::Connection *c,
&& (method_index < 0 || (!c->isSlotObject && c->method() == method_index))
&& (slot == 0 || (c->isSlotObject && c->slotObj->compare(slot)))))) {
bool needToUnlock = false;
QMutex *receiverMutex = 0;
QBasicMutex *receiverMutex = nullptr;
if (c->receiver) {
receiverMutex = signalSlotLock(c->receiver);
// need to relock this receiver and sender in the correct order
@ -3375,8 +3374,8 @@ bool QMetaObjectPrivate::disconnect(const QObject *sender,
QObject *s = const_cast<QObject *>(sender);
QMutex *senderMutex = signalSlotLock(sender);
QMutexLocker locker(senderMutex);
QBasicMutex *senderMutex = signalSlotLock(sender);
QBasicMutexLocker locker(senderMutex);
QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.load();
if (!scd)
@ -3523,7 +3522,7 @@ void QMetaObject::connectSlotsByName(QObject *o)
\a signal must be in the signal index range (see QObjectPrivate::signalIndex()).
*/
static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv,
QMutexLocker &locker)
QBasicMutexLocker &locker)
{
const int *argumentTypes = c->argumentTypes.load();
if (!argumentTypes) {
@ -3614,7 +3613,7 @@ void doActivate(QObject *sender, int signal_index, void **argv)
Q_TRACE(QMetaObject_activate_begin_signal, sender, signal_index);
{
QMutexLocker locker(signalSlotLock(sender));
QBasicMutexLocker locker(signalSlotLock(sender));
Q_ASSERT(sp->connections);
QObjectPrivate::ConnectionDataPointer connections(sp->connections.load());
@ -4015,7 +4014,7 @@ void QObject::dumpObjectInfo() const
objectName().isEmpty() ? "unnamed" : objectName().toLocal8Bit().data());
Q_D(const QObject);
QMutexLocker locker(signalSlotLock(this));
QBasicMutexLocker locker(signalSlotLock(this));
// first, look for connections where this object is the sender
qDebug(" SIGNALS OUT");
@ -4845,8 +4844,8 @@ bool QObject::disconnect(const QMetaObject::Connection &connection)
if (!c || !c->receiver)
return false;
QMutex *senderMutex = signalSlotLock(c->sender);
QMutex *receiverMutex = signalSlotLock(c->receiver);
QBasicMutex *senderMutex = signalSlotLock(c->sender);
QBasicMutex *receiverMutex = signalSlotLock(c->receiver);
{
QOrderedMutexLocker locker(senderMutex, receiverMutex);

View File

@ -58,6 +58,8 @@
QT_BEGIN_NAMESPACE
#if QT_CONFIG(thread)
/*
Locks 2 mutexes in a defined order, avoiding a recursive lock if
we're trying to lock the same mutex twice.
@ -65,9 +67,9 @@ QT_BEGIN_NAMESPACE
class QOrderedMutexLocker
{
public:
QOrderedMutexLocker(QMutex *m1, QMutex *m2)
: mtx1((m1 == m2) ? m1 : (std::less<QMutex *>()(m1, m2) ? m1 : m2)),
mtx2((m1 == m2) ? 0 : (std::less<QMutex *>()(m1, m2) ? m2 : m1)),
QOrderedMutexLocker(QBasicMutex *m1, QBasicMutex *m2)
: mtx1((m1 == m2) ? m1 : (std::less<QBasicMutex *>()(m1, m2) ? m1 : m2)),
mtx2((m1 == m2) ? 0 : (std::less<QBasicMutex *>()(m1, m2) ? m2 : m1)),
locked(false)
{
relock();
@ -95,12 +97,12 @@ public:
}
}
static bool relock(QMutex *mtx1, QMutex *mtx2)
static bool relock(QBasicMutex *mtx1, QBasicMutex *mtx2)
{
// mtx1 is already locked, mtx2 not... do we need to unlock and relock?
if (mtx1 == mtx2)
return false;
if (std::less<QMutex *>()(mtx1, mtx2)) {
if (std::less<QBasicMutex *>()(mtx1, mtx2)) {
mtx2->lock();
return true;
}
@ -113,10 +115,58 @@ public:
}
private:
QMutex *mtx1, *mtx2;
QBasicMutex *mtx1, *mtx2;
bool locked;
};
class QBasicMutexLocker
{
public:
inline explicit QBasicMutexLocker(QBasicMutex *m) QT_MUTEX_LOCK_NOEXCEPT
: m(m), isLocked(true)
{
m->lock();
}
inline ~QBasicMutexLocker() { if (isLocked) unlock(); }
inline void unlock() Q_DECL_NOTHROW
{
isLocked = false;
m->unlock();
}
inline void relock() QT_MUTEX_LOCK_NOEXCEPT
{
isLocked = true;
m->lock();
}
private:
Q_DISABLE_COPY(QBasicMutexLocker)
QBasicMutex *m;
bool isLocked;
};
#else
class QOrderedMutexLocker
{
public:
QOrderedMutexLocker(QBasicMutex *, QBasicMutex *) {}
~QOrderedMutexLocker() {}
void relock() {}
void unlock() {}
static bool relock(QBasicMutex *, QBasicMutex *) {}
};
using QBasicMutexLocker = QMutexLocker;
#endif
QT_END_NAMESPACE
#endif