Q{Semaphore,ReadWriteLock}Private: reorganize the members

They are now ordered so that the mutex and the wait condition are in
different cache lines, to avoid false sharing situations, if the types
are holding the actual threading primitive structures, not mere pointers
to some other structure elsewhere.

For 64-bit systems:

OS                  | mutex |  cond | Remark
--------------------+-------+-------+------------------
Darwin              |    64 |    48 |
FreeBSD             |     8 |     8 |
INTEGRITY           |     8 |     8 | QMutex & QWaitCondition
Linux               |    24 |    48 | Always uses futex
MinGW (Winpthreads) |     8 |     8 | Always uses futex
MSVC (MS STL)       |    32 |    16 | Always uses futex
NetBSD              |    48 |    40 |
OpenBSD             |     8 |     8 |
QNX                 |     ? |     ? |

Change-Id: I63b988479db546dabffcfffd176698e4f0097e90
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Thiago Macieira 2023-06-07 22:46:59 -07:00
parent 0f9894f79b
commit fbc491230f
3 changed files with 35 additions and 9 deletions

View File

@ -45,9 +45,10 @@ public:
explicit QReadWriteLockPrivate(bool isRecursive = false)
: recursive(isRecursive) {}
QtPrivate::mutex mutex;
QtPrivate::condition_variable writerCond;
alignas(QtPrivate::IdealMutexAlignment) QtPrivate::condition_variable writerCond;
QtPrivate::condition_variable readerCond;
alignas(QtPrivate::IdealMutexAlignment) QtPrivate::mutex mutex;
int readerCount = 0;
int writerCount = 0;
int waitingReaders = 0;

View File

@ -251,14 +251,31 @@ futexSemaphoreTryAcquire(QBasicAtomicInteger<quintptr> &u, int n, T timeout)
return false;
}
class QSemaphorePrivate {
namespace { namespace QtSemaphorePrivate {
using namespace QtPrivate;
struct Layout1
{
alignas(IdealMutexAlignment) QtPrivate::mutex mutex;
qsizetype avail = 0;
alignas(IdealMutexAlignment) QtPrivate::condition_variable cond;
};
struct Layout2
{
alignas(IdealMutexAlignment) QtPrivate::mutex mutex;
alignas(IdealMutexAlignment) QtPrivate::condition_variable cond;
qsizetype avail = 0;
};
// Choose Layout1 if it is smaller than Layout2. That happens for platforms
// where sizeof(mutex) is 64.
using Members = std::conditional_t<sizeof(Layout1) <= sizeof(Layout2), Layout1, Layout2>;
} } // namespace QtSemaphorePrivate
class QSemaphorePrivate : public QtSemaphorePrivate::Members
{
public:
explicit QSemaphorePrivate(int n) : avail(n) { }
QtPrivate::mutex mutex;
QtPrivate::condition_variable cond;
int avail;
explicit QSemaphorePrivate(qsizetype n) { avail = n; }
};
/*!

View File

@ -123,6 +123,14 @@ using condition_variable = std::condition_variable;
#endif // C++11 threads
// Ideal alignment for mutex and condition_variable: it's the hardware
// interference size (size of a cache line) if the types are likely to contain
// the actual data structures, otherwise just that of a pointer.
static constexpr quintptr IdealMutexAlignment =
sizeof(QtPrivate::mutex) > sizeof(void *) &&
sizeof(QtPrivate::condition_variable) > sizeof(void *) ?
64 : alignof(void*);
} // namespace QtPrivate
QT_END_NAMESPACE