diff options
Diffstat (limited to 'dom/cache/Context.h')
-rw-r--r-- | dom/cache/Context.h | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/dom/cache/Context.h b/dom/cache/Context.h new file mode 100644 index 0000000000..036f315b82 --- /dev/null +++ b/dom/cache/Context.h @@ -0,0 +1,224 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_cache_Context_h +#define mozilla_dom_cache_Context_h + +#include "mozilla/dom/SafeRefPtr.h" +#include "mozilla/dom/cache/Types.h" +#include "nsCOMPtr.h" +#include "nsISupportsImpl.h" +#include "nsProxyRelease.h" +#include "nsString.h" +#include "nsTArray.h" +#include "nsTObserverArray.h" + +class nsIEventTarget; +class nsIThread; + +namespace mozilla::dom { + +namespace quota { + +class DirectoryLock; + +} // namespace quota + +namespace cache { + +class Action; +class Manager; + +// The Context class is RAII-style class for managing IO operations within the +// Cache. +// +// When a Context is created it performs the complicated steps necessary to +// initialize the QuotaManager. Action objects dispatched on the Context are +// delayed until this initialization is complete. They are then allow to +// execute on any specified thread. Once all references to the Context are +// gone, then the steps necessary to release the QuotaManager are performed. +// After initialization the Context holds a self reference, so it will stay +// alive until one of three conditions occur: +// +// 1) The Manager will call Context::AllowToClose() when all of the actors +// have removed themselves as listener. This means an idle context with +// no active DOM objects will close gracefully. +// 2) The QuotaManager aborts all operations so it can delete the files. +// In this case the QuotaManager calls Client::AbortOperationsForLocks() +// which in turn cancels all existing Action objects and then marks the +// Manager as invalid. +// 3) Browser shutdown occurs and the Manager calls Context::CancelAll(). +// +// In either case, though, the Action objects must be destroyed first to +// allow the Context to be destroyed. +// +// While the Context performs operations asynchronously on threads, all of +// methods in its public interface must be called on the same thread +// originally used to create the Context. +// +// As an invariant, all Context objects must be destroyed before permitting +// the "profile-before-change" shutdown event to complete. This is ensured +// via the code in ShutdownObserver.cpp. +class Context final : public SafeRefCounted<Context> { + using DirectoryLock = mozilla::dom::quota::DirectoryLock; + + public: + // Define a class allowing other threads to hold the Context alive. This also + // allows these other threads to safely close or cancel the Context. + class ThreadsafeHandle final : public AtomicSafeRefCounted<ThreadsafeHandle> { + friend class Context; + + public: + explicit ThreadsafeHandle(SafeRefPtr<Context> aContext); + ~ThreadsafeHandle(); + + MOZ_DECLARE_REFCOUNTED_TYPENAME(cache::Context::ThreadsafeHandle) + + void AllowToClose(); + void InvalidateAndAllowToClose(); + + private: + void AllowToCloseOnOwningThread(); + void InvalidateAndAllowToCloseOnOwningThread(); + + void ContextDestroyed(Context& aContext); + + // Cleared to allow the Context to close. Only safe to access on + // owning thread. + SafeRefPtr<Context> mStrongRef; + + // Used to support cancelation even while the Context is already allowed + // to close. Cleared by ~Context() calling ContextDestroyed(). Only + // safe to access on owning thread. + Context* mWeakRef; + + nsCOMPtr<nsISerialEventTarget> mOwningEventTarget; + }; + + // Different objects hold references to the Context while some work is being + // performed asynchronously. These objects must implement the Activity + // interface and register themselves with the AddActivity(). When they are + // destroyed they must call RemoveActivity(). This allows the Context to + // cancel any outstanding Activity work when the Context is cancelled. + class Activity { + public: + virtual void Cancel() = 0; + virtual bool MatchesCacheId(CacheId aCacheId) const = 0; + }; + + // Create a Context attached to the given Manager. The given Action + // will run on the QuotaManager IO thread. Note, this Action must + // be execute synchronously. + static SafeRefPtr<Context> Create(SafeRefPtr<Manager> aManager, + nsISerialEventTarget* aTarget, + SafeRefPtr<Action> aInitAction, + Maybe<Context&> aOldContext); + + // Execute given action on the target once the quota manager has been + // initialized. + // + // Only callable from the thread that created the Context. + void Dispatch(SafeRefPtr<Action> aAction); + + Maybe<DirectoryLock&> MaybeDirectoryLockRef() const; + + // Cancel any Actions running or waiting to run. This should allow the + // Context to be released and Listener::RemoveContext() will be called + // when complete. + // + // Only callable from the thread that created the Context. + void CancelAll(); + + // True if CancelAll() has been called. + bool IsCanceled() const; + + // Like CancelAll(), but also marks the Manager as "invalid". + void Invalidate(); + + // Remove any self references and allow the Context to be released when + // there are no more Actions to process. + void AllowToClose(); + + // Cancel any Actions running or waiting to run that operate on the given + // cache ID. + // + // Only callable from the thread that created the Context. + void CancelForCacheId(CacheId aCacheId); + + void AddActivity(Activity& aActivity); + void RemoveActivity(Activity& aActivity); + + // Tell the Context that some state information has been orphaned in the + // data store and won't be cleaned up. The Context will leave the marker + // in place to trigger cleanup the next times its opened. + void NoteOrphanedData(); + + private: + class Data; + class QuotaInitRunnable; + class ActionRunnable; + + enum State { + STATE_CONTEXT_PREINIT, + STATE_CONTEXT_INIT, + STATE_CONTEXT_READY, + STATE_CONTEXT_CANCELED + }; + + struct PendingAction { + nsCOMPtr<nsIEventTarget> mTarget; + SafeRefPtr<Action> mAction; + }; + + void Init(Maybe<Context&> aOldContext); + void Start(); + void DispatchAction(SafeRefPtr<Action> aAction, bool aDoomData = false); + void OnQuotaInit(nsresult aRv, + const Maybe<CacheDirectoryMetadata>& aDirectoryMetadata, + already_AddRefed<DirectoryLock> aDirectoryLock); + + SafeRefPtr<ThreadsafeHandle> CreateThreadsafeHandle(); + + void SetNextContext(SafeRefPtr<Context> aNextContext); + + void DoomTargetData(); + + SafeRefPtr<Manager> mManager; + nsCOMPtr<nsISerialEventTarget> mTarget; + RefPtr<Data> mData; + State mState; + bool mOrphanedData; + Maybe<CacheDirectoryMetadata> mDirectoryMetadata; + RefPtr<QuotaInitRunnable> mInitRunnable; + SafeRefPtr<Action> mInitAction; + nsTArray<PendingAction> mPendingActions; + + // Weak refs since activites must remove themselves from this list before + // being destroyed by calling RemoveActivity(). + nsTObserverArray<NotNull<Activity*>> mActivityList; + + // The ThreadsafeHandle may have a strong ref back to us. This creates + // a ref-cycle that keeps the Context alive. The ref-cycle is broken + // when ThreadsafeHandle::AllowToClose() is called. + SafeRefPtr<ThreadsafeHandle> mThreadsafeHandle; + + RefPtr<DirectoryLock> mDirectoryLock; + SafeRefPtr<Context> mNextContext; + + public: + // XXX Consider adding a private guard parameter. + Context(SafeRefPtr<Manager> aManager, nsISerialEventTarget* aTarget, + SafeRefPtr<Action> aInitAction); + ~Context(); + + NS_DECL_OWNINGTHREAD + MOZ_DECLARE_REFCOUNTED_TYPENAME(cache::Context) +}; + +} // namespace cache +} // namespace mozilla::dom + +#endif // mozilla_dom_cache_Context_h |