Bug 1852754: part 9) Add tests for dynamically loading <link rel="prefetch"> elements...
[gecko.git] / xpcom / docs / thread-safety.rst
blobbe2f15680480d9561eb216c72a0df705d6042147
1 **Thread safety analysis in Gecko**
2 ===================================
4 Clang thread-safety analysis is supported in Gecko. This means
5 builds will generate warnings when static analysis detects an issue with
6 locking of mutex/monitor-protected members and data structures. Note
7 that Chrome uses the same feature. An example warning: ::
9   warning: dom/media/AudioStream.cpp:504:22 [-Wthread-safety-analysis]
10   reading variable 'mDataSource' requires holding mutex 'mMonitor'
12 If your patch causes warnings like this, you’ll need to resolve them;
13 they will be errors on checkin.
15 This analysis depends on thread-safety attributions in the source. These
16 have been added to Mozilla’s Mutex and Monitor classes and subclasses,
17 but in practice the analysis is largely dependent on additions to the
18 code being checked, in particular adding MOZ_GUARDED_BY(mutex) attributions
19 on the definitions of member variables. Like this: ::
21   mozilla::Mutex mLock;
22   bool mShutdown MOZ_GUARDED_BY(mLock);
24 For background on Clang’s thread-safety support, see `their
25 documentation <https://clang.llvm.org/docs/ThreadSafetyAnalysis.html>`__.
27 Newly added Mutexes and Monitors **MUST** use thread-safety annotations,
28 and we are enabling static checks to verify this. Legacy uses of Mutexes
29 and Monitors are marked with MOZ_UNANNOTATED.
31 If you’re modifying code that has been annotated with
32 MOZ_GUARDED_BY()/MOZ_REQUIRES()/etc, you should **make sure that the annotations
33 are updated properly**; e.g. if you change the thread-usage of a member
34 variable or method you should mark it accordingly, comment, and resolve
35 any warnings. Since the warnings will be errors in autoland/m-c, you
36 won’t be able to land code with active warnings.
38 **Annotating locking and usage requirements in class definitions**
39 ------------------------------------------------------------------
41 Values that require a lock to access, or which are simply used from more
42 than one thread, should always have documentation in the definition
43 about the locking requirements and/or what threads it’s touched from: ::
45   // This array is accessed from both the direct video thread, and the graph
46   // thread. Protected by mMutex.
48   nsTArray<std::pair<ImageContainer::FrameID, VideoChunk>> mFrames
49   MOZ_GUARDED_BY(mMutex);
51   // Set on MainThread, deleted on either MainThread mainthread, used on
52   // MainThread or IO Thread in DoStopSession
53   nsCOMPtr<nsITimer> mReconnectDelayTimer MOZ_GUARDED_BY(mMutex);
55 It’s strongly recommended to group values by access pattern, but it’s
56 **critical** to make it clear what the requirements to access a value
57 are. With values protected by Mutexes and Monitors, adding a
58 MOZ_GUARDED_BY(mutex/monitor) should be sufficient, though you may want to
59 also document what threads access it, and if they read or write to it.
61 Values which have more complex access requirements (see single-writer
62 and time-based-locking below) need clear documentation where they’re
63 defined: ::
65   MutexSingleWriter mMutex;
67   // mResource should only be modified on the main thread with the lock.
68   // So it can be read without lock on the main thread or on other threads
69   // with the lock.
70   RefPtr<ChannelMediaResource> mResource MOZ_GUARDED_BY(mMutex);
72 **WARNING:** thread-safety analysis is not magic; it depends on you telling
73 it the requirements around access. If you don’t mark something as
74 MOZ_GUARDED_BY() it won’t figure it out for you, and you can end up with a data
75 race. When writing multithreaded code, you should always be thinking about
76 which threads can access what and when, and document this.
78 **How to annotate different locking patterns in Gecko**
79 -------------------------------------------------------
81 Gecko uses a number of different locking patterns. They include:
83 -  **Always Lock** -
84    Multiple threads may read and write the value
86 -  **Single Writer** -
87    One thread does all the writing, other threads
88    read the value, but code on the writing thread also reads it
89    without the lock
91 -  **Out-of-band invariants** -
92    A value may be accessed from other threads,
93    but only after or before certain other events or in a certain state,
94    like when after a listener has been added or before a processing
95    thread has been shut down.
97 The simplest and easiest to check with static analysis is **Always
98 Lock**, and generally you should prefer this pattern. This is very
99 simple; you add MOZ_GUARDED_BY(mutex/monitor), and must own the lock to
100 access the value. This can be implemented by some combination of direct
101 Lock/AutoLock calls in the method; an assertion that the lock is already
102 held by the current thread, or annotating the method as requiring the
103 lock (MOZ_REQUIRES(mutex)) in the method definition: ::
105   // Ensures mSize is initialized, if it can be.
106   // mLock must be held when this is called, and mInput must be non-null.
107   void EnsureSizeInitialized() MOZ_REQUIRES(mLock);
108   ...
109   // This lock protects mSeekable, mInput, mSize, and mSizeInitialized.
110   Mutex mLock;
111   int64_t mSize MOZ_GUARDED_BY(mLock);
113 **Single Writer** is tricky for static analysis normally, since it
114 doesn’t know what thread an access will occur on. In general, you should
115 prefer using Always Lock in non-performance-sensitive code, especially
116 since these mutexes are almost always uncontended and therefore cheap to
117 lock.
119 To support this fairly common pattern in Mozilla code, we’ve added
120 MutexSingleWriter and MonitorSingleWriter subclasses. To use these, you
121 need to subclass SingleWriterLockOwner on one object (typically the
122 object containing the Mutex), implement ::OnWritingThread(), and pass
123 the object to the constructor for MutexSingleWriter. In code that
124 accesses the guarded value from the writing thread, you need to add
125 mMutex.AssertIsOnWritingThread(), which both does a debug-only runtime
126 assertion by calling OnWritingThread(), and also asserts to the static
127 analyzer that the lock is held (which it isn’t).
129 There is one case this causes problems with: when a method needs to
130 access the value (without the lock), and then decides to write to the
131 value from the same method, taking the lock. To the static analyzer,
132 this looks like a double-lock. Either you will need to add
133 MOZ_NO_THREAD_SAFETY_ANALYSIS to the method, move the write into another
134 method you call, or locally disable the warning with
135 MOZ_PUSH_IGNORE_THREAD_SAFETY and MOZ_POP_THREAD_SAFETY. We’re discussing with
136 the clang static analysis developers how to better handle this.
138 Note also that this provides no checking that the lock is taken to write
139 to the value: ::
141   MutexSingleWriter mMutex;
142   // mResource should only be modified on the main thread with the lock.
143   // So it can be read without lock on the main thread or on other threads
144   // with the lock.
145   RefPtr<ChannelMediaResource> mResource MOZ_GUARDED_BY(mMutex);
146   ...
147   nsresult ChannelMediaResource::Listener::OnStartRequest(nsIRequest *aRequest) {
148     mMutex.AssertOnWritingThread();
150     // Read from the only writing thread; no lock needed
151     if (!mResource) {
152       return NS_OK;
153     }
154     return mResource->OnStartRequest(aRequest, mOffset);
155   }
157 If you need to assert you’re on the writing thread, then later take a
158 lock to modify a value, it will cause a warning: ”acquiring mutex
159 'mMutex' that is already held”. You can resolve this by turning off
160 thread-safety analysis for the lock: ::
162   mMutex.AssertOnWritingThread();
163   ...
164   {
165     MOZ_PUSH_IGNORE_THREAD_SAFETY
166     MutexSingleWriterAutoLock lock(mMutex);
167     MOZ_POP_THREAD_SAFETY
169 **Out-of-band Invariants** is used in a number of places (and may be
170 combined with either of the above patterns). It's using other knowledge
171 about the execution pattern of the code to assert that it's safe to avoid
172 taking certain locks.   A primary example is when a value can
173 only be accessed from a single thread for part of its lifetime (this can
174 also be referred to as "time-based locking").
176 Note that thread-safety analysis always ignores constructors and destructors
177 (which shouldn’t have races with other threads barring really odd usages).
178 Since only a single thread can access during those time periods, locking is
179 not required there.  However, if a method is called from a constructor,
180 that method may generate warnings since the compiler doesn't know if it
181 might be called from elsewhere: ::
183   ...
184   class nsFoo {
185   public:
186     nsFoo() {
187       mBar = true; // Ok since we're in the constructor, no warning
188       Init();
189     }
190     void Init() {  // we're only called from the constructor
191       // This causes a thread-safety warning, since the compiler
192       // can't prove that Init() is only called from the constructor
193       mQuerty = true;
194     }
195     ...
196     mMutex mMutex;
197     uint32_t mBar MOZ_GUARDED_BY(mMutex);
198     uint32_t mQuerty MOZ_GUARDED_BY(mMutex);
199   }
201 Another example might be a value that’s used from other threads, but only
202 if an observer has been installed. Thus code that always runs before the
203 observer is installed, or after it’s removed, does not need to lock.
205 These patterns are impossible to statically check in most cases. If all
206 the periods where it’s accessed from one thread only are on the same
207 thread, you could use the Single Writer pattern support to cover this
208 case. You would add AssertIsOnWritingThread() calls to methods that meet
209 the criteria that only a single thread can access the value (but only if
210 that holds). Unlike regular uses of SingleWriter, however, there’s no way
211 to check if you added such an assertion to code that runs on the “right”
212 thread, but during a period where another thread might modify it.
214 For this reason, we **strongly** suggest that you convert cases of
215 Out-of-band-invariants/Time-based-locking to Always Lock if you’re
216 refactoring the code or making major modifications. This is far less prone
217 to error, and also to future changes breaking the assumptions about other
218 threads accessing the value. In all but a few cases where code is on a very
219 ‘hot’ path, this will have no impact on performance - taking an uncontended
220 lock is cheap.
222 To quiet warnings where these patterns are in use, you'll need to either
223 add locks (preferred), or suppress the warnings with MOZ_NO_THREAD_SAFETY_ANALYSIS or
224 MOZ_PUSH_IGNORE_THREAD_SAFETY/MOZ_POP_THREAD_SAFETY.
226 **This pattern especially needs good documentation in the code as to what
227 threads will access what members under what conditions!**::
229   // Can't be accessed by multiple threads yet
230   nsresult nsAsyncStreamCopier::InitInternal(nsIInputStream* source,
231                                              nsIOutputStream* sink,
232                                              nsIEventTarget* target,
233                                              uint32_t chunkSize,
234                                              bool closeSource,
235                                              bool closeSink)
236         MOZ_NO_THREAD_SAFETY_ANALYSIS {
238 and::
240   // We can't be accessed by another thread because this hasn't been
241   // added to the public list yet
242   MOZ_PUSH_IGNORE_THREAD_SAFETY
243   mRestrictedPortList.AppendElement(gBadPortList[i]);
244   MOZ_POP_THREAD_SAFETY
246 and::
248   // This is called on entries in another entry's mCallback array, under the lock
249   // of that other entry. No other threads can access this entry at this time.
250   bool CacheEntry::Callback::DeferDoom(bool* aDoom) const {
252 **Known limitations**
253 ---------------------
255 **Static analysis can’t handle all reasonable patterns.** In particular,
256 per their documentation, it can’t handle conditional locks, like: ::
258   if (OnMainThread()) {
259     mMutex.Lock();
260   }
262 You should resolve this either via MOZ_NO_THREAD_SAFETY_ANALYSIS on the
263 method, or MOZ_PUSH_IGNORE_THREAD_SAFETY/MOZ_POP_THREAD_SAFETY.
265 **Sometimes the analyzer can’t figure out that two objects are both the
266 same Mutex**, and it will warn you. You may be able to resolve this by
267 making sure you’re using the same pattern to access the mutex: ::
269    mChan->mMonitor->AssertCurrentThreadOwns();
270    ...
271    {
272  -    MonitorAutoUnlock guard(*monitor);
273  +    MonitorAutoUnlock guard(*(mChan->mMonitor.get())); // avoids mutex warning
274      ok = node->SendUserMessage(port, std::move(aMessage));
275    }
277 **Maybe<MutexAutoLock>** doesn’t tell the static analyzer when the mutex
278 is owned or freed; follow locking via the MayBe<> by
279 **mutex->AssertCurrentThreadOwns();** (and ditto for Monitors): ::
281   Maybe<MonitorAutoLock> lock(std::in_place, *mMonitor);
282   mMonitor->AssertCurrentThreadOwns(); // for threadsafety analysis
284 If you reset() the Maybe<>, you may need to surround it with
285 MOZ_PUSH_IGNORE_THREAD_SAFETY and MOZ_POP_THREAD_SAFETY macros: ::
287   MOZ_PUSH_IGNORE_THREAD_SAFETY
288   aLock.reset();
289   MOZ_POP_THREAD_SAFETY
291 **Passing a protected value by-reference** sometimes will confuse the
292 analyzer. Use MOZ_PUSH_IGNORE_THREAD_SAFETY and MOZ_POP_THREAD_SAFETY macros to
293 resolve this.
295 **Classes which need thread-safety annotations**
296 ------------------------------------------------
298 -  Mutex
300 -  StaticMutex
302 -  RecursiveMutex
304 -  BaseProfilerMutex
306 -  Monitor
308 -  StaticMonitor
310 -  ReentrantMonitor
312 -  RWLock
314 -  Anything that hides an internal Mutex/etc and presents a Mutex-like
315       API (::Lock(), etc).
317 **Additional Notes**
318 --------------------
320 Some code passes **Proof-of-Lock** AutoLock parameters, as a poor form of
321 static analysis. While it’s hard to make mistakes if you pass an AutoLock
322 reference, it is possible to pass a lock to the wrong Mutex/Monitor.
324 Proof-of-lock is basically redundant to MOZ_REQUIRES() and obsolete, and
325 depends on the optimizer to remove it, and per above it can be misused,
326 with effort.  With MOZ_REQUIRES(), any proof-of-lock parameters can be removed,
327 though you don't have to do so immediately.
329 In any method taking an aProofOfLock parameter, add a MOZ_REQUIRES(mutex) to
330 the definition (and optionally remove the proof-of-lock), or add a
331 mMutex.AssertCurrentThreadOwns() to the method: ::
333     nsresult DispatchLockHeld(already_AddRefed<WorkerRunnable> aRunnable,
334  -                            nsIEventTarget* aSyncLoopTarget,
335  -                            const MutexAutoLock& aProofOfLock);
336  +                            nsIEventTarget* aSyncLoopTarget) MOZ_REQUIRES(mMutex);
338 or (if for some reason it's hard to specify the mutex in the header)::
340     nsresult DispatchLockHeld(already_AddRefed<WorkerRunnable> aRunnable,
341  -                            nsIEventTarget* aSyncLoopTarget,
342  -                            const MutexAutoLock& aProofOfLock);
343  +                            nsIEventTarget* aSyncLoopTarget) {
344  +  mMutex.AssertCurrentThreadOwns();
346 In addition to MOZ_GUARDED_BY() there’s also MOZ_PT_GUARDED_BY(), which says
347 that the pointer isn’t guarded, but the data pointed to by the pointer
350 Classes that expose a Mutex-like interface can be annotated like Mutex;
351 see some of the examples in the tree that use MOZ_CAPABILITY and
352 MOZ_ACQUIRE()/MOZ_RELEASE().
354 Shared locks are supported, though we don’t use them much. See RWLock.