void StorageDBThread::SyncPreload(StorageCacheBridge* aCache, bool aForceSync) { PROFILER_LABEL_FUNC(js::ProfileEntry::Category::STORAGE); if (!aForceSync && aCache->LoadedCount()) { // Preload already started for this cache, just wait for it to finish. // LoadWait will exit after LoadDone on the cache has been called. SetHigherPriority(); aCache->LoadWait(); SetDefaultPriority(); return; } // Bypass sync load when an update is pending in the queue to write, we would // get incosistent data in the cache. Also don't allow sync main-thread // preload when DB open and init is still pending on the background thread. if (mDBReady && mWALModeEnabled) { bool pendingTasks; { MonitorAutoLock monitor(mThreadObserver->GetMonitor()); pendingTasks = mPendingTasks.IsOriginUpdatePending(aCache->OriginSuffix(), aCache->OriginNoSuffix()) || mPendingTasks.IsOriginClearPending(aCache->OriginSuffix(), aCache->OriginNoSuffix()); } if (!pendingTasks) { // WAL is enabled, thus do the load synchronously on the main thread. DBOperation preload(DBOperation::opPreload, aCache); preload.PerformAndFinalize(this); return; } } // Need to go asynchronously since WAL is not allowed or scheduled updates // need to be flushed first. // Schedule preload for this cache as the first operation. nsresult rv = InsertDBOp(new DBOperation(DBOperation::opPreloadUrgent, aCache)); // LoadWait exits after LoadDone of the cache has been called. if (NS_SUCCEEDED(rv)) { aCache->LoadWait(); } }
nsresult DOMStorageDBThread::InsertDBOp(DOMStorageDBThread::DBOperation* aOperation) { MonitorAutoLock monitor(mThreadObserver->GetMonitor()); // Sentinel to don't forget to delete the operation when we exit early. nsAutoPtr<DOMStorageDBThread::DBOperation> opScope(aOperation); if (mStopIOThread) { // Thread use after shutdown demanded. MOZ_ASSERT(false); return NS_ERROR_NOT_INITIALIZED; } if (NS_FAILED(mStatus)) { MonitorAutoUnlock unlock(mThreadObserver->GetMonitor()); aOperation->Finalize(mStatus); return mStatus; } switch (aOperation->Type()) { case DBOperation::opPreload: case DBOperation::opPreloadUrgent: if (mPendingTasks.IsScopeUpdatePending(aOperation->Scope())) { // If there is a pending update operation for the scope first do the flush // before we preload the cache. This may happen in an extremely rare case // when a child process throws away its cache before flush on the parent // has finished. If we would preloaded the cache as a priority operation // before the pending flush, we would have got an inconsistent cache content. mFlushImmediately = true; } else if (mPendingTasks.IsScopeClearPending(aOperation->Scope())) { // The scope is scheduled to be cleared, so just quickly load as empty. // We need to do this to prevent load of the DB data before the scope has // actually been cleared from the database. Preloads are processed // immediately before update and clear operations on the database that // are flushed periodically in batches. MonitorAutoUnlock unlock(mThreadObserver->GetMonitor()); aOperation->Finalize(NS_OK); return NS_OK; } // NO BREAK case DBOperation::opGetUsage: if (aOperation->Type() == DBOperation::opPreloadUrgent) { SetHigherPriority(); // Dropped back after urgent preload execution mPreloads.InsertElementAt(0, aOperation); } else { mPreloads.AppendElement(aOperation); } // DB operation adopted, don't delete it. opScope.forget(); // Immediately start executing this. monitor.Notify(); break; default: // Update operations are first collected, coalesced and then flushed // after a short time. mPendingTasks.Add(aOperation); // DB operation adopted, don't delete it. opScope.forget(); ScheduleFlush(); break; } return NS_OK; }