Bug 922741 - make callbacks iteration in CacheEntry smarter, r=michal
This commit is contained in:
@@ -475,8 +475,7 @@ function openCacheEntry(key, cb)
|
|||||||
},
|
},
|
||||||
onCacheEntryAvailable: function(entry, isNew, appCache, status) {
|
onCacheEntryAvailable: function(entry, isNew, appCache, status) {
|
||||||
cb(entry);
|
cb(entry);
|
||||||
},
|
}
|
||||||
get mainThreadOnly() { return true; }
|
|
||||||
};
|
};
|
||||||
diskStorage.asyncOpenURI(Services.io.newURI(key, null, null), "", nsICacheStorage.OPEN_READONLY, checkCacheListener);
|
diskStorage.asyncOpenURI(Services.io.newURI(key, null, null), "", nsICacheStorage.OPEN_READONLY, checkCacheListener);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ namespace net {
|
|||||||
|
|
||||||
static uint32_t const ENTRY_WANTED =
|
static uint32_t const ENTRY_WANTED =
|
||||||
nsICacheEntryOpenCallback::ENTRY_WANTED;
|
nsICacheEntryOpenCallback::ENTRY_WANTED;
|
||||||
|
static uint32_t const RECHECK_AFTER_WRITE_FINISHED =
|
||||||
|
nsICacheEntryOpenCallback::RECHECK_AFTER_WRITE_FINISHED;
|
||||||
static uint32_t const ENTRY_NEEDS_REVALIDATION =
|
static uint32_t const ENTRY_NEEDS_REVALIDATION =
|
||||||
nsICacheEntryOpenCallback::ENTRY_NEEDS_REVALIDATION;
|
nsICacheEntryOpenCallback::ENTRY_NEEDS_REVALIDATION;
|
||||||
static uint32_t const ENTRY_NOT_WANTED =
|
static uint32_t const ENTRY_NOT_WANTED =
|
||||||
@@ -54,6 +56,53 @@ CacheEntry::Handle::~Handle()
|
|||||||
MOZ_COUNT_DTOR(CacheEntry::Handle);
|
MOZ_COUNT_DTOR(CacheEntry::Handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CacheEntry::Callback
|
||||||
|
|
||||||
|
CacheEntry::Callback::Callback(nsICacheEntryOpenCallback *aCallback,
|
||||||
|
bool aReadOnly, bool aCheckOnAnyThread)
|
||||||
|
: mCallback(aCallback)
|
||||||
|
, mTargetThread(do_GetCurrentThread())
|
||||||
|
, mReadOnly(aReadOnly)
|
||||||
|
, mCheckOnAnyThread(aCheckOnAnyThread)
|
||||||
|
, mRecheckAfterWrite(false)
|
||||||
|
, mNotWanted(false)
|
||||||
|
{
|
||||||
|
MOZ_COUNT_CTOR(CacheEntry::Callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
CacheEntry::Callback::Callback(CacheEntry::Callback const &aThat)
|
||||||
|
: mCallback(aThat.mCallback)
|
||||||
|
, mTargetThread(aThat.mTargetThread)
|
||||||
|
, mReadOnly(aThat.mReadOnly)
|
||||||
|
, mCheckOnAnyThread(aThat.mCheckOnAnyThread)
|
||||||
|
, mRecheckAfterWrite(aThat.mRecheckAfterWrite)
|
||||||
|
, mNotWanted(aThat.mNotWanted)
|
||||||
|
{
|
||||||
|
MOZ_COUNT_CTOR(CacheEntry::Callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
CacheEntry::Callback::~Callback()
|
||||||
|
{
|
||||||
|
MOZ_COUNT_DTOR(CacheEntry::Callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult CacheEntry::Callback::OnCheckThread(bool *aOnCheckThread) const
|
||||||
|
{
|
||||||
|
if (!mCheckOnAnyThread) {
|
||||||
|
// Check we are on the target
|
||||||
|
return mTargetThread->IsOnCurrentThread(aOnCheckThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can invoke check anywhere
|
||||||
|
*aOnCheckThread = true;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult CacheEntry::Callback::OnAvailThread(bool *aOnAvailThread) const
|
||||||
|
{
|
||||||
|
return mTargetThread->IsOnCurrentThread(aOnAvailThread);
|
||||||
|
}
|
||||||
|
|
||||||
// CacheEntry
|
// CacheEntry
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS3(CacheEntry,
|
NS_IMPL_ISUPPORTS3(CacheEntry,
|
||||||
@@ -76,13 +125,13 @@ CacheEntry::CacheEntry(const nsACString& aStorageID,
|
|||||||
, mIsDoomed(false)
|
, mIsDoomed(false)
|
||||||
, mSecurityInfoLoaded(false)
|
, mSecurityInfoLoaded(false)
|
||||||
, mPreventCallbacks(false)
|
, mPreventCallbacks(false)
|
||||||
, mHasMainThreadOnlyCallback(false)
|
|
||||||
, mHasData(false)
|
, mHasData(false)
|
||||||
, mState(NOTLOADED)
|
, mState(NOTLOADED)
|
||||||
, mRegistration(NEVERREGISTERED)
|
, mRegistration(NEVERREGISTERED)
|
||||||
, mWriter(nullptr)
|
, mWriter(nullptr)
|
||||||
, mPredictedDataSize(0)
|
, mPredictedDataSize(0)
|
||||||
, mDataSize(0)
|
, mDataSize(0)
|
||||||
|
, mReleaseThread(NS_GetCurrentThread())
|
||||||
{
|
{
|
||||||
MOZ_COUNT_CTOR(CacheEntry);
|
MOZ_COUNT_CTOR(CacheEntry);
|
||||||
|
|
||||||
@@ -94,7 +143,7 @@ CacheEntry::CacheEntry(const nsACString& aStorageID,
|
|||||||
|
|
||||||
CacheEntry::~CacheEntry()
|
CacheEntry::~CacheEntry()
|
||||||
{
|
{
|
||||||
ProxyReleaseMainThread(mURI);
|
ProxyRelease(mURI, mReleaseThread);
|
||||||
|
|
||||||
LOG(("CacheEntry::~CacheEntry [this=%p]", this));
|
LOG(("CacheEntry::~CacheEntry [this=%p]", this));
|
||||||
MOZ_COUNT_DTOR(CacheEntry);
|
MOZ_COUNT_DTOR(CacheEntry);
|
||||||
@@ -166,26 +215,20 @@ void CacheEntry::AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags
|
|||||||
bool readonly = aFlags & nsICacheStorage::OPEN_READONLY;
|
bool readonly = aFlags & nsICacheStorage::OPEN_READONLY;
|
||||||
bool truncate = aFlags & nsICacheStorage::OPEN_TRUNCATE;
|
bool truncate = aFlags & nsICacheStorage::OPEN_TRUNCATE;
|
||||||
bool priority = aFlags & nsICacheStorage::OPEN_PRIORITY;
|
bool priority = aFlags & nsICacheStorage::OPEN_PRIORITY;
|
||||||
|
bool multithread = aFlags & nsICacheStorage::CHECK_MULTITHREADED;
|
||||||
bool mainThreadOnly;
|
|
||||||
if (aCallback && NS_FAILED(aCallback->GetMainThreadOnly(&mainThreadOnly)))
|
|
||||||
mainThreadOnly = true; // rather play safe...
|
|
||||||
|
|
||||||
MOZ_ASSERT(!readonly || !truncate, "Bad flags combination");
|
MOZ_ASSERT(!readonly || !truncate, "Bad flags combination");
|
||||||
MOZ_ASSERT(!(truncate && mState > LOADING), "Must not call truncate on already loaded entry");
|
MOZ_ASSERT(!(truncate && mState > LOADING), "Must not call truncate on already loaded entry");
|
||||||
|
|
||||||
|
Callback callback(aCallback, readonly, multithread);
|
||||||
|
|
||||||
mozilla::MutexAutoLock lock(mLock);
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
|
|
||||||
if (Load(truncate, priority) ||
|
if (Load(truncate, priority) ||
|
||||||
PendingCallbacks() ||
|
PendingCallbacks() ||
|
||||||
!InvokeCallback(aCallback, readonly)) {
|
!InvokeCallback(callback)) {
|
||||||
// Load in progress or callback bypassed...
|
// Load in progress or callback bypassed...
|
||||||
if (mainThreadOnly) {
|
RememberCallback(callback);
|
||||||
LOG((" callback is main-thread only"));
|
|
||||||
mHasMainThreadOnlyCallback = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
RememberCallback(aCallback, readonly);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -338,52 +381,46 @@ already_AddRefed<CacheEntry> CacheEntry::ReopenTruncated(nsICacheEntryOpenCallba
|
|||||||
|
|
||||||
newEntry->TransferCallbacks(*this);
|
newEntry->TransferCallbacks(*this);
|
||||||
mCallbacks.Clear();
|
mCallbacks.Clear();
|
||||||
mReadOnlyCallbacks.Clear();
|
|
||||||
mHasMainThreadOnlyCallback = false;
|
|
||||||
|
|
||||||
return newEntry.forget();
|
return newEntry.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CacheEntry::TransferCallbacks(CacheEntry const& aFromEntry)
|
void CacheEntry::TransferCallbacks(CacheEntry & aFromEntry)
|
||||||
{
|
{
|
||||||
mozilla::MutexAutoLock lock(mLock);
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
|
|
||||||
LOG(("CacheEntry::TransferCallbacks [entry=%p, from=%p]",
|
LOG(("CacheEntry::TransferCallbacks [entry=%p, from=%p]",
|
||||||
this, &aFromEntry));
|
this, &aFromEntry));
|
||||||
|
|
||||||
mCallbacks.AppendObjects(aFromEntry.mCallbacks);
|
if (!mCallbacks.Length())
|
||||||
mReadOnlyCallbacks.AppendObjects(aFromEntry.mReadOnlyCallbacks);
|
mCallbacks.SwapElements(aFromEntry.mCallbacks);
|
||||||
if (aFromEntry.mHasMainThreadOnlyCallback)
|
else
|
||||||
mHasMainThreadOnlyCallback = true;
|
mCallbacks.AppendElements(aFromEntry.mCallbacks);
|
||||||
|
|
||||||
if (mCallbacks.Length() || mReadOnlyCallbacks.Length())
|
if (mCallbacks.Length())
|
||||||
BackgroundOp(Ops::CALLBACKS, true);
|
BackgroundOp(Ops::CALLBACKS, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CacheEntry::RememberCallback(nsICacheEntryOpenCallback* aCallback,
|
void CacheEntry::RememberCallback(Callback const& aCallback)
|
||||||
bool aReadOnly)
|
|
||||||
{
|
{
|
||||||
// AsyncOpen can be called w/o a callback reference (when this is a new/truncated entry)
|
// AsyncOpen can be called w/o a callback reference (when this is a new/truncated entry)
|
||||||
if (!aCallback)
|
if (!aCallback.mCallback)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LOG(("CacheEntry::RememberCallback [this=%p, cb=%p]", this, aCallback));
|
LOG(("CacheEntry::RememberCallback [this=%p, cb=%p]", this, aCallback.mCallback.get()));
|
||||||
|
|
||||||
mLock.AssertCurrentThreadOwns();
|
mLock.AssertCurrentThreadOwns();
|
||||||
|
|
||||||
if (!aReadOnly)
|
mCallbacks.AppendElement(aCallback);
|
||||||
mCallbacks.AppendObject(aCallback);
|
|
||||||
else
|
|
||||||
mReadOnlyCallbacks.AppendObject(aCallback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CacheEntry::PendingCallbacks()
|
bool CacheEntry::PendingCallbacks()
|
||||||
{
|
{
|
||||||
mLock.AssertCurrentThreadOwns();
|
mLock.AssertCurrentThreadOwns();
|
||||||
return mCallbacks.Length() || mReadOnlyCallbacks.Length();
|
return mCallbacks.Length();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CacheEntry::InvokeCallbacksMainThread()
|
void CacheEntry::InvokeCallbacksLock()
|
||||||
{
|
{
|
||||||
mozilla::MutexAutoLock lock(mLock);
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
InvokeCallbacks();
|
InvokeCallbacks();
|
||||||
@@ -391,80 +428,84 @@ void CacheEntry::InvokeCallbacksMainThread()
|
|||||||
|
|
||||||
void CacheEntry::InvokeCallbacks()
|
void CacheEntry::InvokeCallbacks()
|
||||||
{
|
{
|
||||||
LOG(("CacheEntry::InvokeCallbacks BEGIN [this=%p]", this));
|
|
||||||
|
|
||||||
mLock.AssertCurrentThreadOwns();
|
mLock.AssertCurrentThreadOwns();
|
||||||
|
|
||||||
do {
|
LOG(("CacheEntry::InvokeCallbacks BEGIN [this=%p]", this));
|
||||||
if (mPreventCallbacks) {
|
|
||||||
LOG(("CacheEntry::InvokeCallbacks END [this=%p] callbacks prevented!", this));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mCallbacks.Count()) {
|
// Invoke first all r/w callbacks, then all r/o callbacks.
|
||||||
LOG((" no r/w callbacks"));
|
if (InvokeCallbacks(false))
|
||||||
break;
|
InvokeCallbacks(true);
|
||||||
}
|
|
||||||
|
|
||||||
if (mHasMainThreadOnlyCallback && !NS_IsMainThread()) {
|
|
||||||
nsRefPtr<nsRunnableMethod<CacheEntry> > event =
|
|
||||||
NS_NewRunnableMethod(this, &CacheEntry::InvokeCallbacksMainThread);
|
|
||||||
NS_DispatchToMainThread(event);
|
|
||||||
LOG(("CacheEntry::InvokeCallbacks END [this=%p] dispatching to maintread", this));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsICacheEntryOpenCallback> callback = mCallbacks[0];
|
|
||||||
mCallbacks.RemoveElementAt(0);
|
|
||||||
|
|
||||||
if (!InvokeCallback(callback, false)) {
|
|
||||||
mCallbacks.InsertElementAt(0, callback);
|
|
||||||
LOG(("CacheEntry::InvokeCallbacks END [this=%p] callback bypassed", this));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} while (true);
|
|
||||||
|
|
||||||
while (mReadOnlyCallbacks.Count()) {
|
|
||||||
if (mHasMainThreadOnlyCallback && !NS_IsMainThread()) {
|
|
||||||
nsRefPtr<nsRunnableMethod<CacheEntry> > event =
|
|
||||||
NS_NewRunnableMethod(this, &CacheEntry::InvokeCallbacksMainThread);
|
|
||||||
NS_DispatchToMainThread(event);
|
|
||||||
LOG(("CacheEntry::InvokeCallbacks END [this=%p] dispatching to maintread", this));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsICacheEntryOpenCallback> callback = mReadOnlyCallbacks[0];
|
|
||||||
mReadOnlyCallbacks.RemoveElementAt(0);
|
|
||||||
|
|
||||||
if (!InvokeCallback(callback, true)) {
|
|
||||||
// Didn't trigger, so we must stop
|
|
||||||
mReadOnlyCallbacks.InsertElementAt(0, callback);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mCallbacks.Count() && !mReadOnlyCallbacks.Count())
|
|
||||||
mHasMainThreadOnlyCallback = false;
|
|
||||||
|
|
||||||
LOG(("CacheEntry::InvokeCallbacks END [this=%p]", this));
|
LOG(("CacheEntry::InvokeCallbacks END [this=%p]", this));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CacheEntry::InvokeCallback(nsICacheEntryOpenCallback* aCallback,
|
bool CacheEntry::InvokeCallbacks(bool aReadOnly)
|
||||||
bool aReadOnly)
|
{
|
||||||
|
mLock.AssertCurrentThreadOwns();
|
||||||
|
|
||||||
|
uint32_t i = 0;
|
||||||
|
while (i < mCallbacks.Length()) {
|
||||||
|
if (mPreventCallbacks) {
|
||||||
|
LOG((" callbacks prevented!"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mIsDoomed && (mState == WRITING || mState == REVALIDATING)) {
|
||||||
|
LOG((" entry is being written/revalidated"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mCallbacks[i].mReadOnly != aReadOnly) {
|
||||||
|
// Callback is not r/w or r/o, go to another one in line
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onCheckThread;
|
||||||
|
nsresult rv = mCallbacks[i].OnCheckThread(&onCheckThread);
|
||||||
|
|
||||||
|
if (NS_SUCCEEDED(rv) && !onCheckThread) {
|
||||||
|
// Redispatch to the target thread
|
||||||
|
nsRefPtr<nsRunnableMethod<CacheEntry> > event =
|
||||||
|
NS_NewRunnableMethod(this, &CacheEntry::InvokeCallbacksLock);
|
||||||
|
|
||||||
|
rv = mCallbacks[i].mTargetThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
|
||||||
|
if (NS_SUCCEEDED(rv)) {
|
||||||
|
LOG((" re-dispatching to target thread"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Callback callback = mCallbacks[i];
|
||||||
|
mCallbacks.RemoveElementAt(i);
|
||||||
|
|
||||||
|
if (NS_SUCCEEDED(rv) && !InvokeCallback(callback)) {
|
||||||
|
// Callback didn't fire, put it back and go to another one in line.
|
||||||
|
// Only reason InvokeCallback returns false is that onCacheEntryCheck
|
||||||
|
// returns RECHECK_AFTER_WRITE_FINISHED. If we would stop the loop, other
|
||||||
|
// readers or potential writers would be unnecessarily kept from being
|
||||||
|
// invoked.
|
||||||
|
mCallbacks.InsertElementAt(i, callback);
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CacheEntry::InvokeCallback(Callback & aCallback)
|
||||||
{
|
{
|
||||||
LOG(("CacheEntry::InvokeCallback [this=%p, state=%s, cb=%p]",
|
LOG(("CacheEntry::InvokeCallback [this=%p, state=%s, cb=%p]",
|
||||||
this, StateString(mState), aCallback));
|
this, StateString(mState), aCallback.mCallback.get()));
|
||||||
|
|
||||||
mLock.AssertCurrentThreadOwns();
|
mLock.AssertCurrentThreadOwns();
|
||||||
|
|
||||||
bool notWanted = false;
|
// When this entry is doomed we want to notify the callback any time
|
||||||
|
|
||||||
if (!mIsDoomed) {
|
if (!mIsDoomed) {
|
||||||
// When we are here, the entry must be loaded from disk
|
// When we are here, the entry must be loaded from disk
|
||||||
MOZ_ASSERT(mState > LOADING);
|
MOZ_ASSERT(mState > LOADING);
|
||||||
|
|
||||||
if (mState == WRITING ||
|
if (mState == WRITING || mState == REVALIDATING) {
|
||||||
mState == REVALIDATING) {
|
|
||||||
// Prevent invoking other callbacks since one of them is now writing
|
// Prevent invoking other callbacks since one of them is now writing
|
||||||
// or revalidating this entry. No consumers should get this entry
|
// or revalidating this entry. No consumers should get this entry
|
||||||
// until metadata are filled with values downloaded from the server
|
// until metadata are filled with values downloaded from the server
|
||||||
@@ -473,7 +514,10 @@ bool CacheEntry::InvokeCallback(nsICacheEntryOpenCallback* aCallback,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!aReadOnly) {
|
// mRecheckAfterWrite flag already set means the callback has already passed
|
||||||
|
// the onCacheEntryCheck call. Until the current write is not finished this
|
||||||
|
// callback will be bypassed.
|
||||||
|
if (!aCallback.mReadOnly && !aCallback.mRecheckAfterWrite) {
|
||||||
if (mState == EMPTY) {
|
if (mState == EMPTY) {
|
||||||
// Advance to writing state, we expect to invoke the callback and let
|
// Advance to writing state, we expect to invoke the callback and let
|
||||||
// it fill content of this entry. Must set and check the state here
|
// it fill content of this entry. Must set and check the state here
|
||||||
@@ -482,7 +526,7 @@ bool CacheEntry::InvokeCallback(nsICacheEntryOpenCallback* aCallback,
|
|||||||
LOG((" advancing to WRITING state"));
|
LOG((" advancing to WRITING state"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!aCallback) {
|
if (!aCallback.mCallback) {
|
||||||
// We can be given no callback only in case of recreate, it is ok
|
// We can be given no callback only in case of recreate, it is ok
|
||||||
// to advance to WRITING state since the caller of recreate is expected
|
// to advance to WRITING state since the caller of recreate is expected
|
||||||
// to write this entry now.
|
// to write this entry now.
|
||||||
@@ -496,7 +540,8 @@ bool CacheEntry::InvokeCallback(nsICacheEntryOpenCallback* aCallback,
|
|||||||
// mayhemer: TODO check and solve any potential races of concurent OnCacheEntryCheck
|
// mayhemer: TODO check and solve any potential races of concurent OnCacheEntryCheck
|
||||||
mozilla::MutexAutoUnlock unlock(mLock);
|
mozilla::MutexAutoUnlock unlock(mLock);
|
||||||
|
|
||||||
nsresult rv = aCallback->OnCacheEntryCheck(this, nullptr, &checkResult);
|
nsresult rv = aCallback.mCallback->OnCacheEntryCheck(
|
||||||
|
this, nullptr, &checkResult);
|
||||||
LOG((" OnCacheEntryCheck: rv=0x%08x, result=%d", rv, checkResult));
|
LOG((" OnCacheEntryCheck: rv=0x%08x, result=%d", rv, checkResult));
|
||||||
|
|
||||||
if (NS_FAILED(rv))
|
if (NS_FAILED(rv))
|
||||||
@@ -510,6 +555,12 @@ bool CacheEntry::InvokeCallback(nsICacheEntryOpenCallback* aCallback,
|
|||||||
// Proceed to callback...
|
// Proceed to callback...
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case RECHECK_AFTER_WRITE_FINISHED:
|
||||||
|
LOG((" consumer will check on the entry again after write is done"));
|
||||||
|
// The consumer wants the entry to complete first.
|
||||||
|
aCallback.mRecheckAfterWrite = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case ENTRY_NEEDS_REVALIDATION:
|
case ENTRY_NEEDS_REVALIDATION:
|
||||||
LOG((" will be holding callbacks until entry is revalidated"));
|
LOG((" will be holding callbacks until entry is revalidated"));
|
||||||
// State is READY now and from that state entry cannot transit to any other
|
// State is READY now and from that state entry cannot transit to any other
|
||||||
@@ -521,46 +572,73 @@ bool CacheEntry::InvokeCallback(nsICacheEntryOpenCallback* aCallback,
|
|||||||
case ENTRY_NOT_WANTED:
|
case ENTRY_NOT_WANTED:
|
||||||
LOG((" consumer not interested in the entry"));
|
LOG((" consumer not interested in the entry"));
|
||||||
// Do not give this entry to the consumer, it is not interested in us.
|
// Do not give this entry to the consumer, it is not interested in us.
|
||||||
notWanted = true;
|
aCallback.mNotWanted = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aCallback) {
|
if (aCallback.mCallback) {
|
||||||
|
if (!mIsDoomed && aCallback.mRecheckAfterWrite) {
|
||||||
|
// If we don't have data and the callback wants a complete entry,
|
||||||
|
// don't invoke now.
|
||||||
|
bool bypass = !mHasData;
|
||||||
|
if (!bypass) {
|
||||||
|
int64_t _unused;
|
||||||
|
bypass = !mFile->DataSize(&_unused);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bypass) {
|
||||||
|
LOG((" bypassing, entry data still being written"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry is complete now, do the check+avail call again
|
||||||
|
aCallback.mRecheckAfterWrite = false;
|
||||||
|
return InvokeCallback(aCallback);
|
||||||
|
}
|
||||||
|
|
||||||
mozilla::MutexAutoUnlock unlock(mLock);
|
mozilla::MutexAutoUnlock unlock(mLock);
|
||||||
InvokeAvailableCallback(aCallback, aReadOnly, notWanted);
|
InvokeAvailableCallback(aCallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CacheEntry::InvokeAvailableCallback(nsICacheEntryOpenCallback* aCallback,
|
void CacheEntry::InvokeAvailableCallback(Callback const & aCallback)
|
||||||
bool aReadOnly,
|
|
||||||
bool aNotWanted)
|
|
||||||
{
|
{
|
||||||
LOG(("CacheEntry::InvokeAvailableCallback [this=%p, state=%s, cb=%p, r/o=%d, n/w=%d]",
|
LOG(("CacheEntry::InvokeAvailableCallback [this=%p, state=%s, cb=%p, r/o=%d, n/w=%d]",
|
||||||
this, StateString(mState), aCallback, aReadOnly, aNotWanted));
|
this, StateString(mState), aCallback.mCallback.get(), aCallback.mReadOnly, aCallback.mNotWanted));
|
||||||
|
|
||||||
|
nsresult rv;
|
||||||
|
|
||||||
uint32_t const state = mState;
|
uint32_t const state = mState;
|
||||||
|
|
||||||
// When we are here, the entry must be loaded from disk
|
// When we are here, the entry must be loaded from disk
|
||||||
MOZ_ASSERT(state > LOADING || mIsDoomed);
|
MOZ_ASSERT(state > LOADING || mIsDoomed);
|
||||||
|
|
||||||
if (!NS_IsMainThread()) {
|
bool onAvailThread;
|
||||||
// Must happen on the main thread :(
|
rv = aCallback.OnAvailThread(&onAvailThread);
|
||||||
nsRefPtr<AvailableCallbackRunnable> event =
|
if (NS_FAILED(rv)) {
|
||||||
new AvailableCallbackRunnable(this, aCallback, aReadOnly, aNotWanted);
|
LOG((" target thread dead?"));
|
||||||
NS_DispatchToMainThread(event);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This happens only on the main thread / :( /
|
if (!onAvailThread) {
|
||||||
|
// Dispatch to the right thread
|
||||||
|
nsRefPtr<AvailableCallbackRunnable> event =
|
||||||
|
new AvailableCallbackRunnable(this, aCallback);
|
||||||
|
|
||||||
if (mIsDoomed || aNotWanted) {
|
rv = aCallback.mTargetThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
|
||||||
|
LOG((" redispatched, (rv = 0x%08x)", rv));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mIsDoomed || aCallback.mNotWanted) {
|
||||||
LOG((" doomed or not wanted, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND"));
|
LOG((" doomed or not wanted, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND"));
|
||||||
aCallback->OnCacheEntryAvailable(nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND);
|
aCallback.mCallback->OnCacheEntryAvailable(
|
||||||
|
nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -571,13 +649,15 @@ void CacheEntry::InvokeAvailableCallback(nsICacheEntryOpenCallback* aCallback,
|
|||||||
BackgroundOp(Ops::FRECENCYUPDATE);
|
BackgroundOp(Ops::FRECENCYUPDATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
aCallback->OnCacheEntryAvailable(this, false, nullptr, NS_OK);
|
aCallback.mCallback->OnCacheEntryAvailable(
|
||||||
|
this, false, nullptr, NS_OK);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aReadOnly) {
|
if (aCallback.mReadOnly) {
|
||||||
LOG((" r/o and not ready, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND"));
|
LOG((" r/o and not ready, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND"));
|
||||||
aCallback->OnCacheEntryAvailable(nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND);
|
aCallback.mCallback->OnCacheEntryAvailable(
|
||||||
|
nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -589,7 +669,8 @@ void CacheEntry::InvokeAvailableCallback(nsICacheEntryOpenCallback* aCallback,
|
|||||||
// Consumer will be responsible to fill or validate the entry metadata and data.
|
// Consumer will be responsible to fill or validate the entry metadata and data.
|
||||||
|
|
||||||
nsRefPtr<Handle> handle = NewWriteHandle();
|
nsRefPtr<Handle> handle = NewWriteHandle();
|
||||||
nsresult rv = aCallback->OnCacheEntryAvailable(handle, state == WRITING, nullptr, NS_OK);
|
rv = aCallback.mCallback->OnCacheEntryAvailable(
|
||||||
|
handle, state == WRITING, nullptr, NS_OK);
|
||||||
|
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
LOG((" writing/revalidating failed (0x%08x)", rv));
|
LOG((" writing/revalidating failed (0x%08x)", rv));
|
||||||
@@ -652,6 +733,15 @@ void CacheEntry::OnWriterClosed(Handle const* aHandle)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CacheEntry::OnOutputClosed()
|
||||||
|
{
|
||||||
|
// Called when the file's output stream is closed. Invoke any callbacks
|
||||||
|
// waiting for complete entry.
|
||||||
|
|
||||||
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
|
InvokeCallbacks();
|
||||||
|
}
|
||||||
|
|
||||||
bool CacheEntry::UsingDisk() const
|
bool CacheEntry::UsingDisk() const
|
||||||
{
|
{
|
||||||
CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
|
CacheStorageService::Self()->Lock().AssertCurrentThreadOwns();
|
||||||
@@ -841,9 +931,6 @@ nsresult CacheEntry::OpenOutputStreamInternal(int64_t offset, nsIOutputStream *
|
|||||||
|
|
||||||
MOZ_ASSERT(mState > LOADING);
|
MOZ_ASSERT(mState > LOADING);
|
||||||
|
|
||||||
if (!mFile)
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
// No need to sync on mUseDisk here, we don't need to be consistent
|
// No need to sync on mUseDisk here, we don't need to be consistent
|
||||||
@@ -853,8 +940,11 @@ nsresult CacheEntry::OpenOutputStreamInternal(int64_t offset, nsIOutputStream *
|
|||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsRefPtr<CacheOutputCloseListener> listener =
|
||||||
|
new CacheOutputCloseListener(this);
|
||||||
|
|
||||||
nsCOMPtr<nsIOutputStream> stream;
|
nsCOMPtr<nsIOutputStream> stream;
|
||||||
rv = mFile->OpenOutputStream(getter_AddRefs(stream));
|
rv = mFile->OpenOutputStream(listener, getter_AddRefs(stream));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
nsCOMPtr<nsISeekableStream> seekable =
|
nsCOMPtr<nsISeekableStream> seekable =
|
||||||
@@ -924,17 +1014,11 @@ NS_IMETHODIMP CacheEntry::SetSecurityInfo(nsISupports *aSecurityInfo)
|
|||||||
|
|
||||||
NS_ENSURE_SUCCESS(mFileStatus, mFileStatus);
|
NS_ENSURE_SUCCESS(mFileStatus, mFileStatus);
|
||||||
|
|
||||||
nsRefPtr<CacheFile> file;
|
|
||||||
{
|
{
|
||||||
mozilla::MutexAutoLock lock(mLock);
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
|
|
||||||
mSecurityInfo = aSecurityInfo;
|
mSecurityInfo = aSecurityInfo;
|
||||||
mSecurityInfoLoaded = true;
|
mSecurityInfoLoaded = true;
|
||||||
|
|
||||||
if (!mFile)
|
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
|
||||||
|
|
||||||
file = mFile;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCOMPtr<nsISerializable> serializable =
|
nsCOMPtr<nsISerializable> serializable =
|
||||||
@@ -1271,7 +1355,7 @@ void CacheEntry::DoomAlreadyRemoved()
|
|||||||
{
|
{
|
||||||
mozilla::MutexAutoLock lock(mLock);
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
|
|
||||||
if (mCallbacks.Length() || mReadOnlyCallbacks.Length()) {
|
if (mCallbacks.Length()) {
|
||||||
// Must force post here since may be indirectly called from
|
// Must force post here since may be indirectly called from
|
||||||
// InvokeCallbacks of this entry and we don't want reentrancy here.
|
// InvokeCallbacks of this entry and we don't want reentrancy here.
|
||||||
BackgroundOp(Ops::CALLBACKS, true);
|
BackgroundOp(Ops::CALLBACKS, true);
|
||||||
@@ -1348,5 +1432,32 @@ void CacheEntry::BackgroundOp(uint32_t aOperations, bool aForceAsync)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CacheOutputCloseListener
|
||||||
|
|
||||||
|
CacheOutputCloseListener::CacheOutputCloseListener(CacheEntry* aEntry)
|
||||||
|
: mEntry(aEntry)
|
||||||
|
{
|
||||||
|
MOZ_COUNT_CTOR(CacheOutputCloseListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
CacheOutputCloseListener::~CacheOutputCloseListener()
|
||||||
|
{
|
||||||
|
MOZ_COUNT_DTOR(CacheOutputCloseListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CacheOutputCloseListener::OnOutputClosed()
|
||||||
|
{
|
||||||
|
// We need this class and to redispatch since this callback is invoked
|
||||||
|
// under the file's lock and to do the job we need to enter the entry's
|
||||||
|
// lock too. That would lead to potential deadlocks.
|
||||||
|
NS_DispatchToCurrentThread(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP CacheOutputCloseListener::Run()
|
||||||
|
{
|
||||||
|
mEntry->OnOutputClosed();
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
} // net
|
} // net
|
||||||
} // mozilla
|
} // mozilla
|
||||||
|
|||||||
@@ -35,19 +35,15 @@ PRTimeToSeconds(PRTime t_usec)
|
|||||||
class nsIStorageStream;
|
class nsIStorageStream;
|
||||||
class nsIOutputStream;
|
class nsIOutputStream;
|
||||||
class nsIURI;
|
class nsIURI;
|
||||||
|
class nsIThread;
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace net {
|
namespace net {
|
||||||
|
|
||||||
class CacheStorageService;
|
class CacheStorageService;
|
||||||
class CacheStorage;
|
class CacheStorage;
|
||||||
|
class CacheFileOutputStream;
|
||||||
namespace {
|
class CacheOutputCloseListener;
|
||||||
class FrecencyComparator;
|
|
||||||
class ExpirationComparator;
|
|
||||||
class EvictionRunnable;
|
|
||||||
class WalkRunnable;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CacheEntry : public nsICacheEntry
|
class CacheEntry : public nsICacheEntry
|
||||||
, public nsIRunnable
|
, public nsIRunnable
|
||||||
@@ -129,29 +125,45 @@ private:
|
|||||||
nsRefPtr<CacheEntry> mEntry;
|
nsRefPtr<CacheEntry> mEntry;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Callback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Callback(nsICacheEntryOpenCallback *aCallback,
|
||||||
|
bool aReadOnly, bool aCheckOnAnyThread);
|
||||||
|
Callback(Callback const &aThat);
|
||||||
|
~Callback();
|
||||||
|
|
||||||
|
nsCOMPtr<nsICacheEntryOpenCallback> mCallback;
|
||||||
|
nsCOMPtr<nsIThread> mTargetThread;
|
||||||
|
bool mReadOnly : 1;
|
||||||
|
bool mCheckOnAnyThread : 1;
|
||||||
|
bool mRecheckAfterWrite : 1;
|
||||||
|
bool mNotWanted : 1;
|
||||||
|
|
||||||
|
nsresult OnCheckThread(bool *aOnCheckThread) const;
|
||||||
|
nsresult OnAvailThread(bool *aOnAvailThread) const;
|
||||||
|
};
|
||||||
|
|
||||||
// Since OnCacheEntryAvailable must be invoked on the main thread
|
// Since OnCacheEntryAvailable must be invoked on the main thread
|
||||||
// we need a runnable for it...
|
// we need a runnable for it...
|
||||||
class AvailableCallbackRunnable : public nsRunnable
|
class AvailableCallbackRunnable : public nsRunnable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AvailableCallbackRunnable(CacheEntry* aEntry,
|
AvailableCallbackRunnable(CacheEntry* aEntry,
|
||||||
nsICacheEntryOpenCallback* aCallback,
|
Callback const &aCallback)
|
||||||
bool aReadOnly,
|
: mEntry(aEntry)
|
||||||
bool aNotWanted)
|
, mCallback(aCallback)
|
||||||
: mEntry(aEntry), mCallback(aCallback)
|
{}
|
||||||
, mReadOnly(aReadOnly), mNotWanted(aNotWanted) {}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NS_IMETHOD Run()
|
NS_IMETHOD Run()
|
||||||
{
|
{
|
||||||
mEntry->InvokeAvailableCallback(mCallback, mReadOnly, mNotWanted);
|
mEntry->InvokeAvailableCallback(mCallback);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsRefPtr<CacheEntry> mEntry;
|
nsRefPtr<CacheEntry> mEntry;
|
||||||
nsCOMPtr<nsICacheEntryOpenCallback> mCallback;
|
Callback mCallback;
|
||||||
bool mReadOnly : 1;
|
|
||||||
bool mNotWanted : 1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Since OnCacheEntryDoomed must be invoked on the main thread
|
// Since OnCacheEntryDoomed must be invoked on the main thread
|
||||||
@@ -184,12 +196,13 @@ private:
|
|||||||
bool Load(bool aTruncate, bool aPriority);
|
bool Load(bool aTruncate, bool aPriority);
|
||||||
void OnLoaded();
|
void OnLoaded();
|
||||||
|
|
||||||
void RememberCallback(nsICacheEntryOpenCallback* aCallback, bool aReadOnly);
|
void RememberCallback(Callback const & aCallback);
|
||||||
bool PendingCallbacks();
|
bool PendingCallbacks();
|
||||||
|
void InvokeCallbacksLock();
|
||||||
void InvokeCallbacks();
|
void InvokeCallbacks();
|
||||||
bool InvokeCallback(nsICacheEntryOpenCallback* aCallback, bool aReadOnly);
|
bool InvokeCallbacks(bool aReadOnly);
|
||||||
void InvokeAvailableCallback(nsICacheEntryOpenCallback* aCallback, bool aReadOnly, bool aNotWanted);
|
bool InvokeCallback(Callback & aCallback);
|
||||||
void InvokeCallbacksMainThread();
|
void InvokeAvailableCallback(Callback const & aCallback);
|
||||||
|
|
||||||
nsresult OpenOutputStreamInternal(int64_t offset, nsIOutputStream * *_retval);
|
nsresult OpenOutputStreamInternal(int64_t offset, nsIOutputStream * *_retval);
|
||||||
|
|
||||||
@@ -198,17 +211,21 @@ private:
|
|||||||
Handle* NewWriteHandle();
|
Handle* NewWriteHandle();
|
||||||
void OnWriterClosed(Handle const* aHandle);
|
void OnWriterClosed(Handle const* aHandle);
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class CacheOutputCloseListener;
|
||||||
|
void OnOutputClosed();
|
||||||
|
|
||||||
// Schedules a background operation on the management thread.
|
// Schedules a background operation on the management thread.
|
||||||
// When executed on the management thread directly, the operation(s)
|
// When executed on the management thread directly, the operation(s)
|
||||||
// is (are) executed immediately.
|
// is (are) executed immediately.
|
||||||
void BackgroundOp(uint32_t aOperation, bool aForceAsync = false);
|
void BackgroundOp(uint32_t aOperation, bool aForceAsync = false);
|
||||||
|
|
||||||
already_AddRefed<CacheEntry> ReopenTruncated(nsICacheEntryOpenCallback* aCallback);
|
already_AddRefed<CacheEntry> ReopenTruncated(nsICacheEntryOpenCallback* aCallback);
|
||||||
void TransferCallbacks(CacheEntry const& aFromEntry);
|
void TransferCallbacks(CacheEntry & aFromEntry);
|
||||||
|
|
||||||
mozilla::Mutex mLock;
|
mozilla::Mutex mLock;
|
||||||
|
|
||||||
nsCOMArray<nsICacheEntryOpenCallback> mCallbacks, mReadOnlyCallbacks;
|
nsTArray<Callback> mCallbacks;
|
||||||
nsCOMPtr<nsICacheEntryDoomCallback> mDoomCallback;
|
nsCOMPtr<nsICacheEntryDoomCallback> mDoomCallback;
|
||||||
|
|
||||||
nsRefPtr<CacheFile> mFile;
|
nsRefPtr<CacheFile> mFile;
|
||||||
@@ -232,8 +249,6 @@ private:
|
|||||||
bool mSecurityInfoLoaded : 1;
|
bool mSecurityInfoLoaded : 1;
|
||||||
// Prevents any callback invocation
|
// Prevents any callback invocation
|
||||||
bool mPreventCallbacks : 1;
|
bool mPreventCallbacks : 1;
|
||||||
// Way around when having a callback that cannot be invoked on non-main thread
|
|
||||||
bool mHasMainThreadOnlyCallback : 1;
|
|
||||||
// true: after load and an existing file, or after output stream has been opened.
|
// true: after load and an existing file, or after output stream has been opened.
|
||||||
// note - when opening an input stream, and this flag is false, output stream
|
// note - when opening an input stream, and this flag is false, output stream
|
||||||
// is open along ; this makes input streams on new entries behave correctly
|
// is open along ; this makes input streams on new entries behave correctly
|
||||||
@@ -301,6 +316,24 @@ private:
|
|||||||
uint32_t mDataSize; // ???
|
uint32_t mDataSize; // ???
|
||||||
|
|
||||||
mozilla::TimeStamp mLoadStart;
|
mozilla::TimeStamp mLoadStart;
|
||||||
|
|
||||||
|
nsCOMPtr<nsIThread> mReleaseThread;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CacheOutputCloseListener : public nsRunnable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void OnOutputClosed();
|
||||||
|
virtual ~CacheOutputCloseListener();
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class CacheEntry;
|
||||||
|
|
||||||
|
NS_DECL_NSIRUNNABLE
|
||||||
|
CacheOutputCloseListener(CacheEntry* aEntry);
|
||||||
|
|
||||||
|
private:
|
||||||
|
nsRefPtr<CacheEntry> mEntry;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // net
|
} // net
|
||||||
|
|||||||
@@ -807,7 +807,7 @@ CacheFile::OpenInputStream(nsIInputStream **_retval)
|
|||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
CacheFile::OpenOutputStream(nsIOutputStream **_retval)
|
CacheFile::OpenOutputStream(CacheOutputCloseListener *aCloseListener, nsIOutputStream **_retval)
|
||||||
{
|
{
|
||||||
CacheFileAutoLock lock(this);
|
CacheFileAutoLock lock(this);
|
||||||
|
|
||||||
@@ -827,7 +827,7 @@ CacheFile::OpenOutputStream(nsIOutputStream **_retval)
|
|||||||
return NS_ERROR_NOT_AVAILABLE;
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
mOutput = new CacheFileOutputStream(this);
|
mOutput = new CacheFileOutputStream(this, aCloseListener);
|
||||||
|
|
||||||
LOG(("CacheFile::OpenOutputStream() - Creating new output stream %p "
|
LOG(("CacheFile::OpenOutputStream() - Creating new output stream %p "
|
||||||
"[this=%p]", mOutput, this));
|
"[this=%p]", mOutput, this));
|
||||||
@@ -1338,6 +1338,9 @@ CacheFile::RemoveOutput(CacheFileOutputStream *aOutput)
|
|||||||
if (!mMemoryOnly)
|
if (!mMemoryOnly)
|
||||||
WriteMetadataIfNeeded();
|
WriteMetadataIfNeeded();
|
||||||
|
|
||||||
|
// Notify close listener as the last action
|
||||||
|
aOutput->NotifyCloseListener();
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ namespace net {
|
|||||||
|
|
||||||
class CacheFileInputStream;
|
class CacheFileInputStream;
|
||||||
class CacheFileOutputStream;
|
class CacheFileOutputStream;
|
||||||
|
class CacheOutputCloseListener;
|
||||||
class MetadataWriteTimer;
|
class MetadataWriteTimer;
|
||||||
|
|
||||||
#define CACHEFILELISTENER_IID \
|
#define CACHEFILELISTENER_IID \
|
||||||
@@ -77,7 +78,7 @@ public:
|
|||||||
NS_IMETHOD OnMetadataWritten(nsresult aResult);
|
NS_IMETHOD OnMetadataWritten(nsresult aResult);
|
||||||
|
|
||||||
NS_IMETHOD OpenInputStream(nsIInputStream **_retval);
|
NS_IMETHOD OpenInputStream(nsIInputStream **_retval);
|
||||||
NS_IMETHOD OpenOutputStream(nsIOutputStream **_retval);
|
NS_IMETHOD OpenOutputStream(CacheOutputCloseListener *aCloseListener, nsIOutputStream **_retval);
|
||||||
NS_IMETHOD SetMemoryOnly();
|
NS_IMETHOD SetMemoryOnly();
|
||||||
NS_IMETHOD Doom(CacheFileListener *aCallback);
|
NS_IMETHOD Doom(CacheFileListener *aCallback);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "CacheFileOutputStream.h"
|
#include "CacheFileOutputStream.h"
|
||||||
|
|
||||||
#include "CacheFile.h"
|
#include "CacheFile.h"
|
||||||
|
#include "CacheEntry.h"
|
||||||
#include "nsStreamUtils.h"
|
#include "nsStreamUtils.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "mozilla/DebugOnly.h"
|
#include "mozilla/DebugOnly.h"
|
||||||
@@ -43,8 +44,10 @@ NS_INTERFACE_MAP_BEGIN(CacheFileOutputStream)
|
|||||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIOutputStream)
|
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIOutputStream)
|
||||||
NS_INTERFACE_MAP_END_THREADSAFE
|
NS_INTERFACE_MAP_END_THREADSAFE
|
||||||
|
|
||||||
CacheFileOutputStream::CacheFileOutputStream(CacheFile *aFile)
|
CacheFileOutputStream::CacheFileOutputStream(CacheFile *aFile,
|
||||||
|
CacheOutputCloseListener *aCloseListener)
|
||||||
: mFile(aFile)
|
: mFile(aFile)
|
||||||
|
, mCloseListener(aCloseListener)
|
||||||
, mPos(0)
|
, mPos(0)
|
||||||
, mClosed(false)
|
, mClosed(false)
|
||||||
, mStatus(NS_OK)
|
, mStatus(NS_OK)
|
||||||
@@ -294,6 +297,16 @@ CacheFileOutputStream::OnChunkUpdated(CacheFileChunk *aChunk)
|
|||||||
return NS_ERROR_UNEXPECTED;
|
return NS_ERROR_UNEXPECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CacheFileOutputStream::NotifyCloseListener()
|
||||||
|
{
|
||||||
|
nsRefPtr<CacheOutputCloseListener> listener;
|
||||||
|
listener.swap(mCloseListener);
|
||||||
|
if (!listener)
|
||||||
|
return;
|
||||||
|
|
||||||
|
listener->OnOutputClosed();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CacheFileOutputStream::ReleaseChunk()
|
CacheFileOutputStream::ReleaseChunk()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ namespace mozilla {
|
|||||||
namespace net {
|
namespace net {
|
||||||
|
|
||||||
class CacheFile;
|
class CacheFile;
|
||||||
|
class CacheOutputCloseListener;
|
||||||
|
|
||||||
class CacheFileOutputStream : public nsIAsyncOutputStream
|
class CacheFileOutputStream : public nsIAsyncOutputStream
|
||||||
, public nsISeekableStream
|
, public nsISeekableStream
|
||||||
@@ -27,7 +28,7 @@ class CacheFileOutputStream : public nsIAsyncOutputStream
|
|||||||
NS_DECL_NSISEEKABLESTREAM
|
NS_DECL_NSISEEKABLESTREAM
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CacheFileOutputStream(CacheFile *aFile);
|
CacheFileOutputStream(CacheFile *aFile, CacheOutputCloseListener *aCloseListener);
|
||||||
|
|
||||||
NS_IMETHOD OnChunkRead(nsresult aResult, CacheFileChunk *aChunk);
|
NS_IMETHOD OnChunkRead(nsresult aResult, CacheFileChunk *aChunk);
|
||||||
NS_IMETHOD OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk);
|
NS_IMETHOD OnChunkWritten(nsresult aResult, CacheFileChunk *aChunk);
|
||||||
@@ -35,6 +36,8 @@ public:
|
|||||||
CacheFileChunk *aChunk);
|
CacheFileChunk *aChunk);
|
||||||
NS_IMETHOD OnChunkUpdated(CacheFileChunk *aChunk);
|
NS_IMETHOD OnChunkUpdated(CacheFileChunk *aChunk);
|
||||||
|
|
||||||
|
void NotifyCloseListener();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual ~CacheFileOutputStream();
|
virtual ~CacheFileOutputStream();
|
||||||
|
|
||||||
@@ -45,6 +48,7 @@ private:
|
|||||||
|
|
||||||
nsRefPtr<CacheFile> mFile;
|
nsRefPtr<CacheFile> mFile;
|
||||||
nsRefPtr<CacheFileChunk> mChunk;
|
nsRefPtr<CacheFileChunk> mChunk;
|
||||||
|
nsRefPtr<CacheOutputCloseListener> mCloseListener;
|
||||||
int64_t mPos;
|
int64_t mPos;
|
||||||
bool mClosed;
|
bool mClosed;
|
||||||
nsresult mStatus;
|
nsresult mStatus;
|
||||||
|
|||||||
@@ -199,14 +199,19 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
template<class T>
|
template<class T>
|
||||||
void ProxyReleaseMainThread(nsCOMPtr<T> &object)
|
void ProxyRelease(nsCOMPtr<T> &object, nsIThread* thread)
|
||||||
{
|
{
|
||||||
T* release;
|
T* release;
|
||||||
object.forget(&release);
|
object.forget(&release);
|
||||||
|
|
||||||
nsCOMPtr<nsIThread> mainThread;
|
NS_ProxyRelease(thread, release);
|
||||||
NS_GetMainThread(getter_AddRefs(mainThread));
|
}
|
||||||
NS_ProxyRelease(mainThread, release);
|
|
||||||
|
template<class T>
|
||||||
|
void ProxyReleaseMainThread(nsCOMPtr<T> &object)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
|
||||||
|
ProxyRelease(object, mainThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // net
|
} // net
|
||||||
|
|||||||
@@ -25,6 +25,8 @@
|
|||||||
static NS_DEFINE_CID(kStreamTransportServiceCID,
|
static NS_DEFINE_CID(kStreamTransportServiceCID,
|
||||||
NS_STREAMTRANSPORTSERVICE_CID);
|
NS_STREAMTRANSPORTSERVICE_CID);
|
||||||
|
|
||||||
|
static uint32_t const CHECK_MULTITHREADED = nsICacheStorage::CHECK_MULTITHREADED;
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace net {
|
namespace net {
|
||||||
|
|
||||||
@@ -511,8 +513,9 @@ _OldCacheLoad::_OldCacheLoad(nsCSubstring const& aScheme,
|
|||||||
, mLoadInfo(GetLoadContextInfo(aLoadInfo))
|
, mLoadInfo(GetLoadContextInfo(aLoadInfo))
|
||||||
, mFlags(aFlags)
|
, mFlags(aFlags)
|
||||||
, mWriteToDisk(aWriteToDisk)
|
, mWriteToDisk(aWriteToDisk)
|
||||||
, mMainThreadOnly(true)
|
|
||||||
, mNew(true)
|
, mNew(true)
|
||||||
|
, mOpening(true)
|
||||||
|
, mSync(false)
|
||||||
, mStatus(NS_ERROR_UNEXPECTED)
|
, mStatus(NS_ERROR_UNEXPECTED)
|
||||||
, mRunCount(0)
|
, mRunCount(0)
|
||||||
, mAppCache(aAppCache)
|
, mAppCache(aAppCache)
|
||||||
@@ -529,26 +532,22 @@ _OldCacheLoad::~_OldCacheLoad()
|
|||||||
nsresult _OldCacheLoad::Start()
|
nsresult _OldCacheLoad::Start()
|
||||||
{
|
{
|
||||||
LOG(("_OldCacheLoad::Start [this=%p, key=%s]", this, mCacheKey.get()));
|
LOG(("_OldCacheLoad::Start [this=%p, key=%s]", this, mCacheKey.get()));
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
|
|
||||||
mLoadStart = mozilla::TimeStamp::Now();
|
mLoadStart = mozilla::TimeStamp::Now();
|
||||||
|
|
||||||
bool mainThreadOnly;
|
|
||||||
if (mCallback && (
|
|
||||||
NS_SUCCEEDED(mCallback->GetMainThreadOnly(&mainThreadOnly)) &&
|
|
||||||
!mainThreadOnly)) {
|
|
||||||
mMainThreadOnly = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
|
// Consumers that can invoke this code as first and off the main thread
|
||||||
|
// are responsible for initiating these two services on the main thread.
|
||||||
|
// Currently this is only nsWyciwygChannel.
|
||||||
|
|
||||||
// XXX: Start the cache service; otherwise DispatchToCacheIOThread will
|
// XXX: Start the cache service; otherwise DispatchToCacheIOThread will
|
||||||
// fail.
|
// fail.
|
||||||
nsCOMPtr<nsICacheService> service =
|
nsCOMPtr<nsICacheService> service =
|
||||||
do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
|
do_GetService(NS_CACHESERVICE_CONTRACTID, &rv);
|
||||||
|
|
||||||
// Ensure the stream transport service gets initialized on the main thread
|
// Ensure the stream transport service gets initialized on the main thread
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv) && NS_IsMainThread()) {
|
||||||
nsCOMPtr<nsIStreamTransportService> sts =
|
nsCOMPtr<nsIStreamTransportService> sts =
|
||||||
do_GetService(kStreamTransportServiceCID, &rv);
|
do_GetService(kStreamTransportServiceCID, &rv);
|
||||||
}
|
}
|
||||||
@@ -558,8 +557,21 @@ nsresult _OldCacheLoad::Start()
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
|
bool onCacheTarget;
|
||||||
|
rv = mCacheThread->IsOnCurrentThread(&onCacheTarget);
|
||||||
|
if (NS_SUCCEEDED(rv) && onCacheTarget) {
|
||||||
|
mSync = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NS_SUCCEEDED(rv)) {
|
||||||
|
if (mSync) {
|
||||||
|
rv = Run();
|
||||||
|
}
|
||||||
|
else {
|
||||||
rv = mCacheThread->Dispatch(this, NS_DISPATCH_NORMAL);
|
rv = mCacheThread->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
@@ -571,7 +583,8 @@ _OldCacheLoad::Run()
|
|||||||
|
|
||||||
nsresult rv;
|
nsresult rv;
|
||||||
|
|
||||||
if (!NS_IsMainThread()) {
|
if (mOpening) {
|
||||||
|
mOpening = false;
|
||||||
nsCOMPtr<nsICacheSession> session;
|
nsCOMPtr<nsICacheSession> session;
|
||||||
rv = GetCacheSession(mScheme, mWriteToDisk, mLoadInfo, mAppCache,
|
rv = GetCacheSession(mScheme, mWriteToDisk, mLoadInfo, mAppCache,
|
||||||
getter_AddRefs(session));
|
getter_AddRefs(session));
|
||||||
@@ -590,8 +603,21 @@ _OldCacheLoad::Run()
|
|||||||
LOG((" session->AsyncOpenCacheEntry with access=%d", cacheAccess));
|
LOG((" session->AsyncOpenCacheEntry with access=%d", cacheAccess));
|
||||||
|
|
||||||
bool bypassBusy = mFlags & nsICacheStorage::OPEN_BYPASS_IF_BUSY;
|
bool bypassBusy = mFlags & nsICacheStorage::OPEN_BYPASS_IF_BUSY;
|
||||||
rv = session->AsyncOpenCacheEntry(mCacheKey, cacheAccess, this, bypassBusy);
|
|
||||||
|
|
||||||
|
if (mSync && cacheAccess == nsICache::ACCESS_WRITE) {
|
||||||
|
nsCOMPtr<nsICacheEntryDescriptor> entry;
|
||||||
|
rv = session->OpenCacheEntry(mCacheKey, cacheAccess, bypassBusy,
|
||||||
|
getter_AddRefs(entry));
|
||||||
|
|
||||||
|
nsCacheAccessMode grantedAccess = 0;
|
||||||
|
if (NS_SUCCEEDED(rv)) {
|
||||||
|
entry->GetAccessGranted(&grantedAccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
return OnCacheEntryAvailable(entry, grantedAccess, rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = session->AsyncOpenCacheEntry(mCacheKey, cacheAccess, this, bypassBusy);
|
||||||
if (NS_SUCCEEDED(rv))
|
if (NS_SUCCEEDED(rv))
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
@@ -625,7 +651,7 @@ _OldCacheLoad::Run()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mMainThreadOnly)
|
if (!(mFlags & CHECK_MULTITHREADED))
|
||||||
Check();
|
Check();
|
||||||
|
|
||||||
// break cycles
|
// break cycles
|
||||||
@@ -666,9 +692,12 @@ _OldCacheLoad::OnCacheEntryAvailable(nsICacheEntryDescriptor *entry,
|
|||||||
mStatus = status;
|
mStatus = status;
|
||||||
mNew = access == nsICache::ACCESS_WRITE;
|
mNew = access == nsICache::ACCESS_WRITE;
|
||||||
|
|
||||||
if (!mMainThreadOnly)
|
if (mFlags & CHECK_MULTITHREADED)
|
||||||
Check();
|
Check();
|
||||||
|
|
||||||
|
if (mSync)
|
||||||
|
return Run();
|
||||||
|
|
||||||
return NS_DispatchToMainThread(this);
|
return NS_DispatchToMainThread(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -77,15 +77,16 @@ private:
|
|||||||
|
|
||||||
nsCOMPtr<nsIEventTarget> mCacheThread;
|
nsCOMPtr<nsIEventTarget> mCacheThread;
|
||||||
|
|
||||||
nsCString mScheme;
|
nsCString const mScheme;
|
||||||
nsCString mCacheKey;
|
nsCString const mCacheKey;
|
||||||
nsCOMPtr<nsICacheEntryOpenCallback> mCallback;
|
nsCOMPtr<nsICacheEntryOpenCallback> mCallback;
|
||||||
nsCOMPtr<nsILoadContextInfo> mLoadInfo;
|
nsCOMPtr<nsILoadContextInfo> mLoadInfo;
|
||||||
uint32_t mFlags;
|
uint32_t const mFlags;
|
||||||
|
|
||||||
bool const mWriteToDisk : 1;
|
bool const mWriteToDisk : 1;
|
||||||
bool mMainThreadOnly : 1;
|
|
||||||
bool mNew : 1;
|
bool mNew : 1;
|
||||||
|
bool mOpening : 1;
|
||||||
|
bool mSync : 1;
|
||||||
|
|
||||||
nsCOMPtr<nsICacheEntry> mCacheEntry;
|
nsCOMPtr<nsICacheEntry> mCacheEntry;
|
||||||
nsresult mStatus;
|
nsresult mStatus;
|
||||||
|
|||||||
@@ -7,13 +7,17 @@
|
|||||||
interface nsICacheEntry;
|
interface nsICacheEntry;
|
||||||
interface nsIApplicationCache;
|
interface nsIApplicationCache;
|
||||||
|
|
||||||
[scriptable, uuid(cdd8b9be-71f0-4b0a-a7f4-626fbb3d2e9b)]
|
[scriptable, uuid(1fc9fe11-c6ac-4748-94bd-8555a5a12b94)]
|
||||||
interface nsICacheEntryOpenCallback : nsISupports
|
interface nsICacheEntryOpenCallback : nsISupports
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* State of the entry determined by onCacheEntryCheck.
|
* State of the entry determined by onCacheEntryCheck.
|
||||||
*
|
*
|
||||||
* ENTRY_WANTED - the consumer is interested in the entry, we will pass it.
|
* ENTRY_WANTED - the consumer is interested in the entry, we will pass it.
|
||||||
|
* RECHECK_AFTER_WRITE_FINISHED - the consumer cannot use the entry while data is
|
||||||
|
* still being written and wants to check it again after the current write is
|
||||||
|
* finished. This actually prevents concurrent read/write and is used with
|
||||||
|
* non-resumable HTTP responses.
|
||||||
* ENTRY_NEEDS_REVALIDATION - entry needs to be revalidated first with origin server,
|
* ENTRY_NEEDS_REVALIDATION - entry needs to be revalidated first with origin server,
|
||||||
* this means the loading channel will decide whether to use the entry content
|
* this means the loading channel will decide whether to use the entry content
|
||||||
* as is after it gets a positive response from the server about validity of the
|
* as is after it gets a positive response from the server about validity of the
|
||||||
@@ -23,12 +27,22 @@ interface nsICacheEntryOpenCallback : nsISupports
|
|||||||
* ENTRY_NOT_WANTED - the consumer is not interested in the entry, we will not pass it.
|
* ENTRY_NOT_WANTED - the consumer is not interested in the entry, we will not pass it.
|
||||||
*/
|
*/
|
||||||
const unsigned long ENTRY_WANTED = 0;
|
const unsigned long ENTRY_WANTED = 0;
|
||||||
const unsigned long ENTRY_NEEDS_REVALIDATION = 1;
|
const unsigned long RECHECK_AFTER_WRITE_FINISHED = 1;
|
||||||
const unsigned long ENTRY_NOT_WANTED = 2;
|
const unsigned long ENTRY_NEEDS_REVALIDATION = 2;
|
||||||
|
const unsigned long ENTRY_NOT_WANTED = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback to perform any validity checks before the entry should be used.
|
* Callback to perform any validity checks before the entry should be used.
|
||||||
* Called before onCacheEntryAvailable callback.
|
* Called before onCacheEntryAvailable callback, depending on the result it
|
||||||
|
* may be called more then one time.
|
||||||
|
*
|
||||||
|
* This callback is ensured to be called on the same thread on which asyncOpenURI
|
||||||
|
* has been called, unless nsICacheStorage.CHECK_MULTITHREADED flag has been specified.
|
||||||
|
* In that case this callback can be invoked on any thread, usually it is the cache I/O
|
||||||
|
* or cache management thread.
|
||||||
|
*
|
||||||
|
* IMPORTANT NOTE:
|
||||||
|
* This callback may be invoked sooner then respective asyncOpenURI call exits.
|
||||||
*
|
*
|
||||||
* @param aEntry
|
* @param aEntry
|
||||||
* An entry to examine. Consumer has a chance to decide whether the
|
* An entry to examine. Consumer has a chance to decide whether the
|
||||||
@@ -37,15 +51,18 @@ interface nsICacheEntryOpenCallback : nsISupports
|
|||||||
* Optional, application cache the entry has been found in, if any.
|
* Optional, application cache the entry has been found in, if any.
|
||||||
* @return
|
* @return
|
||||||
* State of the entry, see the constants just above.
|
* State of the entry, see the constants just above.
|
||||||
*
|
|
||||||
* NOTE: This callback is invoked on the cache background thread.
|
|
||||||
*/
|
*/
|
||||||
unsigned long onCacheEntryCheck(in nsICacheEntry aEntry,
|
unsigned long onCacheEntryCheck(in nsICacheEntry aEntry,
|
||||||
in nsIApplicationCache aApplicationCache);
|
in nsIApplicationCache aApplicationCache);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback implemented by consumers of nsICacheStorage fetching
|
* Callback giving actual result of asyncOpenURI. It may give consumer the cache
|
||||||
* result of the cache async open request.
|
* entry or a failure result when it's not possible to open it from some reason.
|
||||||
|
* This callback is ensured to be called on the same thread on which asyncOpenURI
|
||||||
|
* has been called.
|
||||||
|
*
|
||||||
|
* IMPORTANT NOTE:
|
||||||
|
* This callback may be invoked sooner then respective asyncOpenURI call exits.
|
||||||
*
|
*
|
||||||
* @param aEntry
|
* @param aEntry
|
||||||
* The entry bound to the originally requested URI. May be null when
|
* The entry bound to the originally requested URI. May be null when
|
||||||
@@ -60,24 +77,15 @@ interface nsICacheEntryOpenCallback : nsISupports
|
|||||||
* given application cache. It should be associated with the loading
|
* given application cache. It should be associated with the loading
|
||||||
* channel.
|
* channel.
|
||||||
* @param aResult
|
* @param aResult
|
||||||
* Result of request. This may be a failure only when one of these
|
* Result of the request. This may be a failure only when one of these
|
||||||
* issues occur:
|
* issues occur:
|
||||||
* - the cache storage service could not be started due to some unexpected
|
* - the cache storage service could not be started due to some unexpected
|
||||||
* faulure
|
* faulure
|
||||||
* - there is not enough disk space to create new entries
|
* - there is not enough disk space to create new entries
|
||||||
* - cache entry was not found in a given application cache
|
* - cache entry was not found in a given application cache
|
||||||
*
|
|
||||||
* NOTE: In the current implementation this callback is invoked on the main thread
|
|
||||||
* however, we would like to call this on a different thread in the future.
|
|
||||||
*/
|
*/
|
||||||
void onCacheEntryAvailable(in nsICacheEntry aEntry,
|
void onCacheEntryAvailable(in nsICacheEntry aEntry,
|
||||||
in boolean aNew,
|
in boolean aNew,
|
||||||
in nsIApplicationCache aApplicationCache,
|
in nsIApplicationCache aApplicationCache,
|
||||||
in nsresult aResult);
|
in nsresult aResult);
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether this callback can be invoked on any thread, or just on the main thread
|
|
||||||
* when the consumer is e.g. a JS.
|
|
||||||
*/
|
|
||||||
readonly attribute boolean mainThreadOnly;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -38,13 +38,17 @@ interface nsICacheStorage : nsISupports
|
|||||||
const uint32_t OPEN_PRIORITY = 1 << 2;
|
const uint32_t OPEN_PRIORITY = 1 << 2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BACKWARD COMPATIBILITY ONLY
|
* Bypass the cache load when write is still in progress.
|
||||||
*
|
|
||||||
* Reflects LOAD_BYPASS_LOCAL_CACHE_IF_BUSY. Only used for the old
|
|
||||||
* backend compatibility. Doesn't have any mening in the new
|
|
||||||
* implementation.
|
|
||||||
*/
|
*/
|
||||||
const uint32_t OPEN_BYPASS_IF_BUSY = 1 << 31;
|
const uint32_t OPEN_BYPASS_IF_BUSY = 1 << 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the cache entry check (onCacheEntryCheck invocation) on any thread
|
||||||
|
* for optimal perfomance optimization. If this flag is not specified it is
|
||||||
|
* ensured that onCacheEntryCheck is called on the same thread as respective
|
||||||
|
* asyncOpen has been called.
|
||||||
|
*/
|
||||||
|
const uint32_t CHECK_MULTITHREADED = 1 << 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asynchronously opens a cache entry for the specified URI.
|
* Asynchronously opens a cache entry for the specified URI.
|
||||||
@@ -63,6 +67,8 @@ interface nsICacheStorage : nsISupports
|
|||||||
* OPEN_READONLY - don't create an entry if there is none
|
* OPEN_READONLY - don't create an entry if there is none
|
||||||
* OPEN_PRIORITY - give this request a priority over others
|
* OPEN_PRIORITY - give this request a priority over others
|
||||||
* OPEN_BYPASS_IF_BUSY - backward compatibility only, LOAD_BYPASS_LOCAL_CACHE_IF_BUSY
|
* OPEN_BYPASS_IF_BUSY - backward compatibility only, LOAD_BYPASS_LOCAL_CACHE_IF_BUSY
|
||||||
|
* CHECK_MULTITHREADED - onCacheEntryCheck may be called on any thread, consumer
|
||||||
|
* implementation is thread-safe
|
||||||
* @param aCallback
|
* @param aCallback
|
||||||
* The consumer that receives the result.
|
* The consumer that receives the result.
|
||||||
* IMPORTANT: The callback may be called sooner the method returns.
|
* IMPORTANT: The callback may be called sooner the method returns.
|
||||||
|
|||||||
@@ -2538,7 +2538,8 @@ nsHttpChannel::OpenCacheEntry(bool usingSSL)
|
|||||||
cacheEntryOpenFlags = nsICacheStorage::OPEN_TRUNCATE;
|
cacheEntryOpenFlags = nsICacheStorage::OPEN_TRUNCATE;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cacheEntryOpenFlags = nsICacheStorage::OPEN_NORMALLY;
|
cacheEntryOpenFlags = nsICacheStorage::OPEN_NORMALLY
|
||||||
|
| nsICacheStorage::CHECK_MULTITHREADED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mApplicationCache) {
|
if (mApplicationCache) {
|
||||||
@@ -3144,16 +3145,6 @@ nsHttpChannel::OnOfflineCacheEntryForWritingAvailable(nsICacheEntry *aEntry,
|
|||||||
return aEntryStatus;
|
return aEntryStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
nsHttpChannel::GetMainThreadOnly(bool *aMainThreadOnly)
|
|
||||||
{
|
|
||||||
NS_ENSURE_ARG(aMainThreadOnly);
|
|
||||||
|
|
||||||
// This implementation accepts callbacks on any thread
|
|
||||||
*aMainThreadOnly = false;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generates the proper cache-key for this instance of nsHttpChannel
|
// Generates the proper cache-key for this instance of nsHttpChannel
|
||||||
nsresult
|
nsresult
|
||||||
nsHttpChannel::GenerateCacheKey(uint32_t postID, nsACString &cacheKey)
|
nsHttpChannel::GenerateCacheKey(uint32_t postID, nsACString &cacheKey)
|
||||||
|
|||||||
@@ -85,10 +85,6 @@ function asyncOpenCacheEntry(key, where, flags, lci, callback, appcache)
|
|||||||
callback(status, entry, appCache);
|
callback(status, entry, appCache);
|
||||||
},
|
},
|
||||||
|
|
||||||
get mainThreadOnly() {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
|
|
||||||
run: function () {
|
run: function () {
|
||||||
var storage = getCacheStorage(where, lci, this._appCache);
|
var storage = getCacheStorage(where, lci, this._appCache);
|
||||||
storage.asyncOpenURI(key, "", flags, this);
|
storage.asyncOpenURI(key, "", flags, this);
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ const METAONLY = 1 << 9;
|
|||||||
const RECREATE = 1 << 10;
|
const RECREATE = 1 << 10;
|
||||||
// Do not give me the entry
|
// Do not give me the entry
|
||||||
const NOTWANTED = 1 << 11;
|
const NOTWANTED = 1 << 11;
|
||||||
|
// Tell the cache to wait for the entry to be completely written first
|
||||||
|
const COMPLETE = 1 << 12;
|
||||||
|
|
||||||
var log_c2 = true;
|
var log_c2 = true;
|
||||||
function LOG_C2(o, m)
|
function LOG_C2(o, m)
|
||||||
@@ -117,10 +119,26 @@ OpenCallback.prototype =
|
|||||||
do_check_neq(this.behavior & (REVAL|PARTIAL), REVAL|PARTIAL);
|
do_check_neq(this.behavior & (REVAL|PARTIAL), REVAL|PARTIAL);
|
||||||
|
|
||||||
if (this.behavior & (REVAL|PARTIAL)) {
|
if (this.behavior & (REVAL|PARTIAL)) {
|
||||||
LOG_C2(this, "onCacheEntryCheck DONE, return REVAL");
|
LOG_C2(this, "onCacheEntryCheck DONE, return ENTRY_NEEDS_REVALIDATION");
|
||||||
return Ci.nsICacheEntryOpenCallback.ENTRY_NEEDS_REVALIDATION;
|
return Ci.nsICacheEntryOpenCallback.ENTRY_NEEDS_REVALIDATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.behavior & COMPLETE) {
|
||||||
|
LOG_C2(this, "onCacheEntryCheck DONE, return RECHECK_AFTER_WRITE_FINISHED");
|
||||||
|
if (newCacheBackEndUsed()) {
|
||||||
|
// Specific to the new backend because of concurrent read/write:
|
||||||
|
// when a consumer returns RECHECK_AFTER_WRITE_FINISHED from onCacheEntryCheck
|
||||||
|
// the cache calls this callback again after the entry write has finished.
|
||||||
|
// This gives the consumer a chance to recheck completeness of the entry
|
||||||
|
// again.
|
||||||
|
// Thus, we reset state as onCheck would have never been called.
|
||||||
|
this.onCheckPassed = false;
|
||||||
|
// Don't return RECHECK_AFTER_WRITE_FINISHED on second call of onCacheEntryCheck.
|
||||||
|
this.behavior &= ~COMPLETE;
|
||||||
|
}
|
||||||
|
return Ci.nsICacheEntryOpenCallback.RECHECK_AFTER_WRITE_FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
LOG_C2(this, "onCacheEntryCheck DONE, return ENTRY_WANTED");
|
LOG_C2(this, "onCacheEntryCheck DONE, return ENTRY_WANTED");
|
||||||
return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
|
return Ci.nsICacheEntryOpenCallback.ENTRY_WANTED;
|
||||||
},
|
},
|
||||||
@@ -218,9 +236,6 @@ OpenCallback.prototype =
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
get mainThreadOnly() {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
selfCheck: function()
|
selfCheck: function()
|
||||||
{
|
{
|
||||||
LOG_C2(this, "selfCheck");
|
LOG_C2(this, "selfCheck");
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
function run_test()
|
||||||
|
{
|
||||||
|
do_get_profile();
|
||||||
|
|
||||||
|
asyncOpenCacheEntry("http://x/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
|
||||||
|
new OpenCallback(NEW, "x1m", "x1d", function(entry) {
|
||||||
|
// nothing to do here, we expect concurent callbacks to get
|
||||||
|
// all notified, then the test finishes
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
var mc = new MultipleCallbacks(3, finish_cache2_test);
|
||||||
|
|
||||||
|
var order = 0;
|
||||||
|
|
||||||
|
asyncOpenCacheEntry("http://x/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
|
||||||
|
new OpenCallback(NORMAL|COMPLETE, "x1m", "x1d", function(entry) {
|
||||||
|
++order;
|
||||||
|
do_check_eq(order, newCacheBackEndUsed() ? 3 : 1);
|
||||||
|
mc.fired();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
asyncOpenCacheEntry("http://x/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
|
||||||
|
new OpenCallback(NORMAL, "x1m", "x1d", function(entry) {
|
||||||
|
++order;
|
||||||
|
do_check_eq(order, newCacheBackEndUsed() ? 1 : 2);
|
||||||
|
mc.fired();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
asyncOpenCacheEntry("http://x/", "disk", Ci.nsICacheStorage.OPEN_NORMALLY, null,
|
||||||
|
new OpenCallback(NORMAL, "x1m", "x1d", function(entry) {
|
||||||
|
++order;
|
||||||
|
do_check_eq(order, newCacheBackEndUsed() ? 2 : 3);
|
||||||
|
mc.fired();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
do_test_pending();
|
||||||
|
}
|
||||||
@@ -39,6 +39,7 @@ skip-if = os == "android"
|
|||||||
[test_cache2-12-evict-disk.js]
|
[test_cache2-12-evict-disk.js]
|
||||||
[test_cache2-13-evict-non-existing.js]
|
[test_cache2-13-evict-non-existing.js]
|
||||||
[test_cache2-14-concurent-readers.js]
|
[test_cache2-14-concurent-readers.js]
|
||||||
|
[test_cache2-14b-concurent-readers-complete.js]
|
||||||
[test_cache2-15-conditional-304.js]
|
[test_cache2-15-conditional-304.js]
|
||||||
[test_cache2-16-conditional-200.js]
|
[test_cache2-16-conditional-200.js]
|
||||||
[test_cache2-17-evict-all.js]
|
[test_cache2-17-evict-all.js]
|
||||||
|
|||||||
Reference in New Issue
Block a user