Skip to content

Commit addf814

Browse files
author
Randell Jesup
committed
Bug 1207753 - Base thread-safety attribution support r=nika
Differential Revision: https://phabricator.services.mozilla.com/D130606
1 parent 1eae42f commit addf814

File tree

16 files changed

+666
-151
lines changed

16 files changed

+666
-151
lines changed

mfbt/Maybe.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "mozilla/MemoryChecking.h"
2222
#include "mozilla/OperatorNewExtensions.h"
2323
#include "mozilla/Poison.h"
24+
#include "mozilla/ThreadSafety.h"
2425

2526
class nsCycleCollectionTraversalCallback;
2627

@@ -636,7 +637,13 @@ class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Maybe
636637
constexpr void reset() {
637638
if (isSome()) {
638639
if constexpr (!std::is_trivially_destructible_v<T>) {
640+
/*
641+
* Static analyzer gets confused if we have Maybe<MutexAutoLock>,
642+
* so we suppress thread-safety warnings here
643+
*/
644+
PUSH_IGNORE_THREAD_SAFETY
639645
ref().T::~T();
646+
POP_THREAD_SAFETY
640647
poisonData();
641648
}
642649
mIsSome = false;

mfbt/ThreadSafety.h

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// Note: the file is largely imported directly from WebRTC upstream, so
2+
// comments may not completely apply to Mozilla's usage.
3+
//
4+
// Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
5+
//
6+
// Use of this source code is governed by a BSD-style license
7+
// that can be found in the LICENSE file in the root of the source
8+
// tree. An additional intellectual property rights grant can be found
9+
// in the file PATENTS. All contributing project authors may
10+
// be found in the AUTHORS file in the root of the source tree.
11+
//
12+
// Borrowed from
13+
// https://code.google.com/p/gperftools/source/browse/src/base/thread_annotations.h
14+
// but adapted for clang attributes instead of the gcc.
15+
//
16+
// This header file contains the macro definitions for thread safety
17+
// annotations that allow the developers to document the locking policies
18+
// of their multi-threaded code. The annotations can also help program
19+
// analysis tools to identify potential thread safety issues.
20+
21+
#ifndef mozilla_ThreadSafety_h
22+
#define mozilla_ThreadSafety_h
23+
#include "mozilla/Attributes.h"
24+
25+
#if defined(__clang__) && (!defined(SWIG))
26+
# define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
27+
// Allow for localized suppression of thread-safety warnings; finer-grained
28+
// than NO_THREAD_SAFETY_ANALYSIS
29+
# define PUSH_IGNORE_THREAD_SAFETY \
30+
_Pragma("GCC diagnostic push") \
31+
_Pragma("GCC diagnostic ignored \"-Wthread-safety\"")
32+
# define POP_THREAD_SAFETY _Pragma("GCC diagnostic pop")
33+
34+
#else
35+
# define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
36+
# define PUSH_IGNORE_THREAD_SAFETY
37+
# define POP_THREAD_SAFETY
38+
#endif
39+
40+
// Document if a shared variable/field needs to be protected by a lock.
41+
// GUARDED_BY allows the user to specify a particular lock that should be
42+
// held when accessing the annotated variable, while GUARDED_VAR only
43+
// indicates a shared variable should be guarded (by any lock). GUARDED_VAR
44+
// is primarily used when the client cannot express the name of the lock.
45+
#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
46+
#define GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(guarded)
47+
48+
// Document if the memory location pointed to by a pointer should be guarded
49+
// by a lock when dereferencing the pointer. Similar to GUARDED_VAR,
50+
// PT_GUARDED_VAR is primarily used when the client cannot express the name
51+
// of the lock. Note that a pointer variable to a shared memory location
52+
// could itself be a shared variable. For example, if a shared global pointer
53+
// q, which is guarded by mu1, points to a shared memory location that is
54+
// guarded by mu2, q should be annotated as follows:
55+
// int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2);
56+
#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
57+
#define PT_GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded)
58+
59+
// Document the acquisition order between locks that can be held
60+
// simultaneously by a thread. For any two locks that need to be annotated
61+
// to establish an acquisition order, only one of them needs the annotation.
62+
// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER
63+
// and ACQUIRED_BEFORE.)
64+
#define ACQUIRED_AFTER(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
65+
#define ACQUIRED_BEFORE(...) THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
66+
67+
// The following three annotations document the lock requirements for
68+
// functions/methods.
69+
70+
// Document if a function expects certain locks to be held before it is called
71+
#define REQUIRES(...) \
72+
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
73+
74+
#define REQUIRES_SHARED(...) \
75+
THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
76+
77+
// Document the locks acquired in the body of the function. These locks
78+
// cannot be held when calling this function (as google3's Mutex locks are
79+
// non-reentrant).
80+
#define EXCLUDES(x) THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(x))
81+
82+
// Document the lock the annotated function returns without acquiring it.
83+
#define RETURN_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
84+
85+
// Document if a class/type is a lockable type (such as the Mutex class).
86+
#define CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(lockable)
87+
88+
// Document if a class is a scoped lockable type (such as the MutexLock class).
89+
#define SCOPED_CAPABILITY THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
90+
91+
// The following annotations specify lock and unlock primitives.
92+
#define CAPABILITY_ACQUIRE(...) \
93+
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
94+
95+
#define EXCLUSIVE_RELEASE(...) \
96+
THREAD_ANNOTATION_ATTRIBUTE__(release_capability(__VA_ARGS__))
97+
98+
#define ACQUIRE_SHARED(...) \
99+
THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
100+
101+
#define TRY_ACQUIRE(...) \
102+
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
103+
104+
#define SHARED_TRYLOCK_FUNCTION(...) \
105+
THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
106+
107+
#define CAPABILITY_RELEASE(...) THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
108+
109+
// An escape hatch for thread safety analysis to ignore the annotated function.
110+
#define NO_THREAD_SAFETY_ANALYSIS \
111+
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
112+
113+
// Newer capabilities
114+
#define ASSERT_CAPABILITY(x) THREAD_ANNOTATION_ATTRIBUTE__(assert_capability(x))
115+
116+
#define ASSERT_SHARED_CAPABILITY(x) \
117+
THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_capability(x))
118+
119+
// Additions from current clang assertions.
120+
// Note: new-style definitions, since these didn't exist in the old style
121+
#define RELEASE_SHARED(...) \
122+
THREAD_ANNOTATION_ATTRIBUTE__(release_shared_capability(__VA_ARGS__))
123+
124+
#define RELEASE_GENERIC(...) \
125+
THREAD_ANNOTATION_ATTRIBUTE__(release_generic_capability(__VA_ARGS__))
126+
127+
// Mozilla additions:
128+
129+
// AutoUnlock is supported by clang currently, but oddly you must use
130+
// EXCLUSIVE_RELEASE() for both the RAII constructor *and* the destructor.
131+
// This hides the ugliness until they fix it upstream.
132+
#define SCOPED_UNLOCK_RELEASE(...) EXCLUSIVE_RELEASE(__VA_ARGS__)
133+
#define SCOPED_UNLOCK_REACQUIRE(...) EXCLUSIVE_RELEASE(__VA_ARGS__)
134+
135+
#endif /* mozilla_ThreadSafety_h */

mfbt/moz.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ EXPORTS.mozilla = [
108108
"TemplateLib.h",
109109
"TextUtils.h",
110110
"ThreadLocal.h",
111+
"ThreadSafety.h",
111112
"ThreadSafeWeakPtr.h",
112113
"ToString.h",
113114
"Tuple.h",

mozglue/baseprofiler/public/BaseProfilerDetail.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define BaseProfilerDetail_h
1111

1212
#include "mozilla/Atomics.h"
13+
#include "mozilla/Attributes.h"
1314
#include "mozilla/Maybe.h"
1415
#include "mozilla/PlatformMutex.h"
1516
#include "mozilla/PlatformRWLock.h"

xpcom/base/StaticMonitor.h

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99

1010
#include "mozilla/Atomics.h"
1111
#include "mozilla/CondVar.h"
12+
#include "mozilla/ThreadSafety.h"
1213

1314
namespace mozilla {
1415

15-
class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticMonitor {
16+
class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS CAPABILITY StaticMonitor {
1617
public:
1718
// In debug builds, check that mMutex is initialized for us as we expect by
1819
// the compiler. In non-debug builds, don't declare a constructor so that
@@ -21,17 +22,20 @@ class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticMonitor {
2122
StaticMonitor() { MOZ_ASSERT(!mMutex); }
2223
#endif
2324

24-
void Lock() { Mutex()->Lock(); }
25+
void Lock() CAPABILITY_ACQUIRE() { Mutex()->Lock(); }
2526

26-
void Unlock() { Mutex()->Unlock(); }
27+
void Unlock() CAPABILITY_RELEASE() { Mutex()->Unlock(); }
2728

2829
void Wait() { CondVar()->Wait(); }
29-
CVStatus Wait(TimeDuration aDuration) { return CondVar()->Wait(aDuration); }
30+
CVStatus Wait(TimeDuration aDuration) {
31+
AssertCurrentThreadOwns();
32+
return CondVar()->Wait(aDuration);
33+
}
3034

3135
void Notify() { CondVar()->Notify(); }
3236
void NotifyAll() { CondVar()->NotifyAll(); }
3337

34-
void AssertCurrentThreadOwns() {
38+
void AssertCurrentThreadOwns() ASSERT_CAPABILITY(this) {
3539
#ifdef DEBUG
3640
Mutex()->AssertCurrentThreadOwns();
3741
#endif
@@ -82,14 +86,14 @@ class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticMonitor {
8286
static void operator delete(void*);
8387
};
8488

85-
class MOZ_STACK_CLASS StaticMonitorAutoLock {
89+
class MOZ_STACK_CLASS SCOPED_CAPABILITY StaticMonitorAutoLock {
8690
public:
87-
explicit StaticMonitorAutoLock(StaticMonitor& aMonitor)
91+
explicit StaticMonitorAutoLock(StaticMonitor& aMonitor) CAPABILITY_ACQUIRE(aMonitor)
8892
: mMonitor(&aMonitor) {
8993
mMonitor->Lock();
9094
}
9195

92-
~StaticMonitorAutoLock() { mMonitor->Unlock(); }
96+
~StaticMonitorAutoLock() CAPABILITY_RELEASE() { mMonitor->Unlock(); }
9397

9498
void Wait() { mMonitor->Wait(); }
9599
CVStatus Wait(TimeDuration aDuration) { return mMonitor->Wait(aDuration); }

xpcom/base/StaticMutex.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ namespace mozilla {
2626
* initialized to 0 in order to initialize mMutex. It is only safe to use
2727
* StaticMutex as a global or static variable.
2828
*/
29-
class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticMutex {
29+
class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS CAPABILITY StaticMutex {
3030
public:
3131
// In debug builds, check that mMutex is initialized for us as we expect by
3232
// the compiler. In non-debug builds, don't declare a constructor so that
@@ -35,11 +35,11 @@ class MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS StaticMutex {
3535
StaticMutex() { MOZ_ASSERT(!mMutex); }
3636
#endif
3737

38-
void Lock() { Mutex()->Lock(); }
38+
void Lock() CAPABILITY_ACQUIRE() { Mutex()->Lock(); }
3939

40-
void Unlock() { Mutex()->Unlock(); }
40+
void Unlock() CAPABILITY_RELEASE() { Mutex()->Unlock(); }
4141

42-
void AssertCurrentThreadOwns() {
42+
void AssertCurrentThreadOwns() ASSERT_CAPABILITY(this) {
4343
#ifdef DEBUG
4444
Mutex()->AssertCurrentThreadOwns();
4545
#endif

xpcom/tests/gtest/TestDeadlockDetector.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ static void DisableCrashReporter() {
7171
// Single-threaded sanity tests
7272

7373
// Stupidest possible deadlock.
74-
static int Sanity_Child() {
74+
static int Sanity_Child() NO_THREAD_SAFETY_ANALYSIS {
7575
DisableCrashReporter();
7676

7777
MUTEX m1("dd.sanity.m1");
@@ -92,7 +92,7 @@ TEST_F(TESTNAME(DeadlockDetectorTest), TESTNAME(SanityDeathTest)) {
9292
}
9393

9494
// Slightly less stupid deadlock.
95-
static int Sanity2_Child() {
95+
static int Sanity2_Child() NO_THREAD_SAFETY_ANALYSIS {
9696
DisableCrashReporter();
9797

9898
MUTEX m1("dd.sanity2.m1");
@@ -118,7 +118,7 @@ TEST_F(TESTNAME(DeadlockDetectorTest), TESTNAME(Sanity2DeathTest)) {
118118
#if 0
119119
// Temporarily disabled, see bug 1370644.
120120
int
121-
Sanity3_Child()
121+
Sanity3_Child() NO_THREAD_SAFETY_ANALYSIS
122122
{
123123
DisableCrashReporter();
124124

@@ -156,7 +156,7 @@ TEST_F(TESTNAME(DeadlockDetectorTest), TESTNAME(Sanity3DeathTest))
156156
}
157157
#endif
158158

159-
static int Sanity4_Child() {
159+
static int Sanity4_Child() NO_THREAD_SAFETY_ANALYSIS {
160160
DisableCrashReporter();
161161

162162
mozilla::ReentrantMonitor m1 MOZ_UNANNOTATED("dd.sanity4.m1");
@@ -179,7 +179,7 @@ TEST_F(TESTNAME(DeadlockDetectorTest), TESTNAME(Sanity4DeathTest)) {
179179
ASSERT_DEATH_IF_SUPPORTED(Sanity4_Child(), regex);
180180
}
181181

182-
static int Sanity5_Child() {
182+
static int Sanity5_Child() NO_THREAD_SAFETY_ANALYSIS {
183183
DisableCrashReporter();
184184

185185
mozilla::RecursiveMutex m1 MOZ_UNANNOTATED("dd.sanity4.m1");
@@ -225,7 +225,7 @@ struct ThreadState {
225225
#if 0
226226
// Temporarily disabled, see bug 1370644.
227227
static void
228-
TwoThreads_thread(void* arg)
228+
TwoThreads_thread(void* arg) NO_THREAD_SAFETY_ANALYSIS
229229
{
230230
ThreadState* state = static_cast<ThreadState*>(arg);
231231

@@ -247,7 +247,7 @@ TwoThreads_thread(void* arg)
247247
}
248248

249249
int
250-
TwoThreads_Child()
250+
TwoThreads_Child() NO_THREAD_SAFETY_ANALYSIS
251251
{
252252
DisableCrashReporter();
253253

@@ -284,7 +284,7 @@ TEST_F(TESTNAME(DeadlockDetectorTest), TESTNAME(TwoThreadsDeathTest))
284284
}
285285
#endif
286286

287-
static void ContentionNoDeadlock_thread(void* arg) {
287+
static void ContentionNoDeadlock_thread(void* arg) NO_THREAD_SAFETY_ANALYSIS {
288288
const uint32_t K = 100000;
289289

290290
ThreadState* state = static_cast<ThreadState*>(arg);
@@ -300,7 +300,7 @@ static void ContentionNoDeadlock_thread(void* arg) {
300300
}
301301
}
302302

303-
static int ContentionNoDeadlock_Child() {
303+
static int ContentionNoDeadlock_Child() NO_THREAD_SAFETY_ANALYSIS {
304304
const size_t kMutexCount = 4;
305305

306306
PRThread* threads[3];

xpcom/tests/gtest/TestRecursiveMutex.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ using mozilla::RecursiveMutexAutoLock;
1515
// well, actually recursively acquirable.
1616

1717
TEST(RecursiveMutex, SmokeTest)
18-
{
18+
NO_THREAD_SAFETY_ANALYSIS {
1919
RecursiveMutex mutex("testing mutex");
2020

2121
RecursiveMutexAutoLock lock1(mutex);

xpcom/tests/gtest/TestSynchronization.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ static PRThread* spawn(void (*run)(void*), void* arg) {
2121
// Sanity check: tests that can be done on a single thread
2222
//
2323
TEST(Synchronization, Sanity)
24-
{
24+
NO_THREAD_SAFETY_ANALYSIS {
2525
Mutex lock("sanity::lock");
2626
lock.Lock();
2727
lock.AssertCurrentThreadOwns();
@@ -112,7 +112,7 @@ TEST(Synchronization, MonitorContention)
112112

113113
static ReentrantMonitor* gMon2;
114114

115-
static void MonitorContention2_thread(void* /*arg*/) {
115+
static void MonitorContention2_thread(void* /*arg*/) NO_THREAD_SAFETY_ANALYSIS {
116116
for (int i = 0; i < 100000; ++i) {
117117
gMon2->Enter();
118118
gMon2->AssertCurrentThreadIn();
@@ -144,7 +144,7 @@ TEST(Synchronization, MonitorContention2)
144144
static ReentrantMonitor* gMon3;
145145
static int32_t gMonFirst;
146146

147-
static void MonitorSyncSanity_thread(void* /*arg*/) {
147+
static void MonitorSyncSanity_thread(void* /*arg*/) NO_THREAD_SAFETY_ANALYSIS {
148148
gMon3->Enter();
149149
gMon3->AssertCurrentThreadIn();
150150
if (gMonFirst) {
@@ -294,7 +294,7 @@ TEST(Synchronization, AutoUnlock)
294294
// AutoMonitor tests
295295
//
296296
TEST(Synchronization, AutoMonitor)
297-
{
297+
NO_THREAD_SAFETY_ANALYSIS {
298298
ReentrantMonitor m1("automonitor");
299299
ReentrantMonitor m2("automonitor2");
300300

xpcom/threads/BlockingResourceBase.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,7 @@ void RecursiveMutex::Unlock() {
502502
UnlockInternal();
503503
}
504504

505-
void RecursiveMutex::AssertCurrentThreadIn() {
505+
void RecursiveMutex::AssertCurrentThreadIn() const {
506506
MOZ_ASSERT(IsAcquired() && mOwningThread == PR_GetCurrentThread());
507507
}
508508

0 commit comments

Comments
 (0)