From 59937de098dd86472e73146bd0c84e980022e24d Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Thu, 16 May 2019 18:09:55 +0200 Subject: [PATCH] Annotate the futex implementation for TSAN TSAN does not understand the futex system call. Now, in QMutex and QSemaphore usage, futex is always wrapped by atomic operations that always do an acquire (before waiting) and a release (before waking waiters). That alone realizes a synchronizes-with, and since Qt uses std::atomics, TSAN knows what's going on and does not complain. But what if one uses futex directly, or we change the algorithms, or introduce some other new synchronization primitive somewhere? Luckily TSAN offers annotations for this that we can use. This patch annotates the main entry point for the futex syscall with a pair of acquire/release semantics. A futex call guarantees total ordering on the operations on the futex word(s), whether the call succeeds or fails. Change-Id: Ib80ff898c09fbb6fc73989247eb757bf70971a8a Reviewed-by: Thiago Macieira --- src/corelib/thread/qfutex_p.h | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/corelib/thread/qfutex_p.h b/src/corelib/thread/qfutex_p.h index 136f6bca8c..7bec4554b7 100644 --- a/src/corelib/thread/qfutex_p.h +++ b/src/corelib/thread/qfutex_p.h @@ -81,15 +81,43 @@ QT_END_NAMESPACE // if not defined in linux/futex.h # define FUTEX_PRIVATE_FLAG 128 // added in v2.6.22 +# if QT_HAS_FEATURE(thread_sanitizer) || defined(__SANITIZE_THREAD__) +# include +inline void _q_tsan_acquire(void *addr, void *addr2) +{ + __tsan_acquire(addr); + if (addr2) + __tsan_acquire(addr2); +} +inline void _q_tsan_release(void *addr, void *addr2) +{ + if (addr2) + __tsan_release(addr2); + __tsan_release(addr); +} +# else +inline void _q_tsan_acquire(void *, void *) {} +inline void _q_tsan_release(void *, void *) {} +# endif // QT_HAS_FEATURE(thread_sanitizer) || defined(__SANITIZE_THREAD__) + QT_BEGIN_NAMESPACE namespace QtLinuxFutex { constexpr inline bool futexAvailable() { return true; } inline int _q_futex(int *addr, int op, int val, quintptr val2 = 0, int *addr2 = nullptr, int val3 = 0) noexcept { + // A futex call ensures total ordering on the futex words + // (in either success or failure of the call). Instruct TSAN accordingly, + // as TSAN does not understand the futex(2) syscall. + _q_tsan_release(addr, addr2); + // we use __NR_futex because some libcs (like Android's bionic) don't // provide SYS_futex etc. - return syscall(__NR_futex, addr, op | FUTEX_PRIVATE_FLAG, val, val2, addr2, val3); + int result = syscall(__NR_futex, addr, op | FUTEX_PRIVATE_FLAG, val, val2, addr2, val3); + + _q_tsan_acquire(addr, addr2); + + return result; } template int *addr(T *ptr) {