Exemplo n.º 1
0
void CacheEntry::DoomAlreadyRemoved()
{
  LOG(("CacheEntry::DoomAlreadyRemoved [this=%p]", this));

  mIsDoomed = true;

  if (!CacheStorageService::IsOnManagementThread()) {
    mozilla::MutexAutoLock lock(mLock);

    BackgroundOp(Ops::DOOM);
    return;
  }

  CacheStorageService::Self()->UnregisterEntry(this);

  {
    mozilla::MutexAutoLock lock(mLock);

    if (mCallbacks.Length() || mReadOnlyCallbacks.Length()) {
      // Must force post here since may be indirectly called from
      // InvokeCallbacks of this entry and we don't want reentrancy here.
      BackgroundOp(Ops::CALLBACKS, true);
    }
  }

  if (NS_SUCCEEDED(mFileStatus)) {
    nsresult rv = mFile->Doom(mDoomCallback ? this : nullptr);
    if (NS_SUCCEEDED(rv)) {
      LOG(("  file doomed"));
      return;
    }
  }

  OnFileDoomed(NS_OK);
}
Exemplo n.º 2
0
CacheEntry::Handle* CacheEntry::NewWriteHandle()
{
  mozilla::MutexAutoLock lock(mLock);

  BackgroundOp(Ops::FRECENCYUPDATE);
  return (mWriter = new Handle(this));
}
Exemplo n.º 3
0
NS_IMETHODIMP CacheEntry::Run()
{
  MOZ_ASSERT(CacheStorageService::IsOnManagementThread());

  mozilla::MutexAutoLock lock(mLock);

  BackgroundOp(mBackgroundOperations.Grab());
  return NS_OK;
}
Exemplo n.º 4
0
CacheEntryHandle* CacheEntry::NewWriteHandle()
{
  mozilla::MutexAutoLock lock(mLock);

  // Ignore the OPEN_SECRETLY flag on purpose here, which should actually be
  // used only along with OPEN_READONLY, but there is no need to enforce that.
  BackgroundOp(Ops::FRECENCYUPDATE);

  return (mWriter = NewHandle());
}
Exemplo n.º 5
0
void CacheEntry::DoomAlreadyRemoved()
{
  LOG(("CacheEntry::DoomAlreadyRemoved [this=%p]", this));

  mozilla::MutexAutoLock lock(mLock);

  mIsDoomed = true;

  // This schedules dooming of the file, dooming is ensured to happen
  // sooner than demand to open the same file made after this point
  // so that we don't get this file for any newer opened entry(s).
  DoomFile();

  // Must force post here since may be indirectly called from
  // InvokeCallbacks of this entry and we don't want reentrancy here.
  BackgroundOp(Ops::CALLBACKS, true);
  // Process immediately when on the management thread.
  BackgroundOp(Ops::UNREGISTER);
}
Exemplo n.º 6
0
void CacheEntry::TransferCallbacks(CacheEntry const& aFromEntry)
{
  mozilla::MutexAutoLock lock(mLock);

  LOG(("CacheEntry::TransferCallbacks [entry=%p, from=%p]",
    this, &aFromEntry));

  mCallbacks.AppendObjects(aFromEntry.mCallbacks);
  mReadOnlyCallbacks.AppendObjects(aFromEntry.mReadOnlyCallbacks);
  if (aFromEntry.mHasMainThreadOnlyCallback)
    mHasMainThreadOnlyCallback = true;

  if (mCallbacks.Length() || mReadOnlyCallbacks.Length())
    BackgroundOp(Ops::CALLBACKS, true);
}
Exemplo n.º 7
0
NS_IMETHODIMP CacheEntry::Recreate(nsICacheEntry **_retval)
{
  LOG(("CacheEntry::Recreate [this=%p, state=%s]", this, StateString(mState)));

  mozilla::MutexAutoLock lock(mLock);

  nsRefPtr<CacheEntryHandle> handle = ReopenTruncated(nullptr);
  if (handle) {
    handle.forget(_retval);
    return NS_OK;
  }

  BackgroundOp(Ops::CALLBACKS, true);
  return NS_OK;
}
Exemplo n.º 8
0
NS_IMETHODIMP CacheEntry::AsyncDoom(nsICacheEntryDoomCallback *aCallback)
{
  LOG(("CacheEntry::AsyncDoom [this=%p]", this));

  {
    mozilla::MutexAutoLock lock(mLock);

    if (mIsDoomed || mDoomCallback)
      return NS_ERROR_IN_PROGRESS; // to aggregate have DOOMING state

    mIsDoomed = true;
    mDoomCallback = aCallback;
    BackgroundOp(Ops::DOOM);
  }

  // Immediately remove the entry from the storage hash table
  CacheStorageService::Self()->RemoveEntry(this);

  return NS_OK;
}
Exemplo n.º 9
0
void CacheEntry::TransferCallbacks(CacheEntry & aFromEntry)
{
  mozilla::MutexAutoLock lock(mLock);

  LOG(("CacheEntry::TransferCallbacks [entry=%p, from=%p]",
    this, &aFromEntry));

  if (!mCallbacks.Length())
    mCallbacks.SwapElements(aFromEntry.mCallbacks);
  else
    mCallbacks.AppendElements(aFromEntry.mCallbacks);

  uint32_t callbacksLength = mCallbacks.Length();
  if (callbacksLength) {
    // Carry the entry reference (unfortunatelly, needs to be done manually...)
    for (uint32_t i = 0; i < callbacksLength; ++i)
      mCallbacks[i].ExchangeEntry(this);

    BackgroundOp(Ops::CALLBACKS, true);
  }
}
Exemplo n.º 10
0
void CacheEntry::InvokeAvailableCallback(Callback const & aCallback)
{
  LOG(("CacheEntry::InvokeAvailableCallback [this=%p, state=%s, cb=%p, r/o=%d, n/w=%d]",
    this, StateString(mState), aCallback.mCallback.get(), aCallback.mReadOnly, aCallback.mNotWanted));

  nsresult rv;

  uint32_t const state = mState;

  // When we are here, the entry must be loaded from disk
  MOZ_ASSERT(state > LOADING || mIsDoomed);

  bool onAvailThread;
  rv = aCallback.OnAvailThread(&onAvailThread);
  if (NS_FAILED(rv)) {
    LOG(("  target thread dead?"));
    return;
  }

  if (!onAvailThread) {
    // Dispatch to the right thread
    nsRefPtr<AvailableCallbackRunnable> event =
      new AvailableCallbackRunnable(this, aCallback);

    rv = aCallback.mTargetThread->Dispatch(event, nsIEventTarget::DISPATCH_NORMAL);
    LOG(("  redispatched, (rv = 0x%08x)", rv));
    return;
  }

  if (NS_SUCCEEDED(mFileStatus) && !aCallback.mSecret) {
    // Let the last-fetched and fetch-count properties be updated.
    mFile->OnFetched();
  }

  if (mIsDoomed || aCallback.mNotWanted) {
    LOG(("  doomed or not wanted, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND"));
    aCallback.mCallback->OnCacheEntryAvailable(
      nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND);
    return;
  }

  if (state == READY) {
    LOG(("  ready/has-meta, notifying OCEA with entry and NS_OK"));

    if (!aCallback.mSecret)
    {
      mozilla::MutexAutoLock lock(mLock);
      BackgroundOp(Ops::FRECENCYUPDATE);
    }

    nsRefPtr<CacheEntryHandle> handle = NewHandle();
    aCallback.mCallback->OnCacheEntryAvailable(
      handle, false, nullptr, NS_OK);
    return;
  }

  // R/O callbacks may do revalidation, let them fall through
  if (aCallback.mReadOnly && !aCallback.mRevalidating) {
    LOG(("  r/o and not ready, notifying OCEA with NS_ERROR_CACHE_KEY_NOT_FOUND"));
    aCallback.mCallback->OnCacheEntryAvailable(
      nullptr, false, nullptr, NS_ERROR_CACHE_KEY_NOT_FOUND);
    return;
  }

  // This is a new or potentially non-valid entry and needs to be fetched first.
  // The CacheEntryHandle blocks other consumers until the channel
  // either releases the entry or marks metadata as filled or whole entry valid,
  // i.e. until MetaDataReady() or SetValid() on the entry is called respectively.

  // Consumer will be responsible to fill or validate the entry metadata and data.

  nsRefPtr<CacheEntryHandle> handle = NewWriteHandle();
  rv = aCallback.mCallback->OnCacheEntryAvailable(
    handle, state == WRITING, nullptr, NS_OK);

  if (NS_FAILED(rv)) {
    LOG(("  writing/revalidating failed (0x%08x)", rv));

    // Consumer given a new entry failed to take care of the entry.
    OnHandleClosed(handle);
    return;
  }

  LOG(("  writing/revalidating"));
}
Exemplo n.º 11
0
bool CacheEntry::Load(bool aTruncate, bool aPriority)
{
  LOG(("CacheEntry::Load [this=%p, trunc=%d]", this, aTruncate));

  mLock.AssertCurrentThreadOwns();

  if (mState > LOADING) {
    LOG(("  already loaded"));
    return false;
  }

  if (mState == LOADING) {
    LOG(("  already loading"));
    return true;
  }

  mState = LOADING;

  MOZ_ASSERT(!mFile);

  nsresult rv;

  nsAutoCString fileKey;
  rv = HashingKeyWithStorage(fileKey);

  // Check the index under two conditions for two states and take appropriate action:
  // 1. When this is a disk entry and not told to truncate, check there is a disk file.
  //    If not, set the 'truncate' flag to true so that this entry will open instantly
  //    as a new one.
  // 2. When this is a memory-only entry, check there is a disk file.
  //    If there is or could be, doom that file.
  if ((!aTruncate || !mUseDisk) && NS_SUCCEEDED(rv)) {
    // Check the index right now to know we have or have not the entry
    // as soon as possible.
    CacheIndex::EntryStatus status;
    if (NS_SUCCEEDED(CacheIndex::HasEntry(fileKey, &status))) {
      switch (status) {
      case CacheIndex::DOES_NOT_EXIST:
        LOG(("  entry doesn't exist according information from the index, truncating"));
        aTruncate = true;
        break;
      case CacheIndex::EXISTS:
      case CacheIndex::DO_NOT_KNOW:
        if (!mUseDisk) {
          LOG(("  entry open as memory-only, but there is (status=%d) a file, dooming it", status));
          CacheFileIOManager::DoomFileByKey(fileKey, nullptr);
        }
        break;
      }
    }
  }

  mFile = new CacheFile();

  BackgroundOp(Ops::REGISTER);

  bool directLoad = aTruncate || !mUseDisk;
  if (directLoad) {
    // mLoadStart will be used to calculate telemetry of life-time of this entry.
    // Low resulution is then enough.
    mLoadStart = TimeStamp::NowLoRes();
  } else {
    mLoadStart = TimeStamp::Now();
  }

  {
    mozilla::MutexAutoUnlock unlock(mLock);

    LOG(("  performing load, file=%p", mFile.get()));
    if (NS_SUCCEEDED(rv)) {
      rv = mFile->Init(fileKey,
                       aTruncate,
                       !mUseDisk,
                       aPriority,
                       directLoad ? nullptr : this);
    }

    if (NS_FAILED(rv)) {
      mFileStatus = rv;
      AsyncDoom(nullptr);
      return false;
    }
  }

  if (directLoad) {
    // Just fake the load has already been done as "new".
    mFileStatus = NS_OK;
    mState = EMPTY;
  }

  return mState == LOADING;
}
Exemplo n.º 12
0
void CacheEntry::InvokeAvailableCallback(nsICacheEntryOpenCallback* aCallback,
                                         bool aReadOnly,
                                         bool aNotWanted)
{
  LOG(("CacheEntry::InvokeAvailableCallback [this=%p, state=%s, cb=%p, r/o=%d, n/w=%d]",
    this, StateString(mState), aCallback, aReadOnly, aNotWanted));

  uint32_t const state = mState;

  // When we are here, the entry must be loaded from disk
  MOZ_ASSERT(state > LOADING || mIsDoomed);

  if (!NS_IsMainThread()) {
    // Must happen on the main thread :(
    nsRefPtr<AvailableCallbackRunnable> event =
      new AvailableCallbackRunnable(this, aCallback, aReadOnly, aNotWanted);
    NS_DispatchToMainThread(event);
    return;
  }

  // This happens only on the main thread / :( /

  if (mIsDoomed || aNotWanted) {
    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);
    return;
  }

  if (state == READY) {
    LOG(("  ready/has-meta, notifying OCEA with entry and NS_OK"));
    {
      mozilla::MutexAutoLock lock(mLock);
      BackgroundOp(Ops::FRECENCYUPDATE);
    }

    aCallback->OnCacheEntryAvailable(this, false, nullptr, NS_OK);
    return;
  }

  if (aReadOnly) {
    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);
    return;
  }

  // This is a new or potentially non-valid entry and needs to be fetched first.
  // The Handle blocks other consumers until the channel
  // either releases the entry or marks metadata as filled or whole entry valid,
  // i.e. until MetaDataReady() or SetValid() on the entry is called respectively.

  // Consumer will be responsible to fill or validate the entry metadata and data.

  nsRefPtr<Handle> handle = NewWriteHandle();
  nsresult rv = aCallback->OnCacheEntryAvailable(handle, state == WRITING, nullptr, NS_OK);

  if (NS_FAILED(rv)) {
    LOG(("  writing/revalidating failed (0x%08x)", rv));

    // Consumer given a new entry failed to take care of the entry.
    OnWriterClosed(handle);
    return;
  }

  LOG(("  writing/revalidating"));
}