Exemplo n.º 1
0
void CacheEntry::AsyncOpen(nsICacheEntryOpenCallback* aCallback, uint32_t aFlags)
{
  LOG(("CacheEntry::AsyncOpen [this=%p, state=%s, flags=%d, callback=%p]",
    this, StateString(mState), aFlags, aCallback));

  bool readonly = aFlags & nsICacheStorage::OPEN_READONLY;
  bool bypassIfBusy = aFlags & nsICacheStorage::OPEN_BYPASS_IF_BUSY;
  bool truncate = aFlags & nsICacheStorage::OPEN_TRUNCATE;
  bool priority = aFlags & nsICacheStorage::OPEN_PRIORITY;
  bool multithread = aFlags & nsICacheStorage::CHECK_MULTITHREADED;
  bool secret = aFlags & nsICacheStorage::OPEN_SECRETLY;

  MOZ_ASSERT(!readonly || !truncate, "Bad flags combination");
  MOZ_ASSERT(!(truncate && mState > LOADING), "Must not call truncate on already loaded entry");

  Callback callback(this, aCallback, readonly, multithread, secret);

  if (!Open(callback, truncate, priority, bypassIfBusy)) {
    // We get here when the callback wants to bypass cache when it's busy.
    LOG(("  writing or revalidating, callback wants to bypass cache"));
    callback.mNotWanted = true;
    InvokeAvailableCallback(callback);
  }
}
Exemplo n.º 2
0
bool CacheEntry::InvokeCallback(Callback & aCallback)
{
  LOG(("CacheEntry::InvokeCallback [this=%p, state=%s, cb=%p]",
    this, StateString(mState), aCallback.mCallback.get()));

  mLock.AssertCurrentThreadOwns();

  // When this entry is doomed we want to notify the callback any time
  if (!mIsDoomed) {
    // When we are here, the entry must be loaded from disk
    MOZ_ASSERT(mState > LOADING);

    if (mState == WRITING || mState == REVALIDATING) {
      // Prevent invoking other callbacks since one of them is now writing
      // or revalidating this entry.  No consumers should get this entry
      // until metadata are filled with values downloaded from the server
      // or the entry revalidated and output stream has been opened.
      LOG(("  entry is being written/revalidated, callback bypassed"));
      return false;
    }

    // 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.mRecheckAfterWrite) {

      if (!aCallback.mReadOnly) {
        if (mState == EMPTY) {
          // 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
          // to prevent more then one
          mState = WRITING;
          LOG(("  advancing to WRITING state"));
        }

        if (!aCallback.mCallback) {
          // 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 write this entry now.
          return true;
        }
      }

      if (mState == READY) {
        // Metadata present, validate the entry
        uint32_t checkResult;
        {
          // mayhemer: TODO check and solve any potential races of concurent OnCacheEntryCheck
          mozilla::MutexAutoUnlock unlock(mLock);

          nsresult rv = aCallback.mCallback->OnCacheEntryCheck(
            this, nullptr, &checkResult);
          LOG(("  OnCacheEntryCheck: rv=0x%08x, result=%d", rv, checkResult));

          if (NS_FAILED(rv))
            checkResult = ENTRY_NOT_WANTED;
        }

        aCallback.mRevalidating = checkResult == ENTRY_NEEDS_REVALIDATION;

        switch (checkResult) {
        case ENTRY_WANTED:
          // Nothing more to do here, the consumer is responsible to handle
          // the result of OnCacheEntryCheck it self.
          // Proceed to callback...
          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:
          LOG(("  will be holding callbacks until entry is revalidated"));
          // State is READY now and from that state entry cannot transit to any other
          // state then REVALIDATING for which cocurrency is not an issue.  Potentially
          // no need to lock here.
          mState = REVALIDATING;
          break;

        case ENTRY_NOT_WANTED:
          LOG(("  consumer not interested in the entry"));
          // Do not give this entry to the consumer, it is not interested in us.
          aCallback.mNotWanted = true;
          break;
        }
      }
    }
  }

  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 && NS_SUCCEEDED(mFileStatus)) {
        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);
    InvokeAvailableCallback(aCallback);
  }

  return true;
}
Exemplo n.º 3
0
bool CacheEntry::InvokeCallback(nsICacheEntryOpenCallback* aCallback,
                                bool aReadOnly)
{
  LOG(("CacheEntry::InvokeCallback [this=%p, state=%s, cb=%p]",
    this, StateString(mState), aCallback));

  mLock.AssertCurrentThreadOwns();

  bool notWanted = false;

  if (!mIsDoomed) {
    // When we are here, the entry must be loaded from disk
    MOZ_ASSERT(mState > LOADING);

    if (mState == WRITING ||
        mState == REVALIDATING) {
      // Prevent invoking other callbacks since one of them is now writing
      // or revalidating this entry.  No consumers should get this entry
      // until metadata are filled with values downloaded from the server
      // or the entry revalidated and output stream has been opened.
      LOG(("  entry is being written/revalidated, callback bypassed"));
      return false;
    }

    if (!aReadOnly) {
      if (mState == EMPTY) {
        // 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
        // to prevent more then one
        mState = WRITING;
        LOG(("  advancing to WRITING state"));
      }

      if (!aCallback) {
        // 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 write this entry now.
        return true;
      }

      if (mState == READY) {
        // Metadata present, validate the entry
        uint32_t checkResult;
        {
          // mayhemer: TODO check and solve any potential races of concurent OnCacheEntryCheck
          mozilla::MutexAutoUnlock unlock(mLock);

          nsresult rv = aCallback->OnCacheEntryCheck(this, nullptr, &checkResult);
          LOG(("  OnCacheEntryCheck: rv=0x%08x, result=%d", rv, checkResult));

          if (NS_FAILED(rv))
            checkResult = ENTRY_WANTED;
        }

        switch (checkResult) {
        case ENTRY_WANTED:
          // Nothing more to do here, the consumer is responsible to handle
          // the result of OnCacheEntryCheck it self.
          // Proceed to callback...
          break;

        case ENTRY_NEEDS_REVALIDATION:
          LOG(("  will be holding callbacks until entry is revalidated"));
          // State is READY now and from that state entry cannot transit to any other
          // state then REVALIDATING for which cocurrency is not an issue.  Potentially
          // no need to lock here.
          mState = REVALIDATING;
          break;

        case ENTRY_NOT_WANTED:
          LOG(("  consumer not interested in the entry"));
          // Do not give this entry to the consumer, it is not interested in us.
          notWanted = true;
          break;
        }
      }
    }
  }

  if (aCallback) {
    mozilla::MutexAutoUnlock unlock(mLock);
    InvokeAvailableCallback(aCallback, aReadOnly, notWanted);
  }

  return true;
}