Bug 1700051: part 49) Add some documentation to `Selection::GetRangesForInterval...
[gecko.git] / xpcom / threads / SpinEventLoopUntil.h
blob398c7084a27f63348ec72ed9ea2a95c5f7f88267
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/. */
7 #ifndef xpcom_threads_SpinEventLoopUntil_h__
8 #define xpcom_threads_SpinEventLoopUntil_h__
10 #include "MainThreadUtils.h"
11 #include "mozilla/Maybe.h"
12 #include "nsThreadUtils.h"
13 #include "xpcpublic.h"
15 class nsIThread;
17 // A wrapper for nested event loops.
19 // This function is intended to make code more obvious (do you remember
20 // what NS_ProcessNextEvent(nullptr, true) means?) and slightly more
21 // efficient, as people often pass nullptr or NS_GetCurrentThread to
22 // NS_ProcessNextEvent, which results in needless querying of the current
23 // thread every time through the loop.
25 // You should use this function in preference to NS_ProcessNextEvent inside
26 // a loop unless one of the following is true:
28 // * You need to pass `false` to NS_ProcessNextEvent; or
29 // * You need to do unusual things around the call to NS_ProcessNextEvent,
30 // such as unlocking mutexes that you are holding.
32 // If you *do* need to call NS_ProcessNextEvent manually, please do call
33 // NS_GetCurrentThread() outside of your loop and pass the returned pointer
34 // into NS_ProcessNextEvent for a tiny efficiency win.
35 namespace mozilla {
37 // You should normally not need to deal with this template parameter. If
38 // you enjoy esoteric event loop details, read on.
40 // If you specify that NS_ProcessNextEvent wait for an event, it is possible
41 // for NS_ProcessNextEvent to return false, i.e. to indicate that an event
42 // was not processed. This can only happen when the thread has been shut
43 // down by another thread, but is still attempting to process events outside
44 // of a nested event loop.
46 // This behavior is admittedly strange. The scenario it deals with is the
47 // following:
49 // * The current thread has been shut down by some owner thread.
50 // * The current thread is spinning an event loop waiting for some condition
51 // to become true.
52 // * Said condition is actually being fulfilled by another thread, so there
53 // are timing issues in play.
55 // Thus, there is a small window where the current thread's event loop
56 // spinning can check the condition, find it false, and call
57 // NS_ProcessNextEvent to wait for another event. But we don't actually
58 // want it to wait indefinitely, because there might not be any other events
59 // in the event loop, and the current thread can't accept dispatched events
60 // because it's being shut down. Thus, actually blocking would hang the
61 // thread, which is bad. The solution, then, is to detect such a scenario
62 // and not actually block inside NS_ProcessNextEvent.
64 // But this is a problem, because we want to return the status of
65 // NS_ProcessNextEvent to the caller of SpinEventLoopUntil if possible. In
66 // the above scenario, however, we'd stop spinning prematurely and cause
67 // all sorts of havoc. We therefore have this template parameter to
68 // control whether errors are ignored or passed out to the caller of
69 // SpinEventLoopUntil. The latter is the default; if you find yourself
70 // wanting to use the former, you should think long and hard before doing
71 // so, and write a comment like this defending your choice.
73 enum class ProcessFailureBehavior {
74 IgnoreAndContinue,
75 ReportToCaller,
78 template <
79 ProcessFailureBehavior Behavior = ProcessFailureBehavior::ReportToCaller,
80 typename Pred>
81 bool SpinEventLoopUntil(Pred&& aPredicate, nsIThread* aThread = nullptr) {
82 nsIThread* thread = aThread ? aThread : NS_GetCurrentThread();
84 // From a latency perspective, spinning the event loop is like leaving script
85 // and returning to the event loop. Tell the watchdog we stopped running
86 // script (until we return).
87 mozilla::Maybe<xpc::AutoScriptActivity> asa;
88 if (NS_IsMainThread()) {
89 asa.emplace(false);
92 while (!aPredicate()) {
93 bool didSomething = NS_ProcessNextEvent(thread, true);
95 if (Behavior == ProcessFailureBehavior::IgnoreAndContinue) {
96 // Don't care what happened, continue on.
97 continue;
98 } else if (!didSomething) {
99 return false;
103 return true;
106 } // namespace mozilla
108 #endif // xpcom_threads_SpinEventLoopUntil_h__