/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* * The state-watching machinery automates the process of responding to changes * in various pieces of state. * * A standard programming pattern is as follows: * * mFoo = ...; * NotifyStuffChanged(); * ... * mBar = ...; * NotifyStuffChanged(); * * This pattern is error-prone and difficult to audit because it requires the * programmer to manually trigger the update routine. This can be especially * problematic when the update routine depends on numerous pieces of state, and * when that state is modified across a variety of helper methods. In these * cases the responsibility for invoking the routine is often unclear, causing * developers to scatter calls to it like pixie dust. This can result in * duplicate invocations (which is wasteful) and missing invocations in corner- * cases (which is a source of bugs). * * This file provides a set of primitives that automatically handle updates and * allow the programmers to explicitly construct a graph of state dependencies. * When used correctly, it eliminates the guess-work and wasted cycles described * above. * * There are two basic pieces: * (1) Objects that can be watched for updates. These inherit WatchTarget. * (2) Objects that receive objects and trigger processing. These inherit * AbstractWatcher. In the current machinery, these exist only internally * within the WatchManager, though that could change. * * Note that none of this machinery is thread-safe - it must all happen on the * same owning thread. To solve multi-threaded use-cases, use state mirroring * and watch the mirrored value. * * Given that semantics may change and comments tend to go out of date, we * deliberately don't provide usage examples here. Grep around to find them.
*/
/* * AbstractWatcher is a superclass from which all watchers must inherit.
*/ class AbstractWatcher { public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractWatcher)
AbstractWatcher() : mDestroyed(false) {} bool IsDestroyed() { return mDestroyed; } virtualvoid Notify() = 0;
/* * WatchTarget is a superclass from which all watchable things must inherit. * Unlike AbstractWatcher, it is a fully-implemented Mix-in, and the subclass * needs only to invoke NotifyWatchers when something changes. * * The functionality that this class provides is not threadsafe, and should only * be used on the thread that owns that WatchTarget.
*/ class WatchTarget { public: explicit WatchTarget(constchar* aName) : mName(aName) {}
protected: void NotifyWatchers() {
WATCH_LOG("%s[%p] notifying watchers\n", mName, this);
PruneWatchers(); for (size_t i = 0; i < mWatchers.Length(); ++i) {
mWatchers[i]->Notify();
}
}
private: // We don't have Watchers explicitly unregister themselves when they die, // because then they'd need back-references to all the WatchTargets they're // subscribed to, and WatchTargets aren't reference-counted. So instead we // just prune dead ones at appropriate times, which works just fine. void PruneWatchers() {
mWatchers.RemoveElementsBy(
[](constauto& watcher) { return watcher->IsDestroyed(); });
}
nsTArray<RefPtr<AbstractWatcher>> mWatchers;
protected: constchar* mName;
};
/* * Watchable is a wrapper class that turns any primitive into a WatchTarget.
*/ template <typename T> class Watchable : public WatchTarget { public:
Watchable(const T& aInitialValue, constchar* aName)
: WatchTarget(aName), mValue(aInitialValue) {}
// Manager class for state-watching. Declare one of these in any class for which // you want to invoke method callbacks. // // Internally, WatchManager maintains one AbstractWatcher per callback method. // Consumers invoke Watch/Unwatch on a particular (WatchTarget, Callback) tuple. // This causes an AbstractWatcher for |Callback| to be instantiated if it // doesn't already exist, and registers it with |WatchTarget|. // // Using Direct Tasks on the TailDispatcher, WatchManager ensures that we fire // watch callbacks no more than once per task, once all other operations for // that task have been completed. // // WatchManager<OwnerType> is intended to be declared as a member of |OwnerType| // objects. Given that, it and its owned objects can't hold permanent strong // refs to the owner, since that would keep the owner alive indefinitely. // Instead, it _only_ holds strong refs while waiting for Direct Tasks to fire. // This ensures that everything is kept alive just long enough. template <typename OwnerType> class WatchManager { public: typedefvoid (OwnerType::*CallbackMethod)(); explicit WatchManager(OwnerType* aOwner, AbstractThread* aOwnerThread)
: mOwner(aOwner), mOwnerThread(aOwnerThread) {}
~WatchManager() { if (!IsShutdown()) {
Shutdown();
}
}
bool IsShutdown() const { return !mOwner; }
// Shutdown needs to happen on mOwnerThread. If the WatchManager will be // destroyed on a different thread, Shutdown() must be called manually. void Shutdown() {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); for (auto& watcher : mWatchers) {
watcher->Destroy();
}
mWatchers.Clear();
mOwner = nullptr;
}
void Notify() override {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
MOZ_DIAGNOSTIC_ASSERT(mOwner, "mOwner is only null after destruction, " "at which point we shouldn't be notified"); if (mNotificationPending) { // We've already got a notification job in the pipe. return;
}
mNotificationPending = true;
// Queue up our notification jobs to run in a stable state.
AbstractThread::DispatchDirectTask(
NS_NewRunnableFunction("WatchManager::PerCallbackWatcher::Notify",
[self = RefPtr<PerCallbackWatcher>(this),
owner = RefPtr<OwnerType>(mOwner)]() { if (!self->mDestroyed) {
((*owner).*(self->mCallbackMethod))();
}
self->mNotificationPending = false;
}));
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.7Bemerkung:
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.