/* -*- 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-mirroring machinery allows pieces of interesting state to be * observed on multiple thread without locking. The basic strategy is to track * changes in a canonical value and post updates to other threads that hold * mirrors for that value. * * One problem with the naive implementation of such a system is that some * pieces of state need to be updated atomically, and certain other operations * need to wait for these atomic updates to complete before executing. The * state-mirroring machinery solves this problem by requiring that its owner * thread uses tail dispatch, and posting state update events (which should * always be run first by TaskDispatcher implementations) to that tail * dispatcher. This ensures that state changes are always atomic from the * perspective of observing threads. * * Given that state-mirroring is an automatic background process, we try to * avoid burdening the caller with worrying too much about teardown. To that * end, we don't assert dispatch success for any of the notifications, and * assume that any canonical or mirror owned by a thread for whom dispatch fails * will soon be disconnected by its holder anyway. * * 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.
*/
namespace mozilla {
// Mirror<T> and Canonical<T> inherit WatchTarget, so we piggy-back on the // logging that WatchTarget already does. Given that, it makes sense to share // the same log module. #define MIRROR_LOG(x, ...) \
MOZ_ASSERT(gStateWatchingLog); \
MOZ_LOG(gStateWatchingLog, LogLevel::Debug, (x, ##__VA_ARGS__))
template <typename T> class AbstractMirror;
/* * AbstractCanonical is a superclass from which all Canonical values must * inherit. It serves as the interface of operations which may be performed (via * asynchronous dispatch) by other threads, in particular by the corresponding * Mirror value.
*/ template <typename T> class AbstractCanonical { public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractCanonical)
AbstractCanonical(AbstractThread* aThread) : mOwnerThread(aThread) {} virtualvoid AddMirror(AbstractMirror<T>* aMirror) = 0; virtualvoid RemoveMirror(AbstractMirror<T>* aMirror) = 0;
/* * AbstractMirror is a superclass from which all Mirror values must * inherit. It serves as the interface of operations which may be performed (via * asynchronous dispatch) by other threads, in particular by the corresponding * Canonical value.
*/ template <typename T> class AbstractMirror { public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractMirror)
AbstractMirror(AbstractThread* aThread) : mOwnerThread(aThread) {} virtualvoid ConnectedOnCanonicalThread(AbstractCanonical<T>* aCanonical) = 0; virtualvoid UpdateValue(const T& aNewValue) = 0; virtualvoid NotifyDisconnected() = 0;
/* * Canonical<T> is a wrapper class that allows a given value to be mirrored by * other threads. It maintains a list of active mirrors, and queues updates for * them when the internal value changes. When changing the value, the caller * needs to pass a TaskDispatcher object, which fires the updates at the * appropriate time. Canonical<T> is also a WatchTarget, and may be set up to * trigger other routines (on the same thread) when the canonical value changes. * * Canonical<T> is intended to be used as a member variable, so it doesn't * actually inherit AbstractCanonical<T> (a refcounted type). Rather, it * contains an inner class called |Impl| that implements most of the interesting * logic.
*/ template <typename T> class Canonical { public:
Canonical(AbstractThread* aThread, const T& aInitialValue, constchar* aName) {
mImpl = new Impl(aThread, aInitialValue, aName);
}
~Canonical() {}
private: class Impl : public AbstractCanonical<T>, public WatchTarget { public: using AbstractCanonical<T>::OwnerThread;
// Notify same-thread watchers. The state watching machinery will make // sure that notifications run at the right time.
NotifyWatchers();
// Check if we've already got a pending update. If so we won't schedule // another one. bool alreadyNotifying = mInitialValue.isSome();
// Stash the initial value if needed, then update to the new value. if (mInitialValue.isNothing()) {
mInitialValue.emplace(mValue);
}
mValue = aNewValue;
// We wait until things have stablized before sending state updates so // that we can avoid sending multiple updates, and possibly avoid sending // any updates at all if the value ends up where it started. if (!alreadyNotifying) {
AbstractThread::DispatchDirectTask(NewRunnableMethod( "Canonical::Impl::DoNotify", this, &Impl::DoNotify));
}
}
T mValue;
Maybe<T> mInitialValue;
nsTArray<RefPtr<AbstractMirror<T>>> mMirrors;
};
public: /* * Connect this Canonical to aMirror. Note that the canonical value starts * being mirrored to aMirror immediately, and requires one thread hop to * aMirror's owning thread before the connection is established. * * Note that this could race with Mirror::DisconnectIfConnected(). It is up to * the caller to provide the guarantee that disconnection happens after the * connection has been established. There is no race between this and * DisconnectAll().
*/ void ConnectMirror(AbstractMirror<T>* aMirror) { return mImpl->ConnectMirror(aMirror);
} void DisconnectAll() { return mImpl->DisconnectAll(); }
// Access to the Impl. operator Impl&() { return *mImpl; }
Impl* operator&() { return mImpl; }
/* * Mirror<T> is a wrapper class that allows a given value to mirror that of a * Canonical<T> owned by another thread. It registers itself with a * Canonical<T>, and is periodically updated with new values. Mirror<T> is also * a WatchTarget, and may be set up to trigger other routines (on the same * thread) when the mirrored value changes. * * Mirror<T> is intended to be used as a member variable, so it doesn't actually * inherit AbstractMirror<T> (a refcounted type). Rather, it contains an inner * class called |Impl| that implements most of the interesting logic.
*/ template <typename T> class Mirror { public:
Mirror(AbstractThread* aThread, const T& aInitialValue, constchar* aName) {
mImpl = new Impl(aThread, aInitialValue, aName);
}
~Mirror() { // As a member of complex objects, a Mirror<T> may be destroyed on a // different thread than its owner, or late in shutdown during CC. Given // that, we require manual disconnection so that callers can put things in // the right place.
MOZ_DIAGNOSTIC_ASSERT(!mImpl->IsConnected());
mImpl->AssertNoIncomingConnects();
}
private: class Impl : public AbstractMirror<T>, public WatchTarget { public: using AbstractMirror<T>::OwnerThread;
public: // Forward control operations to the Impl<T>. /* * Connect aCanonical to this Mirror. Note that this requires one thread hop * back to aCanonical's owning thread before the canonical value starts * being mirrored, and another to our owning thread before the connection is * established. * * Note that this mirror-initialized connection could race with * Canonical::DisconnectAll(). It is up to the caller to provide the guarantee * that disconnection happens after the connection has been established. There * is no race between this and DisconnectIfConnected().
*/ void Connect(AbstractCanonical<T>* aCanonical) { mImpl->Connect(aCanonical); } void DisconnectIfConnected() { mImpl->DisconnectIfConnected(); }
// Access to the Impl<T>. operator Impl&() { return *mImpl; }
Impl* operator&() { return mImpl; }
// Access to the T. const T& Ref() const { return *mImpl; } operatorconst T&() const { return Ref(); }
private:
RefPtr<Impl> mImpl;
};
#undef MIRROR_LOG
} // namespace mozilla
#endif
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet)
¤
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.