Fix UBSAN warnings
Convert tagged pointer to an encapsulated tagged pointer type, and clean up where it is used. Change-Id: I9cf5b8d1dfa345eebaa185e44d762d5008769480 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
f6d7065093
commit
0cab9b56e9
@ -369,16 +369,16 @@ void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection
|
|||||||
c->prevConnectionList->nextConnectionList.storeRelaxed(n);
|
c->prevConnectionList->nextConnectionList.storeRelaxed(n);
|
||||||
c->prevConnectionList = nullptr;
|
c->prevConnectionList = nullptr;
|
||||||
|
|
||||||
Q_ASSERT(c != orphaned.loadRelaxed());
|
Q_ASSERT(c != static_cast<Connection *>(orphaned.load(std::memory_order_relaxed)));
|
||||||
// add c to orphanedConnections
|
// add c to orphanedConnections
|
||||||
Connection *o = nullptr;
|
TaggedSignalVector o = nullptr;
|
||||||
/* No ABA issue here: When adding a node, we only care about the list head, it doesn't
|
/* No ABA issue here: When adding a node, we only care about the list head, it doesn't
|
||||||
* matter if the tail changes.
|
* matter if the tail changes.
|
||||||
*/
|
*/
|
||||||
|
o = orphaned.load(std::memory_order_acquire);
|
||||||
do {
|
do {
|
||||||
o = orphaned.loadRelaxed();
|
|
||||||
c->nextInOrphanList = o;
|
c->nextInOrphanList = o;
|
||||||
} while (!orphaned.testAndSetRelease(o, c));
|
} while (!orphaned.compare_exchange_strong(o, TaggedSignalVector(c), std::memory_order_release));
|
||||||
|
|
||||||
#ifndef QT_NO_DEBUG
|
#ifndef QT_NO_DEBUG
|
||||||
found = false;
|
found = false;
|
||||||
@ -396,7 +396,7 @@ void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection
|
|||||||
void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy)
|
void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy)
|
||||||
{
|
{
|
||||||
QBasicMutex *senderMutex = signalSlotLock(sender);
|
QBasicMutex *senderMutex = signalSlotLock(sender);
|
||||||
ConnectionOrSignalVector *c = nullptr;
|
TaggedSignalVector c = nullptr;
|
||||||
{
|
{
|
||||||
std::unique_lock<QBasicMutex> lock(*senderMutex, std::defer_lock_t{});
|
std::unique_lock<QBasicMutex> lock(*senderMutex, std::defer_lock_t{});
|
||||||
if (lockPolicy == NeedToLock)
|
if (lockPolicy == NeedToLock)
|
||||||
@ -407,7 +407,7 @@ void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sende
|
|||||||
// Since ref == 1, no activate() is in process since we locked the mutex. That implies,
|
// Since ref == 1, no activate() is in process since we locked the mutex. That implies,
|
||||||
// that nothing can reference the orphaned connection objects anymore and they can
|
// that nothing can reference the orphaned connection objects anymore and they can
|
||||||
// be safely deleted
|
// be safely deleted
|
||||||
c = orphaned.fetchAndStoreRelaxed(nullptr);
|
c = orphaned.exchange(nullptr, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
if (c) {
|
if (c) {
|
||||||
// Deleting c might run arbitrary user code, so we must not hold the lock
|
// Deleting c might run arbitrary user code, so we must not hold the lock
|
||||||
@ -421,11 +421,11 @@ void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sende
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void QObjectPrivate::ConnectionData::deleteOrphaned(QObjectPrivate::ConnectionOrSignalVector *o)
|
inline void QObjectPrivate::ConnectionData::deleteOrphaned(TaggedSignalVector o)
|
||||||
{
|
{
|
||||||
while (o) {
|
while (o) {
|
||||||
QObjectPrivate::ConnectionOrSignalVector *next = nullptr;
|
TaggedSignalVector next = nullptr;
|
||||||
if (SignalVector *v = ConnectionOrSignalVector::asSignalVector(o)) {
|
if (SignalVector *v = static_cast<SignalVector *>(o)) {
|
||||||
next = v->nextInOrphanList;
|
next = v->nextInOrphanList;
|
||||||
free(v);
|
free(v);
|
||||||
} else {
|
} else {
|
||||||
|
@ -111,6 +111,7 @@ public:
|
|||||||
struct ConnectionOrSignalVector;
|
struct ConnectionOrSignalVector;
|
||||||
struct SignalVector;
|
struct SignalVector;
|
||||||
struct Sender;
|
struct Sender;
|
||||||
|
struct TaggedSignalVector;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This contains the all connections from and to an object.
|
This contains the all connections from and to an object.
|
||||||
|
@ -34,25 +34,35 @@ struct QObjectPrivate::ConnectionList
|
|||||||
static_assert(std::is_trivially_destructible_v<QObjectPrivate::ConnectionList>);
|
static_assert(std::is_trivially_destructible_v<QObjectPrivate::ConnectionList>);
|
||||||
Q_DECLARE_TYPEINFO(QObjectPrivate::ConnectionList, Q_RELOCATABLE_TYPE);
|
Q_DECLARE_TYPEINFO(QObjectPrivate::ConnectionList, Q_RELOCATABLE_TYPE);
|
||||||
|
|
||||||
|
struct QObjectPrivate::TaggedSignalVector
|
||||||
|
{
|
||||||
|
quintptr c;
|
||||||
|
|
||||||
|
TaggedSignalVector() = default;
|
||||||
|
TaggedSignalVector(std::nullptr_t) noexcept : c(0) {}
|
||||||
|
TaggedSignalVector(Connection *v) noexcept : c(reinterpret_cast<quintptr>(v)) { Q_ASSERT(v && (reinterpret_cast<quintptr>(v) & 0x1) == 0); }
|
||||||
|
TaggedSignalVector(SignalVector *v) noexcept : c(reinterpret_cast<quintptr>(v) | quintptr(1u)) { Q_ASSERT(v); }
|
||||||
|
explicit operator SignalVector *() const noexcept
|
||||||
|
{
|
||||||
|
if (c & 0x1)
|
||||||
|
return reinterpret_cast<SignalVector *>(c & ~quintptr(1u));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
explicit operator Connection *() const noexcept
|
||||||
|
{
|
||||||
|
return reinterpret_cast<Connection *>(c);
|
||||||
|
}
|
||||||
|
operator uintptr_t() const noexcept { return c; }
|
||||||
|
};
|
||||||
|
|
||||||
struct QObjectPrivate::ConnectionOrSignalVector
|
struct QObjectPrivate::ConnectionOrSignalVector
|
||||||
{
|
{
|
||||||
union {
|
union {
|
||||||
// linked list of orphaned connections that need cleaning up
|
// linked list of orphaned connections that need cleaning up
|
||||||
ConnectionOrSignalVector *nextInOrphanList;
|
TaggedSignalVector nextInOrphanList;
|
||||||
// linked list of connections connected to slots in this object
|
// linked list of connections connected to slots in this object
|
||||||
Connection *next;
|
Connection *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
static SignalVector *asSignalVector(ConnectionOrSignalVector *c)
|
|
||||||
{
|
|
||||||
if (reinterpret_cast<quintptr>(c) & 1)
|
|
||||||
return reinterpret_cast<SignalVector *>(reinterpret_cast<quintptr>(c) & ~quintptr(1u));
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
static Connection *fromSignalVector(SignalVector *v)
|
|
||||||
{
|
|
||||||
return reinterpret_cast<Connection *>(reinterpret_cast<quintptr>(v) | quintptr(1u));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
static_assert(std::is_trivial_v<QObjectPrivate::ConnectionOrSignalVector>);
|
static_assert(std::is_trivial_v<QObjectPrivate::ConnectionOrSignalVector>);
|
||||||
|
|
||||||
@ -132,12 +142,12 @@ struct QObjectPrivate::ConnectionData
|
|||||||
QAtomicPointer<SignalVector> signalVector;
|
QAtomicPointer<SignalVector> signalVector;
|
||||||
Connection *senders = nullptr;
|
Connection *senders = nullptr;
|
||||||
Sender *currentSender = nullptr; // object currently activating the object
|
Sender *currentSender = nullptr; // object currently activating the object
|
||||||
QAtomicPointer<Connection> orphaned;
|
std::atomic<TaggedSignalVector> orphaned = {};
|
||||||
|
|
||||||
~ConnectionData()
|
~ConnectionData()
|
||||||
{
|
{
|
||||||
Q_ASSERT(ref.loadRelaxed() == 0);
|
Q_ASSERT(ref.loadRelaxed() == 0);
|
||||||
auto *c = orphaned.fetchAndStoreRelaxed(nullptr);
|
TaggedSignalVector c = orphaned.exchange(nullptr, std::memory_order_relaxed);
|
||||||
if (c)
|
if (c)
|
||||||
deleteOrphaned(c);
|
deleteOrphaned(c);
|
||||||
SignalVector *v = signalVector.loadRelaxed();
|
SignalVector *v = signalVector.loadRelaxed();
|
||||||
@ -159,7 +169,7 @@ struct QObjectPrivate::ConnectionData
|
|||||||
};
|
};
|
||||||
void cleanOrphanedConnections(QObject *sender, LockPolicy lockPolicy = NeedToLock)
|
void cleanOrphanedConnections(QObject *sender, LockPolicy lockPolicy = NeedToLock)
|
||||||
{
|
{
|
||||||
if (orphaned.loadRelaxed() && ref.loadAcquire() == 1)
|
if (orphaned.load(std::memory_order_relaxed) && ref.loadAcquire() == 1)
|
||||||
cleanOrphanedConnectionsImpl(sender, lockPolicy);
|
cleanOrphanedConnectionsImpl(sender, lockPolicy);
|
||||||
}
|
}
|
||||||
void cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy);
|
void cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy);
|
||||||
@ -194,15 +204,14 @@ struct QObjectPrivate::ConnectionData
|
|||||||
|
|
||||||
signalVector.storeRelaxed(newVector);
|
signalVector.storeRelaxed(newVector);
|
||||||
if (vector) {
|
if (vector) {
|
||||||
Connection *o = nullptr;
|
TaggedSignalVector o = nullptr;
|
||||||
/* No ABA issue here: When adding a node, we only care about the list head, it doesn't
|
/* No ABA issue here: When adding a node, we only care about the list head, it doesn't
|
||||||
* matter if the tail changes.
|
* matter if the tail changes.
|
||||||
*/
|
*/
|
||||||
|
o = orphaned.load(std::memory_order_acquire);
|
||||||
do {
|
do {
|
||||||
o = orphaned.loadRelaxed();
|
|
||||||
vector->nextInOrphanList = o;
|
vector->nextInOrphanList = o;
|
||||||
} while (!orphaned.testAndSetRelease(
|
} while (!orphaned.compare_exchange_strong(o, TaggedSignalVector(vector), std::memory_order_release));
|
||||||
o, ConnectionOrSignalVector::fromSignalVector(vector)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int signalVectorCount() const
|
int signalVectorCount() const
|
||||||
@ -210,7 +219,7 @@ struct QObjectPrivate::ConnectionData
|
|||||||
return signalVector.loadAcquire() ? signalVector.loadRelaxed()->count() : -1;
|
return signalVector.loadAcquire() ? signalVector.loadRelaxed()->count() : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void deleteOrphaned(ConnectionOrSignalVector *c);
|
static void deleteOrphaned(TaggedSignalVector o);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct QObjectPrivate::Sender
|
struct QObjectPrivate::Sender
|
||||||
|
Loading…
x
Reference in New Issue
Block a user