Skip to content

Commit

Permalink
Bug 1110487 P2 Implement the nsIOfflineStorage interface in Cache.
Browse files Browse the repository at this point in the history
  • Loading branch information
rmottola committed Jun 26, 2019
1 parent 3b01c38 commit e61a6d5
Show file tree
Hide file tree
Showing 13 changed files with 646 additions and 88 deletions.
245 changes: 205 additions & 40 deletions dom/cache/Context.cpp

Large diffs are not rendered by default.

103 changes: 97 additions & 6 deletions dom/cache/Context.h
Expand Up @@ -11,17 +11,21 @@
#include "nsAutoPtr.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 cache {

class Action;
class Manager;
class OfflineStorage;

// The Context class is RAII-style class for managing IO operations within the
// Cache.
Expand All @@ -31,8 +35,20 @@ class Manager;
// 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.
// Since pending Action objects reference the Context, this allows overlapping
// IO to opportunistically run without re-initializing the QuotaManager again.
// 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 invalidates the storage area so it can delete the
// files. In this case the OfflineStorage calls Cache::Invalidate() 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
Expand All @@ -44,6 +60,53 @@ class Manager;
class Context final
{
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.
nsRefPtr<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;
};

static already_AddRefed<Context>
Create(Manager* aManager, Action* aQuotaIOThreadAction);

Expand All @@ -60,12 +123,28 @@ class Context final
// Only callable from the thread that created the Context.
void CancelAll();

// 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;
}

private:
class QuotaInitRunnable;
class ActionRunnable;
Expand All @@ -86,16 +165,28 @@ class Context final
explicit Context(Manager* aManager);
~Context();
void DispatchAction(nsIEventTarget* aTarget, Action* aAction);
void OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo);
void OnActionRunnableComplete(ActionRunnable* const aAction);
void OnQuotaInit(nsresult aRv, const QuotaInfo& aQuotaInfo,
nsMainThreadPtrHandle<OfflineStorage>& aOfflineStorage);

already_AddRefed<ThreadsafeHandle>
CreateThreadsafeHandle();

nsRefPtr<Manager> mManager;
State mState;
QuotaInfo mQuotaInfo;
nsTArray<PendingAction> mPendingActions;

// weak refs since ~ActionRunnable() removes itself from this list
nsTArray<ActionRunnable*> mActionRunnables;
// 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.
nsRefPtr<ThreadsafeHandle> mThreadsafeHandle;

nsMainThreadPtrHandle<OfflineStorage> mOfflineStorage;

public:
NS_INLINE_DECL_REFCOUNTING(cache::Context)
Expand Down
75 changes: 46 additions & 29 deletions dom/cache/Manager.cpp
Expand Up @@ -180,7 +180,10 @@ class Manager::Factory
ManagerList::ForwardIterator iter(sFactory->mManagerList);
while (iter.HasMore()) {
nsRefPtr<Manager> manager = iter.GetNext();
if (*manager->mManagerId == *aManagerId) {
// If there is an invalid Manager finishing up and a new Manager
// is created for the same origin, then the new Manager will
// be blocked until QuotaManager finishes clearing the origin.
if (manager->IsValid() && *manager->mManagerId == *aManagerId) {
return manager.forget();
}
}
Expand Down Expand Up @@ -1407,6 +1410,9 @@ Manager::RemoveListener(Listener* aListener)
mListeners.RemoveElement(aListener, ListenerEntryListenerComparator());
MOZ_ASSERT(!mListeners.Contains(aListener,
ListenerEntryListenerComparator()));
if (mListeners.IsEmpty() && mContext) {
mContext->AllowToClose();
}
}

void
Expand All @@ -1424,6 +1430,21 @@ Manager::RemoveContext(Context* aContext)
}
}

void
Manager::Invalidate()
{
NS_ASSERT_OWNINGTHREAD(Manager);
// QuotaManager can trigger this more than once.
mValid = false;
}

bool
Manager::IsValid() const
{
NS_ASSERT_OWNINGTHREAD(Manager);
return mValid;
}

void
Manager::AddRefCacheId(CacheId aCacheId)
{
Expand Down Expand Up @@ -1453,7 +1474,7 @@ Manager::ReleaseCacheId(CacheId aCacheId)
bool orphaned = mCacheIdRefs[i].mOrphaned;
mCacheIdRefs.RemoveElementAt(i);
// TODO: note that we need to check this cache for staleness on startup (bug 1110446)
if (orphaned && !mShuttingDown) {
if (orphaned && !mShuttingDown && mValid) {
nsRefPtr<Context> context = CurrentContext();
context->CancelForCacheId(aCacheId);
nsRefPtr<Action> action = new DeleteOrphanedCacheAction(this,
Expand Down Expand Up @@ -1496,7 +1517,7 @@ Manager::ReleaseBodyId(const nsID& aBodyId)
bool orphaned = mBodyIdRefs[i].mOrphaned;
mBodyIdRefs.RemoveElementAt(i);
// TODO: note that we need to check this body for staleness on startup (bug 1110446)
if (orphaned && !mShuttingDown) {
if (orphaned && !mShuttingDown && mValid) {
nsRefPtr<Action> action = new DeleteOrphanedBodyAction(aBodyId);
nsRefPtr<Context> context = CurrentContext();
context->Dispatch(mIOThread, action);
Expand Down Expand Up @@ -1538,9 +1559,8 @@ Manager::CacheMatch(Listener* aListener, RequestId aRequestId, CacheId aCacheId,
{
NS_ASSERT_OWNINGTHREAD(Manager);
MOZ_ASSERT(aListener);
if (mShuttingDown) {
aListener->OnCacheMatch(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
nullptr, nullptr);
if (mShuttingDown || !mValid) {
aListener->OnCacheMatch(aRequestId, NS_ERROR_FAILURE, nullptr, nullptr);
return;
}
nsRefPtr<Context> context = CurrentContext();
Expand All @@ -1559,8 +1579,8 @@ Manager::CacheMatchAll(Listener* aListener, RequestId aRequestId,
{
NS_ASSERT_OWNINGTHREAD(Manager);
MOZ_ASSERT(aListener);
if (mShuttingDown) {
aListener->OnCacheMatchAll(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
if (mShuttingDown || !mValid) {
aListener->OnCacheMatchAll(aRequestId, NS_ERROR_FAILURE,
nsTArray<SavedResponse>(), nullptr);
return;
}
Expand All @@ -1581,8 +1601,8 @@ Manager::CachePutAll(Listener* aListener, RequestId aRequestId, CacheId aCacheId
{
NS_ASSERT_OWNINGTHREAD(Manager);
MOZ_ASSERT(aListener);
if (mShuttingDown) {
aListener->OnCachePutAll(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN);
if (mShuttingDown || !mValid) {
aListener->OnCachePutAll(aRequestId, NS_ERROR_FAILURE);
return;
}
ListenerId listenerId = SaveListener(aListener);
Expand All @@ -1601,8 +1621,8 @@ Manager::CacheDelete(Listener* aListener, RequestId aRequestId,
{
NS_ASSERT_OWNINGTHREAD(Manager);
MOZ_ASSERT(aListener);
if (mShuttingDown) {
aListener->OnCacheDelete(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN, false);
if (mShuttingDown || !mValid) {
aListener->OnCacheDelete(aRequestId, NS_ERROR_FAILURE, false);
return;
}
ListenerId listenerId = SaveListener(aListener);
Expand All @@ -1619,8 +1639,8 @@ Manager::CacheKeys(Listener* aListener, RequestId aRequestId,
{
NS_ASSERT_OWNINGTHREAD(Manager);
MOZ_ASSERT(aListener);
if (mShuttingDown) {
aListener->OnCacheKeys(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
if (mShuttingDown || !mValid) {
aListener->OnCacheKeys(aRequestId, NS_ERROR_FAILURE,
nsTArray<SavedRequest>(), nullptr);
return;
}
Expand All @@ -1640,8 +1660,8 @@ Manager::StorageMatch(Listener* aListener, RequestId aRequestId,
{
NS_ASSERT_OWNINGTHREAD(Manager);
MOZ_ASSERT(aListener);
if (mShuttingDown) {
aListener->OnStorageMatch(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
if (mShuttingDown || !mValid) {
aListener->OnStorageMatch(aRequestId, NS_ERROR_FAILURE,
nullptr, nullptr);
return;
}
Expand All @@ -1660,8 +1680,8 @@ Manager::StorageHas(Listener* aListener, RequestId aRequestId,
{
NS_ASSERT_OWNINGTHREAD(Manager);
MOZ_ASSERT(aListener);
if (mShuttingDown) {
aListener->OnStorageHas(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
if (mShuttingDown || !mValid) {
aListener->OnStorageHas(aRequestId, NS_ERROR_FAILURE,
false);
return;
}
Expand All @@ -1678,8 +1698,8 @@ Manager::StorageOpen(Listener* aListener, RequestId aRequestId,
{
NS_ASSERT_OWNINGTHREAD(Manager);
MOZ_ASSERT(aListener);
if (mShuttingDown) {
aListener->OnStorageOpen(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN, 0);
if (mShuttingDown || !mValid) {
aListener->OnStorageOpen(aRequestId, NS_ERROR_FAILURE, 0);
return;
}
ListenerId listenerId = SaveListener(aListener);
Expand All @@ -1695,8 +1715,8 @@ Manager::StorageDelete(Listener* aListener, RequestId aRequestId,
{
NS_ASSERT_OWNINGTHREAD(Manager);
MOZ_ASSERT(aListener);
if (mShuttingDown) {
aListener->OnStorageDelete(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
if (mShuttingDown || !mValid) {
aListener->OnStorageDelete(aRequestId, NS_ERROR_FAILURE,
false);
return;
}
Expand All @@ -1713,8 +1733,8 @@ Manager::StorageKeys(Listener* aListener, RequestId aRequestId,
{
NS_ASSERT_OWNINGTHREAD(Manager);
MOZ_ASSERT(aListener);
if (mShuttingDown) {
aListener->OnStorageKeys(aRequestId, NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
if (mShuttingDown || !mValid) {
aListener->OnStorageKeys(aRequestId, NS_ERROR_FAILURE,
nsTArray<nsString>());
return;
}
Expand All @@ -1730,6 +1750,7 @@ Manager::Manager(ManagerId* aManagerId, nsIThread* aIOThread)
, mIOThread(aIOThread)
, mContext(nullptr)
, mShuttingDown(false)
, mValid(true)
{
MOZ_ASSERT(mManagerId);
MOZ_ASSERT(mIOThread);
Expand Down Expand Up @@ -1768,11 +1789,6 @@ Manager::Shutdown()
// complete before shutdown proceeds.
mShuttingDown = true;

for (uint32_t i = 0; i < mStreamLists.Length(); ++i) {
nsRefPtr<StreamList> streamList = mStreamLists[i];
streamList->CloseAll();
}

// If there is a context, then we must wait for it to complete. Cancel and
// only note that we are done after its cleaned up.
if (mContext) {
Expand All @@ -1792,6 +1808,7 @@ Manager::CurrentContext()
nsRefPtr<Context> ref = mContext;
if (!ref) {
MOZ_ASSERT(!mShuttingDown);
MOZ_ASSERT(mValid);
nsRefPtr<Action> setupAction = new SetupAction();
ref = Context::Create(this, setupAction);
mContext = ref;
Expand Down
10 changes: 8 additions & 2 deletions dom/cache/Manager.h
Expand Up @@ -125,6 +125,11 @@ class Manager final
// Must be called by Context objects before they are destroyed.
void RemoveContext(Context* aContext);

// Marks the Manager "invalid". Once the Context completes no new operations
// will be permitted with this Manager. New actors will get a new Manager.
void Invalidate();
bool IsValid() const;

// If an actor represents a long term reference to a cache or body stream,
// then they must call AddRefCacheId() or AddRefBodyId(). This will
// cause the Manager to keep the backing data store alive for the given
Expand Down Expand Up @@ -214,8 +219,8 @@ class Manager final
struct ListenerEntry
{
ListenerEntry()
: mId(UINT64_MAX),
mListener(nullptr)
: mId(UINT64_MAX)
, mListener(nullptr)
{
}

Expand Down Expand Up @@ -255,6 +260,7 @@ class Manager final
nsTArray<StreamList*> mStreamLists;

bool mShuttingDown;
bool mValid;

struct CacheIdRefCounter
{
Expand Down

0 comments on commit e61a6d5

Please sign in to comment.