diff options
Diffstat (limited to 'dom/cache/Context.h')
-rw-r--r-- | dom/cache/Context.h | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/dom/cache/Context.h b/dom/cache/Context.h new file mode 100644 index 000000000..278302bf6 --- /dev/null +++ b/dom/cache/Context.h @@ -0,0 +1,235 @@ +/* -*- 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/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 { +namespace 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::AbortOperations() 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 +{ + typedef mozilla::dom::quota::DirectoryLock 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 + { + friend class Context; + public: + void AllowToClose(); + void InvalidateAndAllowToClose(); + private: + explicit ThreadsafeHandle(Context* aContext); + ~ThreadsafeHandle(); + + // disallow copying + ThreadsafeHandle(const ThreadsafeHandle&) = delete; + ThreadsafeHandle& operator=(const ThreadsafeHandle&) = delete; + + void AllowToCloseOnOwningThread(); + void InvalidateAndAllowToCloseOnOwningThread(); + + void ContextDestroyed(Context* aContext); + + // Cleared to allow the Context to close. Only safe to access on + // owning thread. + RefPtr<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<nsIThread> mOwningThread; + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(cache::Context::ThreadsafeHandle) + }; + + // 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 already_AddRefed<Context> + Create(Manager* aManager, nsIThread* aTarget, + Action* aInitAction, 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(Action* aAction); + + // 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); + + const QuotaInfo& + GetQuotaInfo() const + { + return mQuotaInfo; + } + + // 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; + RefPtr<Action> mAction; + }; + + Context(Manager* aManager, nsIThread* aTarget, Action* aInitAction); + ~Context(); + void Init(Context* aOldContext); + void Start(); + void DispatchAction(Action* aAction, bool aDoomData = false); + void OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo, + already_AddRefed<DirectoryLock> aDirectoryLock); + + + already_AddRefed<ThreadsafeHandle> + CreateThreadsafeHandle(); + + void + SetNextContext(Context* aNextContext); + + void + DoomTargetData(); + + RefPtr<Manager> mManager; + nsCOMPtr<nsIThread> mTarget; + RefPtr<Data> mData; + State mState; + bool mOrphanedData; + QuotaInfo mQuotaInfo; + RefPtr<QuotaInitRunnable> mInitRunnable; + RefPtr<Action> mInitAction; + nsTArray<PendingAction> mPendingActions; + + // Weak refs since activites must remove themselves from this list before + // being destroyed by calling RemoveActivity(). + typedef nsTObserverArray<Activity*> ActivityList; + ActivityList 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. + RefPtr<ThreadsafeHandle> mThreadsafeHandle; + + RefPtr<DirectoryLock> mDirectoryLock; + RefPtr<Context> mNextContext; + +public: + NS_INLINE_DECL_REFCOUNTING(cache::Context) +}; + +} // namespace cache +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_cache_Context_h |