Bug 1755481: correct documentation of `nsIClipboard::getData`. r=mccr8
[gecko.git] / xpcom / threads / StateWatching.h
blob3da0c63bfeefc039121ebdcc2556f9075e79f132
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 #if !defined(StateWatching_h_)
8 # define 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 "nsISupports.h"
18 # include "nsTArray.h"
19 # include "nsThreadUtils.h"
22 * The state-watching machinery automates the process of responding to changes
23 * in various pieces of state.
25 * A standard programming pattern is as follows:
27 * mFoo = ...;
28 * NotifyStuffChanged();
29 * ...
30 * mBar = ...;
31 * NotifyStuffChanged();
33 * This pattern is error-prone and difficult to audit because it requires the
34 * programmer to manually trigger the update routine. This can be especially
35 * problematic when the update routine depends on numerous pieces of state, and
36 * when that state is modified across a variety of helper methods. In these
37 * cases the responsibility for invoking the routine is often unclear, causing
38 * developers to scatter calls to it like pixie dust. This can result in
39 * duplicate invocations (which is wasteful) and missing invocations in corner-
40 * cases (which is a source of bugs).
42 * This file provides a set of primitives that automatically handle updates and
43 * allow the programmers to explicitly construct a graph of state dependencies.
44 * When used correctly, it eliminates the guess-work and wasted cycles described
45 * above.
47 * There are two basic pieces:
48 * (1) Objects that can be watched for updates. These inherit WatchTarget.
49 * (2) Objects that receive objects and trigger processing. These inherit
50 * AbstractWatcher. In the current machinery, these exist only internally
51 * within the WatchManager, though that could change.
53 * Note that none of this machinery is thread-safe - it must all happen on the
54 * same owning thread. To solve multi-threaded use-cases, use state mirroring
55 * and watch the mirrored value.
57 * Given that semantics may change and comments tend to go out of date, we
58 * deliberately don't provide usage examples here. Grep around to find them.
61 namespace mozilla {
63 extern LazyLogModule gStateWatchingLog;
65 # define WATCH_LOG(x, ...) \
66 MOZ_LOG(gStateWatchingLog, LogLevel::Debug, (x, ##__VA_ARGS__))
69 * AbstractWatcher is a superclass from which all watchers must inherit.
71 class AbstractWatcher {
72 public:
73 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractWatcher)
74 AbstractWatcher() : mDestroyed(false) {}
75 bool IsDestroyed() { return mDestroyed; }
76 virtual void Notify() = 0;
78 protected:
79 virtual ~AbstractWatcher() { MOZ_ASSERT(mDestroyed); }
80 bool mDestroyed;
84 * WatchTarget is a superclass from which all watchable things must inherit.
85 * Unlike AbstractWatcher, it is a fully-implemented Mix-in, and the subclass
86 * needs only to invoke NotifyWatchers when something changes.
88 * The functionality that this class provides is not threadsafe, and should only
89 * be used on the thread that owns that WatchTarget.
91 class WatchTarget {
92 public:
93 explicit WatchTarget(const char* aName) : mName(aName) {}
95 void AddWatcher(AbstractWatcher* aWatcher) {
96 MOZ_ASSERT(!mWatchers.Contains(aWatcher));
97 mWatchers.AppendElement(aWatcher);
100 void RemoveWatcher(AbstractWatcher* aWatcher) {
101 MOZ_ASSERT(mWatchers.Contains(aWatcher));
102 mWatchers.RemoveElement(aWatcher);
105 protected:
106 void NotifyWatchers() {
107 WATCH_LOG("%s[%p] notifying watchers\n", mName, this);
108 PruneWatchers();
109 for (size_t i = 0; i < mWatchers.Length(); ++i) {
110 mWatchers[i]->Notify();
114 private:
115 // We don't have Watchers explicitly unregister themselves when they die,
116 // because then they'd need back-references to all the WatchTargets they're
117 // subscribed to, and WatchTargets aren't reference-counted. So instead we
118 // just prune dead ones at appropriate times, which works just fine.
119 void PruneWatchers() {
120 mWatchers.RemoveElementsBy(
121 [](const auto& watcher) { return watcher->IsDestroyed(); });
124 nsTArray<RefPtr<AbstractWatcher>> mWatchers;
126 protected:
127 const char* mName;
131 * Watchable is a wrapper class that turns any primitive into a WatchTarget.
133 template <typename T>
134 class Watchable : public WatchTarget {
135 public:
136 Watchable(const T& aInitialValue, const char* aName)
137 : WatchTarget(aName), mValue(aInitialValue) {}
139 const T& Ref() const { return mValue; }
140 operator const T&() const { return Ref(); }
141 Watchable& operator=(const T& aNewValue) {
142 if (aNewValue != mValue) {
143 mValue = aNewValue;
144 NotifyWatchers();
147 return *this;
150 private:
151 Watchable(const Watchable& aOther) = delete;
152 Watchable& operator=(const Watchable& aOther) = delete;
154 T mValue;
157 // Manager class for state-watching. Declare one of these in any class for which
158 // you want to invoke method callbacks.
160 // Internally, WatchManager maintains one AbstractWatcher per callback method.
161 // Consumers invoke Watch/Unwatch on a particular (WatchTarget, Callback) tuple.
162 // This causes an AbstractWatcher for |Callback| to be instantiated if it
163 // doesn't already exist, and registers it with |WatchTarget|.
165 // Using Direct Tasks on the TailDispatcher, WatchManager ensures that we fire
166 // watch callbacks no more than once per task, once all other operations for
167 // that task have been completed.
169 // WatchManager<OwnerType> is intended to be declared as a member of |OwnerType|
170 // objects. Given that, it and its owned objects can't hold permanent strong
171 // refs to the owner, since that would keep the owner alive indefinitely.
172 // Instead, it _only_ holds strong refs while waiting for Direct Tasks to fire.
173 // This ensures that everything is kept alive just long enough.
174 template <typename OwnerType>
175 class WatchManager {
176 public:
177 typedef void (OwnerType::*CallbackMethod)();
178 explicit WatchManager(OwnerType* aOwner, AbstractThread* aOwnerThread)
179 : mOwner(aOwner), mOwnerThread(aOwnerThread) {}
181 ~WatchManager() {
182 if (!IsShutdown()) {
183 Shutdown();
187 bool IsShutdown() const { return !mOwner; }
189 // Shutdown needs to happen on mOwnerThread. If the WatchManager will be
190 // destroyed on a different thread, Shutdown() must be called manually.
191 void Shutdown() {
192 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
193 for (auto& watcher : mWatchers) {
194 watcher->Destroy();
196 mWatchers.Clear();
197 mOwner = nullptr;
200 void Watch(WatchTarget& aTarget, CallbackMethod aMethod) {
201 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
202 aTarget.AddWatcher(&EnsureWatcher(aMethod));
205 void Unwatch(WatchTarget& aTarget, CallbackMethod aMethod) {
206 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
207 PerCallbackWatcher* watcher = GetWatcher(aMethod);
208 MOZ_ASSERT(watcher);
209 aTarget.RemoveWatcher(watcher);
212 void ManualNotify(CallbackMethod aMethod) {
213 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
214 PerCallbackWatcher* watcher = GetWatcher(aMethod);
215 MOZ_ASSERT(watcher);
216 watcher->Notify();
219 private:
220 class PerCallbackWatcher : public AbstractWatcher {
221 public:
222 PerCallbackWatcher(OwnerType* aOwner, AbstractThread* aOwnerThread,
223 CallbackMethod aMethod)
224 : mOwner(aOwner),
225 mOwnerThread(aOwnerThread),
226 mCallbackMethod(aMethod) {}
228 void Destroy() {
229 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
230 mDestroyed = true;
231 mOwner = nullptr;
234 void Notify() override {
235 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
236 MOZ_DIAGNOSTIC_ASSERT(mOwner,
237 "mOwner is only null after destruction, "
238 "at which point we shouldn't be notified");
239 if (mNotificationPending) {
240 // We've already got a notification job in the pipe.
241 return;
243 mNotificationPending = true;
245 // Queue up our notification jobs to run in a stable state.
246 AbstractThread::DispatchDirectTask(
247 NS_NewRunnableFunction("WatchManager::PerCallbackWatcher::Notify",
248 [self = RefPtr<PerCallbackWatcher>(this),
249 owner = RefPtr<OwnerType>(mOwner)]() {
250 if (!self->mDestroyed) {
251 ((*owner).*(self->mCallbackMethod))();
253 self->mNotificationPending = false;
254 }));
257 bool CallbackMethodIs(CallbackMethod aMethod) const {
258 return mCallbackMethod == aMethod;
261 private:
262 ~PerCallbackWatcher() = default;
264 OwnerType* mOwner; // Never null.
265 bool mNotificationPending = false;
266 RefPtr<AbstractThread> mOwnerThread;
267 CallbackMethod mCallbackMethod;
270 PerCallbackWatcher* GetWatcher(CallbackMethod aMethod) {
271 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
272 for (auto& watcher : mWatchers) {
273 if (watcher->CallbackMethodIs(aMethod)) {
274 return watcher;
277 return nullptr;
280 PerCallbackWatcher& EnsureWatcher(CallbackMethod aMethod) {
281 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
282 PerCallbackWatcher* watcher = GetWatcher(aMethod);
283 if (watcher) {
284 return *watcher;
286 watcher = mWatchers
287 .AppendElement(MakeAndAddRef<PerCallbackWatcher>(
288 mOwner, mOwnerThread, aMethod))
289 ->get();
290 return *watcher;
293 nsTArray<RefPtr<PerCallbackWatcher>> mWatchers;
294 OwnerType* mOwner;
295 RefPtr<AbstractThread> mOwnerThread;
298 # undef WATCH_LOG
300 } // namespace mozilla
302 #endif