summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorThiago Macieira <[email protected]>2021-02-22 17:11:42 -0800
committerThiago Macieira <[email protected]>2023-06-18 15:45:37 -0700
commitd2368cde70062bbaa9db463d76cd135dccb55e23 (patch)
treec30701a32b5db62cc4b204b1f20e34f83bebc163 /src
parent674aa084c2d561d2377d8f8a8de1e3e7e60666f6 (diff)
Long live futexes for Darwin!
Like commit 91f6460aff0a6ab5142f16d5f4fc1f559ca1c325 which added support for Windows, this commit does the same for our final major OS. The Darwin kernel exposes a set of __ulock_{wait,wait2,wake} APIs [1], but these APIs are marked as private, so we cannot rely on them being stable, nor we can use these APIs in builds of Qt intended for the Apple App Store. By wholesale disabling the use of the APIs in App Store compliant builds, and runtime checking availability of the APIs when we do build them in, we should be safe, unless the semantics of the APIs change in ways we haven't accounted for, but that's a risk we're willing to take. Note that libc++ uses these private APIs to implement P1135R5 (the C++20 synchronization library) [2], but that use is under a "special permission" from the Darwin team, and meant only for use in the Apple vendored version of libc++ shipped with their operating systems, where they control both the kernel and the standard library. [1] https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.81.2/bsd/sys/ulock.h [2] https://reviews.llvm.org/D68480. Change-Id: Ib709fc1585f647a98d54fffd16663b4965458404 Reviewed-by: Tor Arne Vestbø <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/corelib/CMakeLists.txt1
-rw-r--r--src/corelib/thread/qfutex_mac_p.h140
-rw-r--r--src/corelib/thread/qfutex_p.h4
3 files changed, 144 insertions, 1 deletions
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt
index 9bc5a4380aa..03877853b97 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -674,6 +674,7 @@ qt_internal_extend_target(Core CONDITION QT_FEATURE_thread AND UNIX
qt_internal_extend_target(Core CONDITION APPLE AND QT_FEATURE_thread
SOURCES
+ thread/qfutex_mac_p.h
thread/qmutex_mac.cpp
)
diff --git a/src/corelib/thread/qfutex_mac_p.h b/src/corelib/thread/qfutex_mac_p.h
new file mode 100644
index 00000000000..0de08954abb
--- /dev/null
+++ b/src/corelib/thread/qfutex_mac_p.h
@@ -0,0 +1,140 @@
+// Copyright (C) 2023 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QFUTEX_MAC_P_H
+#define QFUTEX_MAC_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <qdeadlinetimer.h>
+#include <qtsan_impl.h>
+#include <private/qglobal_p.h>
+
+// The Darwin kernel exposes a set of __ulock_{wait,wait2,wake} APIs in
+// https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.81.2/bsd/sys/ulock.h,
+// but these APIs are marked as private, so we cannot rely on them being
+// stable, nor we can use these APIs in builds of Qt intended for
+// the Apple App Store. By wholesale disabling the use of the APIs
+// in App Store compliant builds, and runtime checking availability
+// of the APIs when we do build them in, we should be safe, unless
+// the semantics of the APIs change in ways we haven't accounted for,
+// but that's a risk we're willing to take.
+
+#if QT_CONFIG(appstore_compliant)
+QT_BEGIN_NAMESPACE
+namespace QtFutex = QtDummyFutex;
+QT_END_NAMESPACE
+#else
+
+extern "C" {
+// -------- BEGIN OS Declarations --------
+// Source: https://github.com/apple-oss-distributions/xnu/blob/xnu-8792.81.2/bsd/sys/ulock.h
+// Modification: added __attribute((__weak__))
+// Copyright (c) 2015 Apple Inc. All rights reserved.
+
+__attribute((__weak__))
+extern int __ulock_wait2(uint32_t operation, void *addr, uint64_t value,
+ uint64_t timeout, uint64_t value2);
+__attribute((__weak__))
+extern int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
+
+/*
+ * operation bits [7, 0] contain the operation code.
+ */
+#define UL_COMPARE_AND_WAIT 1
+#define UL_COMPARE_AND_WAIT_SHARED 3
+#define UL_COMPARE_AND_WAIT64 5
+#define UL_COMPARE_AND_WAIT64_SHARED 6
+
+/*
+ * operation bits [15, 8] contain the flags for __ulock_wake
+ */
+#define ULF_WAKE_ALL 0x00000100
+#define ULF_WAKE_THREAD 0x00000200
+#define ULF_WAKE_ALLOW_NON_OWNER 0x00000400
+
+/*
+ * operation bits [15, 8] contain the flags for __ulock_wake
+ */
+#define ULF_WAKE_ALL 0x00000100
+#define ULF_WAKE_THREAD 0x00000200
+#define ULF_WAKE_ALLOW_NON_OWNER 0x00000400
+
+/*
+ * operation bits [31, 24] contain the generic flags
+ */
+#define ULF_NO_ERRNO 0x01000000
+
+// -------- END OS Declarations --------
+} // extern "C"
+
+QT_BEGIN_NAMESPACE
+
+namespace QtDarwinFutex {
+
+/*not constexpr*/ inline bool futexAvailable() { return __ulock_wake && __ulock_wait2; }
+
+template <typename Atomic>
+inline uint32_t baseOperation(Atomic &)
+{
+ static_assert(sizeof(Atomic) >= sizeof(quint32), "Can only operate on 32- or 64-bit atomics");
+
+ uint32_t operation = ULF_NO_ERRNO;
+ if (sizeof(Atomic) == sizeof(quint32))
+ operation |= UL_COMPARE_AND_WAIT;
+ else
+ operation |= UL_COMPARE_AND_WAIT64;
+ return operation;
+}
+
+template <typename Atomic> inline int
+do_wait(Atomic &futex, typename Atomic::Type expectedValue, QDeadlineTimer timer)
+{
+ // source code inspection shows __ulock_wait2 uses nanoseconds for timeout
+ QtTsan::futexRelease(&futex);
+ int ret = __ulock_wait2(baseOperation(futex), &futex, uint64_t(expectedValue),
+ timer.remainingTimeNSecs(), 0);
+ QtTsan::futexAcquire(&futex);
+ return ret;
+}
+
+template <typename Atomic>
+inline void futexWait(Atomic &futex, typename Atomic::Type expectedValue)
+{
+ do_wait(futex, expectedValue, {});
+}
+
+template <typename Atomic>
+inline bool futexWait(Atomic &futex, typename Atomic::Type expectedValue, QDeadlineTimer timer)
+{
+ int r = do_wait(futex, expectedValue, timer);
+ return r == 0 || r != -ETIMEDOUT;
+}
+
+template <typename Atomic> inline void futexWakeAll(Atomic &futex)
+{
+ __ulock_wake(baseOperation(futex) | ULF_WAKE_ALL, &futex, 0);
+}
+
+template <typename Atomic> inline void futexWakeOne(Atomic &futex)
+{
+ __ulock_wake(baseOperation(futex), &futex, 0);
+}
+} //namespace QtDarwinMutex
+
+namespace QtFutex = QtDarwinFutex;
+
+QT_END_NAMESPACE
+
+#endif // QT_CONFIG(appstore_compliant)
+
+#endif // QFUTEX_MAC_P_H
diff --git a/src/corelib/thread/qfutex_p.h b/src/corelib/thread/qfutex_p.h
index d7b91f6d2d7..8ba798f9209 100644
--- a/src/corelib/thread/qfutex_p.h
+++ b/src/corelib/thread/qfutex_p.h
@@ -33,7 +33,9 @@ namespace QtDummyFutex {
QT_END_NAMESPACE
-#if defined(Q_OS_FREEBSD)
+#if defined(Q_OS_DARWIN)
+# include "qfutex_mac_p.h"
+#elif defined(Q_OS_FREEBSD)
# include "qfutex_freebsd_p.h"
#elif defined(Q_OS_LINUX) && !defined(QT_LINUXBASE)
// use Linux mutexes everywhere except for LSB builds