Bug 1932613 - temporarily disable browser_ml_end_to_end.js for permanent failures...
[gecko.git] / xpcom / threads / StateWatching.h
blob7fcc0f254f0c47511101984531317d10d0fd4a5b
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_STATEWATCHING_H_
8 #define XPCOM_THREADS_STATEWATCHING_H_
10 #include <cstddef>
11 #include <new>
12 #include <utility>
13 #include "mozilla/AbstractThread.h"
14 #include "mozilla/Assertions.h"
15 #include "mozilla/Logging.h"
16 #include "mozilla/RefPtr.h"
17 #include "nsCycleCollectionNoteChild.h"
18 #include "nsISupports.h"
19 #include "nsTArray.h"
20 #include "nsThreadUtils.h"
23 * The state-watching machinery automates the process of responding to changes
24 * in various pieces of state.
26 * A standard programming pattern is as follows:
28 * mFoo = ...;
29 * NotifyStuffChanged();
30 * ...
31 * mBar = ...;
32 * NotifyStuffChanged();
34 * This pattern is error-prone and difficult to audit because it requires the
35 * programmer to manually trigger the update routine. This can be especially
36 * problematic when the update routine depends on numerous pieces of state, and
37 * when that state is modified across a variety of helper methods. In these
38 * cases the responsibility for invoking the routine is often unclear, causing
39 * developers to scatter calls to it like pixie dust. This can result in
40 * duplicate invocations (which is wasteful) and missing invocations in corner-
41 * cases (which is a source of bugs).
43 * This file provides a set of primitives that automatically handle updates and
44 * allow the programmers to explicitly construct a graph of state dependencies.
45 * When used correctly, it eliminates the guess-work and wasted cycles described
46 * above.
48 * There are two basic pieces:
49 * (1) Objects that can be watched for updates. These inherit WatchTarget.
50 * (2) Objects that receive objects and trigger processing. These inherit
51 * AbstractWatcher. In the current machinery, these exist only internally
52 * within the WatchManager, though that could change.
54 * Note that none of this machinery is thread-safe - it must all happen on the
55 * same owning thread. To solve multi-threaded use-cases, use state mirroring
56 * and watch the mirrored value.
58 * Given that semantics may change and comments tend to go out of date, we
59 * deliberately don't provide usage examples here. Grep around to find them.
62 namespace mozilla {
64 extern LazyLogModule gStateWatchingLog;
66 #define WATCH_LOG(x, ...) \
67 MOZ_LOG(gStateWatchingLog, LogLevel::Debug, (x, ##__VA_ARGS__))
70 * AbstractWatcher is a superclass from which all watchers must inherit.
72 class AbstractWatcher {
73 public:
74 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractWatcher)
75 AbstractWatcher() : mDestroyed(false) {}
76 bool IsDestroyed() { return mDestroyed; }
77 virtual void Notify() = 0;
79 protected:
80 virtual ~AbstractWatcher() { MOZ_ASSERT(mDestroyed); }
81 bool mDestroyed;
85 * WatchTarget is a superclass from which all watchable things must inherit.
86 * Unlike AbstractWatcher, it is a fully-implemented Mix-in, and the subclass
87 * needs only to invoke NotifyWatchers when something changes.
89 * The functionality that this class provides is not threadsafe, and should only
90 * be used on the thread that owns that WatchTarget.
92 class WatchTarget {
93 public:
94 explicit WatchTarget(const char* aName) : mName(aName) {}
96 void AddWatcher(AbstractWatcher* aWatcher) {
97 MOZ_ASSERT(!mWatchers.Contains(aWatcher));
98 mWatchers.AppendElement(aWatcher);
101 void RemoveWatcher(AbstractWatcher* aWatcher) {
102 MOZ_ASSERT(mWatchers.Contains(aWatcher));
103 mWatchers.RemoveElement(aWatcher);
106 protected:
107 void NotifyWatchers() {
108 WATCH_LOG("%s[%p] notifying watchers\n", mName, this);
109 PruneWatchers();
110 for (size_t i = 0; i < mWatchers.Length(); ++i) {
111 mWatchers[i]->Notify();
115 private:
116 // We don't have Watchers explicitly unregister themselves when they die,
117 // because then they'd need back-references to all the WatchTargets they're
118 // subscribed to, and WatchTargets aren't reference-counted. So instead we
119 // just prune dead ones at appropriate times, which works just fine.
120 void PruneWatchers() {
121 mWatchers.RemoveElementsBy(
122 [](const auto& watcher) { return watcher->IsDestroyed(); });
125 nsTArray<RefPtr<AbstractWatcher>> mWatchers;
127 protected:
128 const char* mName;
132 * Watchable is a wrapper class that turns any primitive into a WatchTarget.
134 template <typename T>
135 class Watchable : public WatchTarget {
136 public:
137 Watchable(const T& aInitialValue, const char* aName)
138 : WatchTarget(aName), mValue(aInitialValue) {}
140 const T& Ref() const { return mValue; }
141 operator const T&() const { return Ref(); }
142 template <typename U>
143 Watchable& operator=(U&& aNewValue) {
144 if (aNewValue != mValue) {
145 mValue = std::forward<U>(aNewValue);
146 NotifyWatchers();
149 return *this;
152 private:
153 Watchable(const Watchable& aOther) = delete;
154 Watchable& operator=(const Watchable& aOther) = delete;
156 T mValue;
159 template <typename T>
160 inline void ImplCycleCollectionUnlink(Watchable<T>& aField) {
161 ImplCycleCollectionUnlink<T>(aField);
164 template <typename T>
165 inline void ImplCycleCollectionTraverse(
166 nsCycleCollectionTraversalCallback& aCallback, const Watchable<T>& aField,
167 const char* aName, uint32_t aFlags = 0) {
168 ImplCycleCollectionTraverse(aCallback, aField.Ref(), aName, aFlags);
171 // Manager class for state-watching. Declare one of these in any class for which
172 // you want to invoke method callbacks.
174 // Internally, WatchManager maintains one AbstractWatcher per callback method.
175 // Consumers invoke Watch/Unwatch on a particular (WatchTarget, Callback) tuple.
176 // This causes an AbstractWatcher for |Callback| to be instantiated if it
177 // doesn't already exist, and registers it with |WatchTarget|.
179 // Using Direct Tasks on the TailDispatcher, WatchManager ensures that we fire
180 // watch callbacks no more than once per task, once all other operations for
181 // that task have been completed.
183 // WatchManager<OwnerType> is intended to be declared as a member of |OwnerType|
184 // objects. Given that, it and its owned objects can't hold permanent strong
185 // refs to the owner, since that would keep the owner alive indefinitely.
186 // Instead, it _only_ holds strong refs while waiting for Direct Tasks to fire.
187 // This ensures that everything is kept alive just long enough.
188 template <typename OwnerType>
189 class WatchManager {
190 public:
191 typedef void (OwnerType::*CallbackMethod)();
192 explicit WatchManager(OwnerType* aOwner, AbstractThread* aOwnerThread)
193 : mOwner(aOwner), mOwnerThread(aOwnerThread) {}
195 ~WatchManager() {
196 if (!IsShutdown()) {
197 Shutdown();
201 bool IsShutdown() const { return !mOwner; }
203 // Shutdown needs to happen on mOwnerThread. If the WatchManager will be
204 // destroyed on a different thread, Shutdown() must be called manually.
205 void Shutdown() {
206 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
207 for (auto& watcher : mWatchers) {
208 watcher->Destroy();
210 mWatchers.Clear();
211 mOwner = nullptr;
214 void Watch(WatchTarget& aTarget, CallbackMethod aMethod) {
215 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
216 aTarget.AddWatcher(&EnsureWatcher(aMethod));
219 void Unwatch(WatchTarget& aTarget, CallbackMethod aMethod) {
220 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
221 PerCallbackWatcher* watcher = GetWatcher(aMethod);
222 MOZ_ASSERT(watcher);
223 aTarget.RemoveWatcher(watcher);
226 void ManualNotify(CallbackMethod aMethod) {
227 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
228 PerCallbackWatcher* watcher = GetWatcher(aMethod);
229 MOZ_ASSERT(watcher);
230 watcher->Notify();
233 private:
234 class PerCallbackWatcher : public AbstractWatcher {
235 public:
236 PerCallbackWatcher(OwnerType* aOwner, AbstractThread* aOwnerThread,
237 CallbackMethod aMethod)
238 : mOwner(aOwner),
239 mOwnerThread(aOwnerThread),
240 mCallbackMethod(aMethod) {}
242 void Destroy() {
243 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
244 mDestroyed = true;
245 mOwner = nullptr;
248 void Notify() override {
249 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
250 MOZ_DIAGNOSTIC_ASSERT(mOwner,
251 "mOwner is only null after destruction, "
252 "at which point we shouldn't be notified");
253 if (mNotificationPending) {
254 // We've already got a notification job in the pipe.
255 return;
257 mNotificationPending = true;
259 // Queue up our notification jobs to run in a stable state.
260 AbstractThread::DispatchDirectTask(
261 NS_NewRunnableFunction("WatchManager::PerCallbackWatcher::Notify",
262 [self = RefPtr<PerCallbackWatcher>(this),
263 owner = RefPtr<OwnerType>(mOwner)]() {
264 if (!self->mDestroyed) {
265 ((*owner).*(self->mCallbackMethod))();
267 self->mNotificationPending = false;
268 }));
271 bool CallbackMethodIs(CallbackMethod aMethod) const {
272 return mCallbackMethod == aMethod;
275 private:
276 ~PerCallbackWatcher() = default;
278 OwnerType* mOwner; // Never null.
279 bool mNotificationPending = false;
280 RefPtr<AbstractThread> mOwnerThread;
281 CallbackMethod mCallbackMethod;
284 PerCallbackWatcher* GetWatcher(CallbackMethod aMethod) {
285 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
286 for (auto& watcher : mWatchers) {
287 if (watcher->CallbackMethodIs(aMethod)) {
288 return watcher;
291 return nullptr;
294 PerCallbackWatcher& EnsureWatcher(CallbackMethod aMethod) {
295 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
296 PerCallbackWatcher* watcher = GetWatcher(aMethod);
297 if (watcher) {
298 return *watcher;
300 watcher = mWatchers
301 .AppendElement(MakeAndAddRef<PerCallbackWatcher>(
302 mOwner, mOwnerThread, aMethod))
303 ->get();
304 return *watcher;
307 nsTArray<RefPtr<PerCallbackWatcher>> mWatchers;
308 OwnerType* mOwner;
309 RefPtr<AbstractThread> mOwnerThread;
312 #undef WATCH_LOG
314 } // namespace mozilla
316 #endif