📄 icondatabase.cpp
字号:
pool.cycle(); } } // Stop the import at any time of the thread has been asked to shutdown if (shouldStopThreadActivity()) { LOG(IconDatabase, "IconDatabase asked to terminate during performURLImport()"); return; } result = query.step(); } if (result != SQLResultDone) LOG(IconDatabase, "Error reading page->icon url mappings from database"); // Clear the m_pageURLsPendingImport set - either the page URLs ended up with an iconURL (that we'll notify about) or not, // but after m_iconURLImportComplete is set to true, we don't care about this set anymore Vector<String> urls; { MutexLocker locker(m_pendingReadingLock); urls.appendRange(m_pageURLsPendingImport.begin(), m_pageURLsPendingImport.end()); m_pageURLsPendingImport.clear(); m_iconURLImportComplete = true; } Vector<String> urlsToNotify; // Loop through the urls pending import // Remove unretained ones if database cleanup is allowed // Keep a set of ones that are retained and pending notification { MutexLocker locker(m_urlAndIconLock); for (unsigned i = 0; i < urls.size(); ++i) { if (!m_retainedPageURLs.contains(urls[i])) { PageURLRecord* record = m_pageURLToRecordMap.get(urls[i]); if (record && !databaseCleanupCounter) { m_pageURLToRecordMap.remove(urls[i]); IconRecord* iconRecord = record->iconRecord(); // If this page is the only remaining retainer of its icon, mark that icon for deletion and don't bother // reading anything related to it if (iconRecord && iconRecord->hasOneRef()) { m_iconURLToRecordMap.remove(iconRecord->iconURL()); { MutexLocker locker(m_pendingReadingLock); m_pageURLsInterestedInIcons.remove(urls[i]); m_iconsPendingReading.remove(iconRecord); } { MutexLocker locker(m_pendingSyncLock); m_iconsPendingSync.set(iconRecord->iconURL(), iconRecord->snapshot(true)); } } delete record; } } else { urlsToNotify.append(urls[i]); } } } LOG(IconDatabase, "Notifying %zu interested page URLs that their icon URL is known due to the import", urlsToNotify.size()); // Now that we don't hold any locks, perform the actual notifications for (unsigned i = 0; i < urlsToNotify.size(); ++i) { LOG(IconDatabase, "Notifying icon info known for pageURL %s", urlsToNotify[i].ascii().data()); m_client->dispatchDidAddIconForPageURL(urlsToNotify[i]); if (shouldStopThreadActivity()) return; pool.cycle(); } // Notify all DocumentLoaders that were waiting for an icon load decision on the main thread callOnMainThread(notifyPendingLoadDecisionsOnMainThread, this);}void* IconDatabase::syncThreadMainLoop(){ ASSERT_ICON_SYNC_THREAD(); static bool prunedUnretainedIcons = false; m_syncLock.lock(); // It's possible thread termination is requested before the main loop even starts - in that case, just skip straight to cleanup while (!m_threadTerminationRequested) { m_syncLock.unlock();#ifndef NDEBUG double timeStamp = currentTime();#endif LOG(IconDatabase, "(THREAD) Main work loop starting"); // If we should remove all icons, do it now. This is an uninteruptible procedure that we will always do before quitting if it is requested if (m_removeIconsRequested) { removeAllIconsOnThread(); m_removeIconsRequested = false; } // Then, if the thread should be quitting, quit now! if (m_threadTerminationRequested) break; bool didAnyWork = true; while (didAnyWork) { bool didWrite = writeToDatabase(); if (shouldStopThreadActivity()) break; didAnyWork = readFromDatabase(); if (shouldStopThreadActivity()) break; // Prune unretained icons after the first time we sync anything out to the database // This way, pruning won't be the only operation we perform to the database by itself // We also don't want to bother doing this if the thread should be terminating (the user is quitting) // or if private browsing is enabled // We also don't want to prune if the m_databaseCleanupCounter count is non-zero - that means someone // has asked to delay pruning if (didWrite && !m_privateBrowsingEnabled && !prunedUnretainedIcons && !databaseCleanupCounter) {#ifndef NDEBUG double time = currentTime();#endif LOG(IconDatabase, "(THREAD) Starting pruneUnretainedIcons()"); pruneUnretainedIcons(); LOG(IconDatabase, "(THREAD) pruneUnretainedIcons() took %.4f seconds", currentTime() - time); // If pruneUnretainedIcons() returned early due to requested thread termination, its still okay // to mark prunedUnretainedIcons true because we're about to terminate anyway prunedUnretainedIcons = true; } didAnyWork = didAnyWork || didWrite; if (shouldStopThreadActivity()) break; } #ifndef NDEBUG double newstamp = currentTime(); LOG(IconDatabase, "(THREAD) Main work loop ran for %.4f seconds, %s requested to terminate", newstamp - timeStamp, shouldStopThreadActivity() ? "was" : "was not");#endif m_syncLock.lock(); // There is some condition that is asking us to stop what we're doing now and handle a special case // This is either removing all icons, or shutting down the thread to quit the app // We handle those at the top of this main loop so continue to jump back up there if (shouldStopThreadActivity()) continue; m_syncCondition.wait(m_syncLock); } m_syncLock.unlock(); // Thread is terminating at this point return cleanupSyncThread();}bool IconDatabase::readFromDatabase(){ ASSERT_ICON_SYNC_THREAD(); #ifndef NDEBUG double timeStamp = currentTime();#endif bool didAnyWork = false; // We'll make a copy of the sets of things that need to be read. Then we'll verify at the time of updating the record that it still wants to be updated // This way we won't hold the lock for a long period of time Vector<IconRecord*> icons; { MutexLocker locker(m_pendingReadingLock); icons.appendRange(m_iconsPendingReading.begin(), m_iconsPendingReading.end()); } // Keep track of icons we actually read to notify them of the new icon HashSet<String> urlsToNotify; for (unsigned i = 0; i < icons.size(); ++i) { didAnyWork = true; RefPtr<SharedBuffer> imageData = getImageDataForIconURLFromSQLDatabase(icons[i]->iconURL()); // Verify this icon still wants to be read from disk { MutexLocker urlLocker(m_urlAndIconLock); { MutexLocker readLocker(m_pendingReadingLock); if (m_iconsPendingReading.contains(icons[i])) { // Set the new data icons[i]->setImageData(imageData.get()); // Remove this icon from the set that needs to be read m_iconsPendingReading.remove(icons[i]); // We have a set of all Page URLs that retain this icon as well as all PageURLs waiting for an icon // We want to find the intersection of these two sets to notify them // Check the sizes of these two sets to minimize the number of iterations const HashSet<String>* outerHash; const HashSet<String>* innerHash; if (icons[i]->retainingPageURLs().size() > m_pageURLsInterestedInIcons.size()) { outerHash = &m_pageURLsInterestedInIcons; innerHash = &(icons[i]->retainingPageURLs()); } else { innerHash = &m_pageURLsInterestedInIcons; outerHash = &(icons[i]->retainingPageURLs()); } HashSet<String>::const_iterator iter = outerHash->begin(); HashSet<String>::const_iterator end = outerHash->end(); for (; iter != end; ++iter) { if (innerHash->contains(*iter)) { LOG(IconDatabase, "%s is interesting in the icon we just read. Adding it to the list and removing it from the interested set", urlForLogging(*iter).ascii().data()); urlsToNotify.add(*iter); } // If we ever get to the point were we've seen every url interested in this icon, break early if (urlsToNotify.size() == m_pageURLsInterestedInIcons.size()) break; } // We don't need to notify a PageURL twice, so all the ones we're about to notify can be removed from the interested set if (urlsToNotify.size() == m_pageURLsInterestedInIcons.size()) m_pageURLsInterestedInIcons.clear(); else { iter = urlsToNotify.begin(); end = urlsToNotify.end(); for (; iter != end; ++iter) m_pageURLsInterestedInIcons.remove(*iter); } } } } if (shouldStopThreadActivity()) return didAnyWork; // Informal testing shows that draining the autorelease pool every 25 iterations is about as low as we can go // before performance starts to drop off, but we don't want to increase this number because then accumulated memory usage will go up AutodrainedPool pool(25); // Now that we don't hold any locks, perform the actual notifications HashSet<String>::iterator iter = urlsToNotify.begin(); HashSet<String>::iterator end = urlsToNotify.end(); for (unsigned iteration = 0; iter != end; ++iter, ++iteration) { LOG(IconDatabase, "Notifying icon received for pageURL %s", urlForLogging(*iter).ascii().data()); m_client->dispatchDidAddIconForPageURL(*iter); if (shouldStopThreadActivity()) return didAnyWork; pool.cycle(); } LOG(IconDatabase, "Done notifying %i pageURLs who just received their icons", urlsToNotify.size()); urlsToNotify.clear(); if (shouldStopThreadActivity()) return didAnyWork; } LOG(IconDatabase, "Reading from database took %.4f seconds", currentTime() - timeStamp); return didAnyWork;}bool IconDatabase::writeToDatabase(){ ASSERT_ICON_SYNC_THREAD();#ifndef NDEBUG double timeStamp = currentTime();#endif bool didAnyWork = false; // We can copy the current work queue then clear it out - If any new work comes in while we're writing out, // we'll pick it up on the next pass. This greatly simplifies the locking strategy for this method and remains cohesive with changes // asked for by the database on the main thread Vector<IconSnapshot> iconSnapshots; Vector<PageURLSnapshot> pageSnapshots; { MutexLocker locker(m_pendingSyncLock); iconSnapshots.appendRange(m_iconsPendingSync.begin().values(), m_iconsPendingSync.end().values()); m_iconsPendingSync.clear(); pageSnapshots.appendRange(m_pageURLsPendingSync.begin().values(), m_pageURLsPendingSync.end().values()); m_pageURLsPendingSync.clear(); } if (iconSnapshots.size() || pageSnapshots.size()) didAnyWork = true; SQLiteTransaction syncTransaction(m_syncDB); syncTransaction.begin(); for (unsigned i = 0; i < iconSnapshots.size(); ++i) { writeIconSnapshotToSQLDatabase(iconSnapshots[i]); LOG(IconDatabase, "Wrote IconRecord for IconURL %s with timeStamp of %i to the DB", urlForLogging(iconSnapshots[i].iconURL).ascii().data(), iconSnapshots[i].
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -