5
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
6
7
7
#include " mozilla/Assertions.h"
8
+ #include " mozilla/Maybe.h"
8
9
10
+ #include < algorithm>
9
11
#include < errno.h>
10
12
#include < pthread.h>
11
13
#include < stdio.h>
12
-
13
- #if defined(XP_DARWIN)
14
- # include < pthread_spis.h>
15
- #endif
14
+ #include < unistd.h>
16
15
17
16
#include " mozilla/PlatformMutex.h"
18
- #include " mozilla/Unused.h"
19
17
#include " MutexPlatformData_posix.h"
20
18
21
19
#define REPORT_PTHREADS_ERROR (result, msg ) \
33
31
} \
34
32
}
35
33
36
- mozilla::detail::MutexImpl::MutexImpl () {
34
+ #ifdef XP_DARWIN
35
+
36
+ // CPU count. Read concurrently from multiple threads. Written once during the
37
+ // first mutex initialization; re-initialization is safe hence relaxed ordering
38
+ // is OK.
39
+ static mozilla::Atomic<uint32_t , mozilla::MemoryOrdering::Relaxed> sCPUCount (0 );
40
+
41
+ static void EnsureCPUCount () {
42
+ if (sCPUCount ) {
43
+ return ;
44
+ }
45
+
46
+ // _SC_NPROCESSORS_CONF and _SC_NPROCESSORS_ONLN are common, but not
47
+ // standard.
48
+ # if defined(_SC_NPROCESSORS_CONF)
49
+ long n = sysconf (_SC_NPROCESSORS_CONF);
50
+ sCPUCount = (n > 0 ) ? uint32_t (n) : 1 ;
51
+ # elif defined(_SC_NPROCESSORS_ONLN)
52
+ long n = sysconf (_SC_NPROCESSORS_ONLN);
53
+ sCPUCount = (n > 0 ) ? uint32_t (n) : 1 ;
54
+ # else
55
+ sCPUCount = 1 ;
56
+ # endif
57
+ }
58
+
59
+ #endif // XP_DARWIN
60
+
61
+ mozilla::detail::MutexImpl::MutexImpl ()
62
+ #ifdef XP_DARWIN
63
+ : averageSpins(0 )
64
+ #endif
65
+ {
37
66
pthread_mutexattr_t * attrp = nullptr ;
38
67
39
- #if defined(DEBUG)
40
- # define MUTEX_KIND PTHREAD_MUTEX_ERRORCHECK
41
- // Linux with glibc, FreeBSD and macOS 10.14+ support adaptive mutexes that
42
- // spin for a short number of tries before sleeping. NSPR's locks did this,
43
- // too, and it seems like a reasonable thing to do.
44
- #elif (defined(__linux__) && defined(__GLIBC__)) || defined(__FreeBSD__)
45
- # define MUTEX_KIND PTHREAD_MUTEX_ADAPTIVE_NP
46
- #elif defined(XP_DARWIN)
47
- # define POLICY_KIND _PTHREAD_MUTEX_POLICY_FIRSTFIT
68
+ // Linux with glibc and FreeBSD support adaptive mutexes that spin
69
+ // for a short number of tries before sleeping. NSPR's locks did
70
+ // this, too, and it seems like a reasonable thing to do.
71
+ #if (defined(__linux__) && defined(__GLIBC__)) || defined(__FreeBSD__)
72
+ # define ADAPTIVE_MUTEX_SUPPORTED
48
73
#endif
49
74
50
- #if defined(MUTEX_KIND) || defined(POLICY_KIND)
75
+ #if defined(DEBUG)
76
+ # define ATTR_REQUIRED
77
+ # define MUTEX_KIND PTHREAD_MUTEX_ERRORCHECK
78
+ #elif defined(ADAPTIVE_MUTEX_SUPPORTED)
51
79
# define ATTR_REQUIRED
80
+ # define MUTEX_KIND PTHREAD_MUTEX_ADAPTIVE_NP
52
81
#endif
53
82
54
83
#if defined(ATTR_REQUIRED)
@@ -58,14 +87,9 @@ mozilla::detail::MutexImpl::MutexImpl() {
58
87
pthread_mutexattr_init (&attr),
59
88
" mozilla::detail::MutexImpl::MutexImpl: pthread_mutexattr_init failed" );
60
89
61
- # if defined(MUTEX_KIND)
62
90
TRY_CALL_PTHREADS (pthread_mutexattr_settype (&attr, MUTEX_KIND),
63
91
" mozilla::detail::MutexImpl::MutexImpl: "
64
92
" pthread_mutexattr_settype failed" );
65
- # elif defined(POLICY_KIND)
66
- // This can fail on macOS 10.13 and lower but that's OK
67
- Unused << pthread_mutexattr_setpolicy_np (&attr, POLICY_KIND);
68
- # endif
69
93
attrp = &attr;
70
94
#endif
71
95
@@ -78,6 +102,10 @@ mozilla::detail::MutexImpl::MutexImpl() {
78
102
" mozilla::detail::MutexImpl::MutexImpl: "
79
103
" pthread_mutexattr_destroy failed" );
80
104
#endif
105
+
106
+ #ifdef XP_DARWIN
107
+ EnsureCPUCount ();
108
+ #endif
81
109
}
82
110
83
111
mozilla::detail::MutexImpl::~MutexImpl () {
@@ -109,7 +137,49 @@ bool mozilla::detail::MutexImpl::mutexTryLock() {
109
137
" mozilla::detail::MutexImpl::mutexTryLock: pthread_mutex_trylock failed" );
110
138
}
111
139
112
- void mozilla::detail::MutexImpl::lock () { mutexLock (); }
140
+ void mozilla::detail::MutexImpl::lock () {
141
+ #ifndef XP_DARWIN
142
+ mutexLock ();
143
+ #else
144
+ // Mutex performance on OSX can be very poor if there's a lot of contention as
145
+ // this causes excessive context switching. On Linux/FreeBSD we use the
146
+ // adaptive mutex type (PTHREAD_MUTEX_ADAPTIVE_NP) to address this, but this
147
+ // isn't available on OSX. The code below is a reimplementation of this
148
+ // feature.
149
+
150
+ MOZ_ASSERT (sCPUCount );
151
+ if (sCPUCount == 1 ) {
152
+ mutexLock ();
153
+ return ;
154
+ }
155
+
156
+ if (!mutexTryLock ()) {
157
+ const int32_t SpinLimit = 100 ;
158
+
159
+ int32_t count = 0 ;
160
+ int32_t maxSpins = std::min (SpinLimit, 2 * averageSpins + 10 );
161
+ do {
162
+ if (count >= maxSpins) {
163
+ mutexLock ();
164
+ break ;
165
+ }
166
+ // Hint to the processor that we're spinning.
167
+ # ifdef __x86_64__
168
+ # define SPIN_HINT " pause"
169
+ # elif defined(__aarch64__)
170
+ # define SPIN_HINT " yield"
171
+ # endif
172
+ asm volatile (SPIN_HINT ::: " memory" );
173
+ # undef SPIN_HINT
174
+ count++;
175
+ } while (!mutexTryLock ());
176
+
177
+ // Update moving average.
178
+ averageSpins += (count - averageSpins) / 8 ;
179
+ MOZ_ASSERT (averageSpins >= 0 && averageSpins <= SpinLimit);
180
+ }
181
+ #endif // XP_DARWIN
182
+ }
113
183
114
184
void mozilla::detail::MutexImpl::unlock () {
115
185
TRY_CALL_PTHREADS (
0 commit comments