forked from mirrors/gecko-dev
Bug 1774458 - Use undocumented, non-public adaptive spinlocks on macOS 10.15+, revert to user-space spinlocks on older versions r=pbone
Differential Revision: https://phabricator.services.mozilla.com/D149599
This commit is contained in:
parent
8592d3250f
commit
7e8fda1a8b
3 changed files with 85 additions and 8 deletions
21
memory/build/Mutex.cpp
Normal file
21
memory/build/Mutex.cpp
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#include "Mutex.h"
|
||||||
|
|
||||||
|
#if defined(XP_DARWIN)
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool Mutex::UseUnfairLocks() {
|
||||||
|
if (__builtin_available(macOS 10.15, *)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool Mutex::gFallbackToOSSpinLock = !UseUnfairLocks();
|
||||||
|
|
||||||
|
#endif // defined(XP_DARWIN)
|
||||||
|
|
@ -10,21 +10,48 @@
|
||||||
#if defined(XP_WIN)
|
#if defined(XP_WIN)
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
#elif defined(XP_DARWIN)
|
#elif defined(XP_DARWIN)
|
||||||
|
# include <libkern/OSAtomic.h>
|
||||||
# include <os/lock.h>
|
# include <os/lock.h>
|
||||||
#else
|
#else
|
||||||
# include <pthread.h>
|
# include <pthread.h>
|
||||||
#endif
|
#endif
|
||||||
#include "mozilla/Attributes.h"
|
#include "mozilla/Attributes.h"
|
||||||
|
|
||||||
|
#if defined(XP_DARWIN)
|
||||||
|
// For information about the following undocumented flags and functions see
|
||||||
|
// https://github.com/apple/darwin-xnu/blob/main/bsd/sys/ulock.h and
|
||||||
|
// https://github.com/apple/darwin-libplatform/blob/main/private/os/lock_private.h
|
||||||
|
# define OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION (0x00010000)
|
||||||
|
# define OS_UNFAIR_LOCK_ADAPTIVE_SPIN (0x00040000)
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
|
||||||
|
typedef uint32_t os_unfair_lock_options_t;
|
||||||
|
OS_UNFAIR_LOCK_AVAILABILITY
|
||||||
|
OS_EXPORT OS_NOTHROW OS_NONNULL_ALL void os_unfair_lock_lock_with_options(
|
||||||
|
os_unfair_lock_t lock, os_unfair_lock_options_t options);
|
||||||
|
}
|
||||||
|
|
||||||
|
static_assert(OS_UNFAIR_LOCK_INIT._os_unfair_lock_opaque == OS_SPINLOCK_INIT,
|
||||||
|
"OS_UNFAIR_LOCK_INIT and OS_SPINLOCK_INIT have the same "
|
||||||
|
"value");
|
||||||
|
static_assert(sizeof(os_unfair_lock) == sizeof(OSSpinLock),
|
||||||
|
"os_unfair_lock and OSSpinLock are the same size");
|
||||||
|
#endif // defined(XP_DARWIN)
|
||||||
|
|
||||||
// Mutexes based on spinlocks. We can't use normal pthread spinlocks in all
|
// Mutexes based on spinlocks. We can't use normal pthread spinlocks in all
|
||||||
// places, because they require malloc()ed memory, which causes bootstrapping
|
// places, because they require malloc()ed memory, which causes
|
||||||
// issues in some cases. We also can't use constructors, because for statics,
|
// bootstrapping issues in some cases. We also can't use constructors,
|
||||||
// they would fire after the first use of malloc, resetting the locks.
|
// because for statics, they would fire after the first use of malloc,
|
||||||
|
// resetting the locks.
|
||||||
struct Mutex {
|
struct Mutex {
|
||||||
#if defined(XP_WIN)
|
#if defined(XP_WIN)
|
||||||
CRITICAL_SECTION mMutex;
|
CRITICAL_SECTION mMutex;
|
||||||
#elif defined(XP_DARWIN)
|
#elif defined(XP_DARWIN)
|
||||||
os_unfair_lock mMutex;
|
union {
|
||||||
|
os_unfair_lock mUnfairLock;
|
||||||
|
OSSpinLock mSpinLock;
|
||||||
|
} mMutex;
|
||||||
#else
|
#else
|
||||||
pthread_mutex_t mMutex;
|
pthread_mutex_t mMutex;
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -36,7 +63,10 @@ struct Mutex {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#elif defined(XP_DARWIN)
|
#elif defined(XP_DARWIN)
|
||||||
mMutex = OS_UNFAIR_LOCK_INIT;
|
// The hack below works because both OS_UNFAIR_LOCK_INIT and
|
||||||
|
// OS_SPINLOCK_INIT initialize the lock to 0 and in both case it's a 32-bit
|
||||||
|
// integer.
|
||||||
|
mMutex.mUnfairLock = OS_UNFAIR_LOCK_INIT;
|
||||||
#elif defined(XP_LINUX) && !defined(ANDROID)
|
#elif defined(XP_LINUX) && !defined(ANDROID)
|
||||||
pthread_mutexattr_t attr;
|
pthread_mutexattr_t attr;
|
||||||
if (pthread_mutexattr_init(&attr) != 0) {
|
if (pthread_mutexattr_init(&attr) != 0) {
|
||||||
|
|
@ -60,7 +90,20 @@ struct Mutex {
|
||||||
#if defined(XP_WIN)
|
#if defined(XP_WIN)
|
||||||
EnterCriticalSection(&mMutex);
|
EnterCriticalSection(&mMutex);
|
||||||
#elif defined(XP_DARWIN)
|
#elif defined(XP_DARWIN)
|
||||||
os_unfair_lock_lock(&mMutex);
|
if (Mutex::gFallbackToOSSpinLock) {
|
||||||
|
OSSpinLockLock(&mMutex.mSpinLock);
|
||||||
|
} else {
|
||||||
|
// We rely on a non-public function to improve performance here.
|
||||||
|
// The OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION flag informs the kernel that
|
||||||
|
// the calling thread is able to make progress even in absence of actions
|
||||||
|
// from other threads and the OS_UNFAIR_LOCK_ADAPTIVE_SPIN one causes the
|
||||||
|
// kernel to spin on a contested lock if the owning thread is running on
|
||||||
|
// the same physical core (presumably only on x86 CPUs given that ARM
|
||||||
|
// macs don't have cores capable of SMT).
|
||||||
|
os_unfair_lock_lock_with_options(
|
||||||
|
&mMutex.mUnfairLock,
|
||||||
|
OS_UNFAIR_LOCK_DATA_SYNCHRONIZATION | OS_UNFAIR_LOCK_ADAPTIVE_SPIN);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
pthread_mutex_lock(&mMutex);
|
pthread_mutex_lock(&mMutex);
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -70,11 +113,20 @@ struct Mutex {
|
||||||
#if defined(XP_WIN)
|
#if defined(XP_WIN)
|
||||||
LeaveCriticalSection(&mMutex);
|
LeaveCriticalSection(&mMutex);
|
||||||
#elif defined(XP_DARWIN)
|
#elif defined(XP_DARWIN)
|
||||||
os_unfair_lock_unlock(&mMutex);
|
if (Mutex::gFallbackToOSSpinLock) {
|
||||||
|
OSSpinLockUnlock(&mMutex.mSpinLock);
|
||||||
|
} else {
|
||||||
|
os_unfair_lock_unlock(&mMutex.mUnfairLock);
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
pthread_mutex_unlock(&mMutex);
|
pthread_mutex_unlock(&mMutex);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(XP_DARWIN)
|
||||||
|
static bool UseUnfairLocks();
|
||||||
|
static bool gFallbackToOSSpinLock;
|
||||||
|
#endif // XP_DARWIN
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mutex that can be used for static initialization.
|
// Mutex that can be used for static initialization.
|
||||||
|
|
@ -101,7 +153,10 @@ struct StaticMutex {
|
||||||
typedef Mutex StaticMutex;
|
typedef Mutex StaticMutex;
|
||||||
|
|
||||||
# if defined(XP_DARWIN)
|
# if defined(XP_DARWIN)
|
||||||
# define STATIC_MUTEX_INIT OS_UNFAIR_LOCK_INIT
|
// The hack below works because both OS_UNFAIR_LOCK_INIT and OS_SPINLOCK_INIT
|
||||||
|
// initialize the lock to 0 and in both case it's a 32-bit integer.
|
||||||
|
# define STATIC_MUTEX_INIT \
|
||||||
|
{ .mUnfairLock = OS_UNFAIR_LOCK_INIT }
|
||||||
# elif defined(XP_LINUX) && !defined(ANDROID)
|
# elif defined(XP_LINUX) && !defined(ANDROID)
|
||||||
# define STATIC_MUTEX_INIT PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
|
# define STATIC_MUTEX_INIT PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP
|
||||||
# else
|
# else
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ if CONFIG["OS_TARGET"] == "Darwin" and (
|
||||||
CONFIG["MOZ_REPLACE_MALLOC"] or CONFIG["MOZ_MEMORY"]
|
CONFIG["MOZ_REPLACE_MALLOC"] or CONFIG["MOZ_MEMORY"]
|
||||||
):
|
):
|
||||||
SOURCES += [
|
SOURCES += [
|
||||||
|
"Mutex.cpp",
|
||||||
"zone.c",
|
"zone.c",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue