Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/mozglue/misc/Mutex_posix.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
6
7
#include "mozilla/Assertions.h"
8
#include "mozilla/Maybe.h"
9
10
#include <algorithm>
11
#include <errno.h>
12
#include <pthread.h>
13
#include <stdio.h>
14
#include <unistd.h>
15
16
#include "mozilla/PlatformMutex.h"
17
#include "MutexPlatformData_posix.h"
18
19
#define REPORT_PTHREADS_ERROR(result, msg)      \
20
0
  {                                             \
21
0
    errno = result;                             \
22
0
    perror(msg);                                \
23
0
    MOZ_CRASH(msg);                             \
24
0
  }
25
26
#define TRY_CALL_PTHREADS(call, msg)            \
27
9.41M
  {                                             \
28
9.41M
    int result = (call);                        \
29
9.41M
    if (result != 0) {                          \
30
0
      REPORT_PTHREADS_ERROR(result, msg);       \
31
0
    }                                           \
32
9.41M
  }
33
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,
40
                       mozilla::recordreplay::Behavior::DontPreserve> sCPUCount(0);
41
42
static void
43
EnsureCPUCount()
44
{
45
  if (sCPUCount) {
46
    return;
47
  }
48
49
  // _SC_NPROCESSORS_CONF and _SC_NPROCESSORS_ONLN are common, but not
50
  // standard.
51
#if defined(_SC_NPROCESSORS_CONF)
52
  long n = sysconf(_SC_NPROCESSORS_CONF);
53
  sCPUCount = (n > 0) ? uint32_t(n) : 1;
54
#elif defined(_SC_NPROCESSORS_ONLN)
55
  long n = sysconf(_SC_NPROCESSORS_ONLN);
56
  sCPUCount = (n > 0) ? uint32_t(n) : 1;
57
#else
58
  sCPUCount = 1;
59
#endif
60
}
61
62
#endif // XP_DARWIN
63
64
mozilla::detail::MutexImpl::MutexImpl(recordreplay::Behavior aRecorded)
65
#ifdef XP_DARWIN
66
  : averageSpins(0)
67
#endif
68
967
{
69
967
  pthread_mutexattr_t* attrp = nullptr;
70
967
71
967
  mozilla::Maybe<mozilla::recordreplay::AutoEnsurePassThroughThreadEvents> pt;
72
967
  if (aRecorded == recordreplay::Behavior::DontPreserve) {
73
229
    pt.emplace();
74
229
  }
75
967
76
967
  // Linux with glibc and FreeBSD support adaptive mutexes that spin
77
967
  // for a short number of tries before sleeping.  NSPR's locks did
78
967
  // this, too, and it seems like a reasonable thing to do.
79
967
#if (defined(__linux__) && defined(__GLIBC__)) || defined(__FreeBSD__)
80
967
#define ADAPTIVE_MUTEX_SUPPORTED
81
967
#endif
82
967
83
#if defined(DEBUG)
84
#define ATTR_REQUIRED
85
#define MUTEX_KIND PTHREAD_MUTEX_ERRORCHECK
86
#elif defined(ADAPTIVE_MUTEX_SUPPORTED)
87
#define ATTR_REQUIRED
88
967
#define MUTEX_KIND PTHREAD_MUTEX_ADAPTIVE_NP
89
967
#endif
90
967
91
967
#if defined(ATTR_REQUIRED)
92
967
  pthread_mutexattr_t attr;
93
967
94
967
  TRY_CALL_PTHREADS(pthread_mutexattr_init(&attr),
95
967
                    "mozilla::detail::MutexImpl::MutexImpl: pthread_mutexattr_init failed");
96
967
97
967
  TRY_CALL_PTHREADS(pthread_mutexattr_settype(&attr, MUTEX_KIND),
98
967
                    "mozilla::detail::MutexImpl::MutexImpl: pthread_mutexattr_settype failed");
99
967
  attrp = &attr;
100
967
#endif
101
967
102
967
  TRY_CALL_PTHREADS(pthread_mutex_init(&platformData()->ptMutex, attrp),
103
967
                    "mozilla::detail::MutexImpl::MutexImpl: pthread_mutex_init failed");
104
967
105
967
#if defined(ATTR_REQUIRED)
106
967
  TRY_CALL_PTHREADS(pthread_mutexattr_destroy(&attr),
107
967
                    "mozilla::detail::MutexImpl::MutexImpl: pthread_mutexattr_destroy failed");
108
967
#endif
109
967
110
#ifdef XP_DARWIN
111
  EnsureCPUCount();
112
#endif
113
}
114
115
mozilla::detail::MutexImpl::~MutexImpl()
116
72
{
117
72
  TRY_CALL_PTHREADS(pthread_mutex_destroy(&platformData()->ptMutex),
118
72
                    "mozilla::detail::MutexImpl::~MutexImpl: pthread_mutex_destroy failed");
119
72
}
120
121
inline void
122
mozilla::detail::MutexImpl::mutexLock()
123
4.70M
{
124
4.70M
  TRY_CALL_PTHREADS(pthread_mutex_lock(&platformData()->ptMutex),
125
4.70M
                    "mozilla::detail::MutexImpl::mutexLock: pthread_mutex_lock failed");
126
4.70M
}
127
128
#ifdef XP_DARWIN
129
inline bool
130
mozilla::detail::MutexImpl::mutexTryLock()
131
{
132
  int result = pthread_mutex_trylock(&platformData()->ptMutex);
133
  if (result == 0) {
134
    return true;
135
  }
136
137
  if (result == EBUSY) {
138
    return false;
139
  }
140
141
  REPORT_PTHREADS_ERROR(result,
142
                        "mozilla::detail::MutexImpl::mutexTryLock: pthread_mutex_trylock failed");
143
}
144
#endif
145
146
void
147
mozilla::detail::MutexImpl::lock()
148
4.70M
{
149
4.70M
#ifndef XP_DARWIN
150
4.70M
  mutexLock();
151
#else
152
  // Mutex performance on OSX can be very poor if there's a lot of contention as
153
  // this causes excessive context switching. On Linux/FreeBSD we use the
154
  // adaptive mutex type (PTHREAD_MUTEX_ADAPTIVE_NP) to address this, but this
155
  // isn't available on OSX. The code below is a reimplementation of this
156
  // feature.
157
158
  MOZ_ASSERT(sCPUCount);
159
  if (sCPUCount == 1 || recordreplay::IsRecordingOrReplaying()) {
160
    mutexLock();
161
    return;
162
  }
163
164
  if (!mutexTryLock()) {
165
    const int32_t SpinLimit = 100;
166
167
    int32_t count = 0;
168
    int32_t maxSpins = std::min(SpinLimit, 2 * averageSpins + 10);
169
    do {
170
      if (count >= maxSpins) {
171
        mutexLock();
172
        break;
173
      }
174
      asm("pause"); // Hint to the processor that we're spinning.
175
      count++;
176
    } while (!mutexTryLock());
177
178
    // Update moving average.
179
    averageSpins += (count - averageSpins) / 8;
180
    MOZ_ASSERT(averageSpins >= 0 && averageSpins <= SpinLimit);
181
  }
182
#endif // XP_DARWIN
183
}
184
185
void
186
mozilla::detail::MutexImpl::unlock()
187
4.70M
{
188
4.70M
  TRY_CALL_PTHREADS(pthread_mutex_unlock(&platformData()->ptMutex),
189
4.70M
                    "mozilla::detail::MutexImpl::unlock: pthread_mutex_unlock failed");
190
4.70M
}
191
192
#undef TRY_CALL_PTHREADS
193
194
mozilla::detail::MutexImpl::PlatformData*
195
mozilla::detail::MutexImpl::platformData()
196
9.40M
{
197
9.40M
  static_assert(sizeof(platformData_) >= sizeof(PlatformData),
198
9.40M
                "platformData_ is too small");
199
9.40M
  return reinterpret_cast<PlatformData*>(platformData_);
200
9.40M
}