summaryrefslogtreecommitdiffstats
path: root/src/corelib
diff options
context:
space:
mode:
authorBradley T. Hughes <bradley.hughes@nokia.com>2010-09-27 13:19:45 (GMT)
committerBradley T. Hughes <bradley.hughes@nokia.com>2010-12-20 15:49:49 (GMT)
commit8a7b5aca7b6575013a4e4ee9b99808d25edf6fdf (patch)
tree0389aa78b05c7518c8c8ff8b2dab193cc4779390 /src/corelib
parentcf17b743d2fe84ab259b7232ab07b58a1872e18e (diff)
downloadQt-8a7b5aca7b6575013a4e4ee9b99808d25edf6fdf.zip
Qt-8a7b5aca7b6575013a4e4ee9b99808d25edf6fdf.tar.gz
Qt-8a7b5aca7b6575013a4e4ee9b99808d25edf6fdf.tar.bz2
Improve QMutex contention performance on Linux
Use futex(2) to implement QMutexPrivate::wait() and ::wakeup(). This makes QMutex perform more or less identically the same as pthread_mutex_t when contended. We have to use the contender count in a different way due to the way that futex() waiting works. Waiting on a futex atomically checks that the value has not changed from the expected value and then puts the thread to sleep. So, on Linux, the contender QAtomicInt will only ever be one of three values: 0 for unlocked, 1 for locked, 2 for contended. This does mean that there is a slight chance for unfairness due to the fetch-and-store to zero in the wakeUp() function, but unfortunately this cannot be avoid as the code is now. Reviewed-by: joao
Diffstat (limited to 'src/corelib')
-rw-r--r--src/corelib/thread/qmutex_p.h2
-rw-r--r--src/corelib/thread/qmutex_unix.cpp43
2 files changed, 39 insertions, 6 deletions
diff --git a/src/corelib/thread/qmutex_p.h b/src/corelib/thread/qmutex_p.h
index e23be94..2d45cfb 100644
--- a/src/corelib/thread/qmutex_p.h
+++ b/src/corelib/thread/qmutex_p.h
@@ -78,7 +78,7 @@ public:
#if defined(Q_OS_MAC)
semaphore_t mach_semaphore;
-#elif defined(Q_OS_UNIX)
+#elif defined(Q_OS_UNIX) && !defined(Q_OS_LINUX)
volatile bool wakeup;
pthread_mutex_t mutex;
pthread_cond_t cond;
diff --git a/src/corelib/thread/qmutex_unix.cpp b/src/corelib/thread/qmutex_unix.cpp
index ce1bfc2..e872187 100644
--- a/src/corelib/thread/qmutex_unix.cpp
+++ b/src/corelib/thread/qmutex_unix.cpp
@@ -56,11 +56,15 @@
#if defined(Q_OS_MAC)
# include <mach/mach.h>
# include <mach/task.h>
+#elif defined(Q_OS_LINUX)
+# include <linux/futex.h>
+# include <sys/syscall.h>
+# include <unistd.h>
#endif
QT_BEGIN_NAMESPACE
-#if !defined(Q_OS_MAC)
+#if !defined(Q_OS_MAC) && !defined(Q_OS_LINUX)
static void report_error(int code, const char *where, const char *what)
{
if (code != 0)
@@ -76,7 +80,7 @@ QMutexPrivate::QMutexPrivate(QMutex::RecursionMode mode)
kern_return_t r = semaphore_create(mach_task_self(), &mach_semaphore, SYNC_POLICY_FIFO, 0);
if (r != KERN_SUCCESS)
qWarning("QMutex: failed to create semaphore, error %d", r);
-#else
+#elif !defined(Q_OS_LINUX)
wakeup = false;
report_error(pthread_mutex_init(&mutex, NULL), "QMutex", "mutex init");
report_error(pthread_cond_init(&cond, NULL), "QMutex", "cv init");
@@ -89,7 +93,7 @@ QMutexPrivate::~QMutexPrivate()
kern_return_t r = semaphore_destroy(mach_task_self(), mach_semaphore);
if (r != KERN_SUCCESS)
qWarning("QMutex: failed to destroy semaphore, error %d", r);
-#else
+#elif !defined(Q_OS_LINUX)
report_error(pthread_cond_destroy(&cond), "QMutex", "cv destroy");
report_error(pthread_mutex_destroy(&mutex), "QMutex", "mutex destroy");
#endif
@@ -122,7 +126,36 @@ void QMutexPrivate::wakeUp()
semaphore_signal(mach_semaphore);
}
-#else // !Q_OS_MAC
+#elif defined(Q_OS_LINUX)
+
+static inline int _q_futex(volatile int *addr, int op, int val, const struct timespec *timeout, int *addr2, int val2)
+{
+ return syscall(SYS_futex, addr, op, val, timeout, addr2, val2);
+}
+
+bool QMutexPrivate::wait(int timeout)
+{
+ while (contenders.fetchAndStoreAcquire(2) > 0) {
+ struct timespec ts, *pts = 0;
+ if (timeout >= 0) {
+ ts.tv_nsec = ((timeout % 1000) * 1000) * 1000;
+ ts.tv_sec = (timeout / 1000);
+ pts = &ts;
+ }
+ int r = _q_futex(&contenders._q_value, FUTEX_WAIT, 2, pts, 0, 0);
+ if (r != 0 && errno == ETIMEDOUT)
+ return false;
+ }
+ return true;
+}
+
+void QMutexPrivate::wakeUp()
+{
+ (void) contenders.fetchAndStoreRelease(0);
+ (void) _q_futex(&contenders._q_value, FUTEX_WAKE, 1, 0, 0, 0);
+}
+
+#else // !Q_OS_MAC && !Q_OS_LINUX
bool QMutexPrivate::wait(int timeout)
{
@@ -166,7 +199,7 @@ void QMutexPrivate::wakeUp()
report_error(pthread_mutex_unlock(&mutex), "QMutex::unlock", "mutex unlock");
}
-#endif // !Q_OS_MAC
+#endif // !Q_OS_MAC && !Q_OS_LINUX
QT_END_NAMESPACE