1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
15 #if defined(XP_DARWIN)
19 #include "mozilla/Assertions.h"
20 #include "mozilla/Attributes.h"
21 #include "mozilla/ThreadSafety.h"
23 #if defined(XP_DARWIN)
24 // For information about the following undocumented flags and functions see
25 // https://github.com/apple/darwin-xnu/blob/main/bsd/sys/ulock.h and
26 // https://github.com/apple/darwin-libplatform/blob/main/private/os/lock_private.h
27 # define OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION (0x00010000)
28 # define OS_UNFAIR_LOCK_ADAPTIVE_SPIN (0x00040000)
32 typedef uint32_t os_unfair_lock_options_t
;
33 OS_UNFAIR_LOCK_AVAILABILITY
34 OS_EXPORT OS_NOTHROW OS_NONNULL_ALL
void os_unfair_lock_lock_with_options(
35 os_unfair_lock_t lock
, os_unfair_lock_options_t options
);
37 #endif // defined(XP_DARWIN)
39 // Mutexes based on spinlocks. We can't use normal pthread spinlocks in all
40 // places, because they require malloc()ed memory, which causes bootstrapping
41 // issues in some cases. We also can't use constructors, because for statics,
42 // they would fire after the first use of malloc, resetting the locks.
43 struct MOZ_CAPABILITY("mutex") Mutex
{
45 CRITICAL_SECTION mMutex
;
46 #elif defined(XP_DARWIN)
47 os_unfair_lock mMutex
;
49 pthread_mutex_t mMutex
;
52 // Initializes a mutex. Returns whether initialization succeeded.
55 if (!InitializeCriticalSectionAndSpinCount(&mMutex
, 5000)) {
58 #elif defined(XP_DARWIN)
59 mMutex
= OS_UNFAIR_LOCK_INIT
;
60 #elif defined(XP_LINUX) && !defined(ANDROID)
61 pthread_mutexattr_t attr
;
62 if (pthread_mutexattr_init(&attr
) != 0) {
65 pthread_mutexattr_settype(&attr
, PTHREAD_MUTEX_ADAPTIVE_NP
);
66 if (pthread_mutex_init(&mMutex
, &attr
) != 0) {
67 pthread_mutexattr_destroy(&attr
);
70 pthread_mutexattr_destroy(&attr
);
72 if (pthread_mutex_init(&mMutex
, nullptr) != 0) {
79 inline void Lock() MOZ_CAPABILITY_ACQUIRE() {
81 EnterCriticalSection(&mMutex
);
82 #elif defined(XP_DARWIN)
83 // We rely on a non-public function to improve performance here.
84 // The OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION flag informs the kernel that
85 // the calling thread is able to make progress even in absence of actions
86 // from other threads and the OS_UNFAIR_LOCK_ADAPTIVE_SPIN one causes the
87 // kernel to spin on a contested lock if the owning thread is running on
88 // the same physical core (presumably only on x86 CPUs given that ARM
89 // macs don't have cores capable of SMT).
90 os_unfair_lock_lock_with_options(
92 OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION
| OS_UNFAIR_LOCK_ADAPTIVE_SPIN
);
94 pthread_mutex_lock(&mMutex
);
98 [[nodiscard
]] bool TryLock() MOZ_TRY_ACQUIRE(true);
100 inline void Unlock() MOZ_CAPABILITY_RELEASE() {
102 LeaveCriticalSection(&mMutex
);
103 #elif defined(XP_DARWIN)
104 os_unfair_lock_unlock(&mMutex
);
106 pthread_mutex_unlock(&mMutex
);
110 #if defined(XP_DARWIN)
111 static bool SpinInKernelSpace();
112 static const bool gSpinInKernelSpace
;
116 // Mutex that can be used for static initialization.
117 // On Windows, CRITICAL_SECTION requires a function call to be initialized,
118 // but for the initialization lock, a static initializer calling the
119 // function would be called too late. We need no-function-call
120 // initialization, which SRWLock provides.
121 // Ideally, we'd use the same type of locks everywhere, but SRWLocks
122 // everywhere incur a performance penalty. See bug 1418389.
124 struct MOZ_CAPABILITY("mutex") StaticMutex
{
127 inline void Lock() MOZ_CAPABILITY_ACQUIRE() {
128 AcquireSRWLockExclusive(&mMutex
);
131 inline void Unlock() MOZ_CAPABILITY_RELEASE() {
132 ReleaseSRWLockExclusive(&mMutex
);
136 // Normally, we'd use a constexpr constructor, but MSVC likes to create
137 // static initializers anyways.
138 # define STATIC_MUTEX_INIT SRWLOCK_INIT
141 typedef Mutex StaticMutex
;
143 # if defined(XP_DARWIN)
144 # define STATIC_MUTEX_INIT OS_UNFAIR_LOCK_INIT
145 # elif defined(XP_LINUX) && !defined(ANDROID)
146 # define STATIC_MUTEX_INIT PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
148 # define STATIC_MUTEX_INIT PTHREAD_MUTEX_INITIALIZER
154 typedef DWORD ThreadId
;
155 inline ThreadId
GetThreadId() { return GetCurrentThreadId(); }
157 typedef pthread_t ThreadId
;
158 inline ThreadId
GetThreadId() { return pthread_self(); }
161 class MOZ_CAPABILITY("mutex") MaybeMutex
: public Mutex
{
168 bool Init(DoLock aDoLock
) {
171 mThreadId
= GetThreadId();
173 return Mutex::Init();
177 // Re initialise after fork(), assumes that mDoLock is already initialised.
178 void Reinit(pthread_t aForkingThread
) {
179 if (mDoLock
== MUST_LOCK
) {
184 // If this is an eluded lock we can only safely re-initialise it if the
185 // thread that called fork is the one that owns the lock.
186 if (pthread_equal(mThreadId
, aForkingThread
)) {
187 mThreadId
= GetThreadId();
190 // We can't guantee that whatever resource this lock protects (probably a
191 // jemalloc arena) is in a consistent state.
192 mDeniedAfterFork
= true;
198 inline void Lock() MOZ_CAPABILITY_ACQUIRE() {
204 inline void Unlock() MOZ_CAPABILITY_RELEASE() {
210 // Return true if we can use this resource from this thread, either because
211 // we'll use the lock or because this is the only thread that will access the
212 // protected resource.
214 bool SafeOnThisThread() const {
215 return mDoLock
== MUST_LOCK
|| GetThreadId() == mThreadId
;
219 bool LockIsEnabled() const { return mDoLock
== MUST_LOCK
; }
224 MOZ_ASSERT(!mDeniedAfterFork
);
227 if (mDoLock
== MUST_LOCK
) {
231 MOZ_ASSERT(GetThreadId() == mThreadId
);
239 bool mDeniedAfterFork
= false;
244 template <typename T
>
245 struct MOZ_SCOPED_CAPABILITY MOZ_RAII AutoLock
{
246 explicit AutoLock(T
& aMutex
) MOZ_CAPABILITY_ACQUIRE(aMutex
) : mMutex(aMutex
) {
250 ~AutoLock() MOZ_CAPABILITY_RELEASE() { mMutex
.Unlock(); }
252 AutoLock(const AutoLock
&) = delete;
253 AutoLock(AutoLock
&&) = delete;
259 using MutexAutoLock
= AutoLock
<Mutex
>;
261 using MaybeMutexAutoLock
= AutoLock
<MaybeMutex
>;