author | Jon Coppeard <jcoppeard@mozilla.com> |
Wed, 02 May 2018 15:11:53 +0100 | |
changeset 416713 | ef231f04c4ec6710dfb179a93cd526e019fc09a6 |
parent 416712 | 2a7aef0304b22b0dd3efbfb105ede8ea515d4fb0 |
child 416714 | 3ab2fbb175bd7fa720d4e990dbaaa0a5fe0c32a9 |
push id | 33933 |
push user | rgurzau@mozilla.com |
push date | Wed, 02 May 2018 21:05:42 +0000 |
treeherder | mozilla-central@2d83e1843241 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | nfroyd |
bugs | 1457882 |
milestone | 61.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
mozglue/misc/Mutex_posix.cpp | file | annotate | diff | comparison | revisions | |
mozglue/misc/PlatformMutex.h | file | annotate | diff | comparison | revisions |
--- a/mozglue/misc/Mutex_posix.cpp +++ b/mozglue/misc/Mutex_posix.cpp @@ -1,34 +1,73 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "mozilla/Assertions.h" +#include <algorithm> #include <errno.h> #include <pthread.h> #include <stdio.h> +#include <unistd.h> #include "mozilla/PlatformMutex.h" #include "MutexPlatformData_posix.h" +#define REPORT_PTHREADS_ERROR(result, msg) \ + { \ + errno = result; \ + perror(msg); \ + MOZ_CRASH(msg); \ + } + #define TRY_CALL_PTHREADS(call, msg) \ { \ int result = (call); \ if (result != 0) { \ - errno = result; \ - perror(msg); \ - MOZ_CRASH(msg); \ + REPORT_PTHREADS_ERROR(result, msg); \ } \ } +#ifdef XP_DARWIN + +// CPU count. Read concurrently from multiple threads. Written once during the +// first mutex initialization; re-initialization is safe hence relaxed ordering +// is OK. +static mozilla::Atomic<uint32_t, mozilla::MemoryOrdering::Relaxed> sCPUCount(0); + +static void +EnsureCPUCount() +{ + if (sCPUCount) { + return; + } + + // _SC_NPROCESSORS_CONF and _SC_NPROCESSORS_ONLN are common, but not + // standard. +#if defined(_SC_NPROCESSORS_CONF) + long n = sysconf(_SC_NPROCESSORS_CONF); + sCPUCount = (n > 0) ? uint32_t(n) : 1; +#elif defined(_SC_NPROCESSORS_ONLN) + long n = sysconf(_SC_NPROCESSORS_ONLN); + sCPUCount = (n > 0) ? uint32_t(n) : 1; +#else + sCPUCount = 1; +#endif +} + +#endif // XP_DARWIN + mozilla::detail::MutexImpl::MutexImpl() +#ifdef XP_DARWIN + : averageSpins(0) +#endif { pthread_mutexattr_t* attrp = nullptr; // Linux with glibc and FreeBSD support adaptive mutexes that spin // for a short number of tries before sleeping. NSPR's locks did // this, too, and it seems like a reasonable thing to do. #if (defined(__linux__) && defined(__GLIBC__)) || defined(__FreeBSD__) #define ADAPTIVE_MUTEX_SUPPORTED @@ -55,29 +94,90 @@ mozilla::detail::MutexImpl::MutexImpl() TRY_CALL_PTHREADS(pthread_mutex_init(&platformData()->ptMutex, attrp), "mozilla::detail::MutexImpl::MutexImpl: pthread_mutex_init failed"); #if defined(ATTR_REQUIRED) TRY_CALL_PTHREADS(pthread_mutexattr_destroy(&attr), "mozilla::detail::MutexImpl::MutexImpl: pthread_mutexattr_destroy failed"); #endif + +#ifdef XP_DARWIN + EnsureCPUCount(); +#endif } mozilla::detail::MutexImpl::~MutexImpl() { TRY_CALL_PTHREADS(pthread_mutex_destroy(&platformData()->ptMutex), "mozilla::detail::MutexImpl::~MutexImpl: pthread_mutex_destroy failed"); } +inline void +mozilla::detail::MutexImpl::mutexLock() +{ + TRY_CALL_PTHREADS(pthread_mutex_lock(&platformData()->ptMutex), + "mozilla::detail::MutexImpl::mutexLock: pthread_mutex_lock failed"); +} + +#ifdef XP_DARWIN +inline bool +mozilla::detail::MutexImpl::mutexTryLock() +{ + int result = pthread_mutex_trylock(&platformData()->ptMutex); + if (result == 0) { + return true; + } + + if (result == EBUSY) { + return false; + } + + REPORT_PTHREADS_ERROR(result, + "mozilla::detail::MutexImpl::mutexTryLock: pthread_mutex_trylock failed"); +} +#endif + void mozilla::detail::MutexImpl::lock() { - TRY_CALL_PTHREADS(pthread_mutex_lock(&platformData()->ptMutex), - "mozilla::detail::MutexImpl::lock: pthread_mutex_lock failed"); +#ifndef XP_DARWIN + mutexLock(); +#else + // Mutex performance on OSX can be very poor if there's a lot of contention as + // this causes excessive context switching. On Linux/FreeBSD we use the + // adaptive mutex type (PTHREAD_MUTEX_ADAPTIVE_NP) to address this, but this + // isn't available on OSX. The code below is a reimplementation of this + // feature. + + MOZ_ASSERT(sCPUCount); + if (sCPUCount == 1) { + mutexLock(); + return; + } + + if (!mutexTryLock()) { + const int32_t SpinLimit = 100; + + int32_t count = 0; + int32_t maxSpins = std::min(SpinLimit, 2 * averageSpins + 10); + do { + if (count >= maxSpins) { + mutexLock(); + break; + } + asm("pause"); // Hint to the processor that we're spinning. + count++; + } while (!mutexTryLock()); + + // Update moving average. + averageSpins += (count - averageSpins) / 8; + MOZ_ASSERT(averageSpins >= 0 && averageSpins <= SpinLimit); + } +#endif // XP_DARWIN } void mozilla::detail::MutexImpl::unlock() { TRY_CALL_PTHREADS(pthread_mutex_unlock(&platformData()->ptMutex), "mozilla::detail::MutexImpl::unlock: pthread_mutex_unlock failed"); }
--- a/mozglue/misc/PlatformMutex.h +++ b/mozglue/misc/PlatformMutex.h @@ -2,16 +2,17 @@ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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/. */ #ifndef mozilla_PlatformMutex_h #define mozilla_PlatformMutex_h +#include "mozilla/Atomics.h" #include "mozilla/Attributes.h" #include "mozilla/Move.h" #if !defined(XP_WIN) # include <pthread.h> #endif namespace mozilla { @@ -37,23 +38,34 @@ protected: MFBT_API void unlock(); private: MutexImpl(const MutexImpl&) = delete; void operator=(const MutexImpl&) = delete; MutexImpl(MutexImpl&&) = delete; void operator=(MutexImpl&&) = delete; + void mutexLock(); +#ifdef XP_DARWIN + bool mutexTryLock(); +#endif + PlatformData* platformData(); #if !defined(XP_WIN) void* platformData_[sizeof(pthread_mutex_t) / sizeof(void*)]; static_assert(sizeof(pthread_mutex_t) / sizeof(void*) != 0 && sizeof(pthread_mutex_t) % sizeof(void*) == 0, "pthread_mutex_t must have pointer alignment"); +#ifdef XP_DARWIN + // Moving average of the number of spins it takes to acquire the mutex if we + // have to wait. May be accessed by multiple threads concurrently. Getting the + // latest value is not essential hence relaxed memory ordering is sufficient. + mozilla::Atomic<int32_t, mozilla::MemoryOrdering::Relaxed> averageSpins; +#endif #else void* platformData_[6]; #endif friend class mozilla::detail::ConditionVariableImpl; }; } // namespace detail