summaryrefslogtreecommitdiffstats
path: root/dom/cache/Context.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/cache/Context.h224
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