📄 icondatabase.cpp
字号:
return m_threadTerminationRequested || m_removeIconsRequested;}void* IconDatabase::iconDatabaseSyncThreadStart(void* vIconDatabase){ IconDatabase* iconDB = static_cast<IconDatabase*>(vIconDatabase); return iconDB->iconDatabaseSyncThread();}void* IconDatabase::iconDatabaseSyncThread(){ // The call to create this thread might not complete before the thread actually starts, so we might fail this ASSERT_ICON_SYNC_THREAD() because the pointer // to our thread structure hasn't been filled in yet. // To fix this, the main thread acquires this lock before creating us, then releases the lock after creation is complete. A quick lock/unlock cycle here will // prevent us from running before that call completes m_syncLock.lock(); m_syncLock.unlock(); ASSERT_ICON_SYNC_THREAD(); LOG(IconDatabase, "(THREAD) IconDatabase sync thread started");#ifndef NDEBUG double startTime = currentTime();#endif // Need to create the database path if it doesn't already exist makeAllDirectories(m_databaseDirectory); // Existence of a journal file is evidence of a previous crash/force quit and automatically qualifies // us to do an integrity check String journalFilename = m_completeDatabasePath + "-journal"; if (!checkIntegrityOnOpen) { AutodrainedPool pool; checkIntegrityOnOpen = fileExists(journalFilename); } { MutexLocker locker(m_syncLock); if (!m_syncDB.open(m_completeDatabasePath)) { LOG_ERROR("Unable to open icon database at path %s - %s", m_completeDatabasePath.ascii().data(), m_syncDB.lastErrorMsg()); return 0; } } if (shouldStopThreadActivity()) return syncThreadMainLoop(); #ifndef NDEBUG double timeStamp = currentTime(); LOG(IconDatabase, "(THREAD) Open took %.4f seconds", timeStamp - startTime);#endif performOpenInitialization(); if (shouldStopThreadActivity()) return syncThreadMainLoop(); #ifndef NDEBUG double newStamp = currentTime(); LOG(IconDatabase, "(THREAD) performOpenInitialization() took %.4f seconds, now %.4f seconds from thread start", newStamp - timeStamp, newStamp - startTime); timeStamp = newStamp;#endif if (!imported()) { LOG(IconDatabase, "(THREAD) Performing Safari2 import procedure"); SQLiteTransaction importTransaction(m_syncDB); importTransaction.begin(); // Commit the transaction only if the import completes (the import should be atomic) if (m_client->performImport()) { setImported(true); importTransaction.commit(); } else { LOG(IconDatabase, "(THREAD) Safari 2 import was cancelled"); importTransaction.rollback(); } if (shouldStopThreadActivity()) return syncThreadMainLoop(); #ifndef NDEBUG newStamp = currentTime(); LOG(IconDatabase, "(THREAD) performImport() took %.4f seconds, now %.4f seconds from thread start", newStamp - timeStamp, newStamp - startTime); timeStamp = newStamp;#endif } // Uncomment the following line to simulate a long lasting URL import (*HUGE* icon databases, or network home directories) // while (currentTime() - timeStamp < 10); // Read in URL mappings from the database LOG(IconDatabase, "(THREAD) Starting iconURL import"); performURLImport(); if (shouldStopThreadActivity()) return syncThreadMainLoop();#ifndef NDEBUG newStamp = currentTime(); LOG(IconDatabase, "(THREAD) performURLImport() took %.4f seconds. Entering main loop %.4f seconds from thread start", newStamp - timeStamp, newStamp - startTime);#endif LOG(IconDatabase, "(THREAD) Beginning sync"); return syncThreadMainLoop();}static int databaseVersionNumber(SQLiteDatabase& db){ return SQLiteStatement(db, "SELECT value FROM IconDatabaseInfo WHERE key = 'Version';").getColumnInt(0);}static bool isValidDatabase(SQLiteDatabase& db){ // These four tables should always exist in a valid db if (!db.tableExists("IconInfo") || !db.tableExists("IconData") || !db.tableExists("PageURL") || !db.tableExists("IconDatabaseInfo")) return false; if (databaseVersionNumber(db) < currentDatabaseVersion) { LOG(IconDatabase, "DB version is not found or below expected valid version"); return false; } return true;}static void createDatabaseTables(SQLiteDatabase& db){ if (!db.executeCommand("CREATE TABLE PageURL (url TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,iconID INTEGER NOT NULL ON CONFLICT FAIL);")) { LOG_ERROR("Could not create PageURL table in database (%i) - %s", db.lastError(), db.lastErrorMsg()); db.close(); return; } if (!db.executeCommand("CREATE INDEX PageURLIndex ON PageURL (url);")) { LOG_ERROR("Could not create PageURL index in database (%i) - %s", db.lastError(), db.lastErrorMsg()); db.close(); return; } if (!db.executeCommand("CREATE TABLE IconInfo (iconID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE ON CONFLICT REPLACE, url TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT FAIL, stamp INTEGER);")) { LOG_ERROR("Could not create IconInfo table in database (%i) - %s", db.lastError(), db.lastErrorMsg()); db.close(); return; } if (!db.executeCommand("CREATE INDEX IconInfoIndex ON IconInfo (url, iconID);")) { LOG_ERROR("Could not create PageURL index in database (%i) - %s", db.lastError(), db.lastErrorMsg()); db.close(); return; } if (!db.executeCommand("CREATE TABLE IconData (iconID INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE ON CONFLICT REPLACE, data BLOB);")) { LOG_ERROR("Could not create IconData table in database (%i) - %s", db.lastError(), db.lastErrorMsg()); db.close(); return; } if (!db.executeCommand("CREATE INDEX IconDataIndex ON IconData (iconID);")) { LOG_ERROR("Could not create PageURL index in database (%i) - %s", db.lastError(), db.lastErrorMsg()); db.close(); return; } if (!db.executeCommand("CREATE TABLE IconDatabaseInfo (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) { LOG_ERROR("Could not create IconDatabaseInfo table in database (%i) - %s", db.lastError(), db.lastErrorMsg()); db.close(); return; } if (!db.executeCommand(String("INSERT INTO IconDatabaseInfo VALUES ('Version', ") + String::number(currentDatabaseVersion) + ");")) { LOG_ERROR("Could not insert icon database version into IconDatabaseInfo table (%i) - %s", db.lastError(), db.lastErrorMsg()); db.close(); return; }} void IconDatabase::performOpenInitialization(){ ASSERT_ICON_SYNC_THREAD(); if (!isOpen()) return; if (checkIntegrityOnOpen) { checkIntegrityOnOpen = false; if (!checkIntegrity()) { LOG(IconDatabase, "Integrity check was bad - dumping IconDatabase"); m_syncDB.close(); { MutexLocker locker(m_syncLock); // Should've been consumed by SQLite, delete just to make sure we don't see it again in the future; deleteFile(m_completeDatabasePath + "-journal"); deleteFile(m_completeDatabasePath); } // Reopen the write database, creating it from scratch if (!m_syncDB.open(m_completeDatabasePath)) { LOG_ERROR("Unable to open icon database at path %s - %s", m_completeDatabasePath.ascii().data(), m_syncDB.lastErrorMsg()); return; } } } int version = databaseVersionNumber(m_syncDB); if (version > currentDatabaseVersion) { LOG(IconDatabase, "Database version number %i is greater than our current version number %i - closing the database to prevent overwriting newer versions", version, currentDatabaseVersion); m_syncDB.close(); m_threadTerminationRequested = true; return; } if (!isValidDatabase(m_syncDB)) { LOG(IconDatabase, "%s is missing or in an invalid state - reconstructing", m_completeDatabasePath.ascii().data()); m_syncDB.clearAllTables(); createDatabaseTables(m_syncDB); } // Reduce sqlite RAM cache size from default 2000 pages (~1.5kB per page). 3MB of cache for icon database is overkill if (!SQLiteStatement(m_syncDB, "PRAGMA cache_size = 200;").executeCommand()) LOG_ERROR("SQLite database could not set cache_size");}bool IconDatabase::checkIntegrity(){ ASSERT_ICON_SYNC_THREAD(); SQLiteStatement integrity(m_syncDB, "PRAGMA integrity_check;"); if (integrity.prepare() != SQLResultOk) { LOG_ERROR("checkIntegrity failed to execute"); return false; } int resultCode = integrity.step(); if (resultCode == SQLResultOk) return true; if (resultCode != SQLResultRow) return false; int columns = integrity.columnCount(); if (columns != 1) { LOG_ERROR("Received %i columns performing integrity check, should be 1", columns); return false; } String resultText = integrity.getColumnText(0); // A successful, no-error integrity check will be "ok" - all other strings imply failure if (resultText == "ok") return true; LOG_ERROR("Icon database integrity check failed - \n%s", resultText.ascii().data()); return false;}void IconDatabase::performURLImport(){ ASSERT_ICON_SYNC_THREAD(); SQLiteStatement query(m_syncDB, "SELECT PageURL.url, IconInfo.url, IconInfo.stamp FROM PageURL INNER JOIN IconInfo ON PageURL.iconID=IconInfo.iconID;"); if (query.prepare() != SQLResultOk) { LOG_ERROR("Unable to prepare icon url import query"); return; } // 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); int result = query.step(); while (result == SQLResultRow) { String pageURL = query.getColumnText(0); String iconURL = query.getColumnText(1); { MutexLocker locker(m_urlAndIconLock); PageURLRecord* pageRecord = m_pageURLToRecordMap.get(pageURL); // If the pageRecord doesn't exist in this map, then no one has retained this pageURL // If the s_databaseCleanupCounter count is non-zero, then we're not supposed to be pruning the database in any manner, // so go ahead and actually create a pageURLRecord for this url even though it's not retained. // If database cleanup *is* allowed, we don't want to bother pulling in a page url from disk that noone is actually interested // in - we'll prune it later instead! if (!pageRecord && databaseCleanupCounter && !pageURL.isEmpty()) { pageRecord = new PageURLRecord(pageURL); m_pageURLToRecordMap.set(pageURL, pageRecord); } if (pageRecord) { IconRecord* currentIcon = pageRecord->iconRecord(); if (!currentIcon || currentIcon->iconURL() != iconURL) { pageRecord->setIconRecord(getOrCreateIconRecord(iconURL)); currentIcon = pageRecord->iconRecord(); } // Regardless, the time stamp from disk still takes precedence. Until we read this icon from disk, we didn't think we'd seen it before // so we marked the timestamp as "now", but it's really much older currentIcon->setTimestamp(query.getColumnInt(2)); } } // FIXME: Currently the WebKit API supports 1 type of notification that is sent whenever we get an Icon URL for a Page URL. We might want to re-purpose it to work for // getting the actually icon itself also (so each pageurl would get this notification twice) or we might want to add a second type of notification - // one for the URL and one for the Image itself // Note that WebIconDatabase is not neccessarily API so we might be able to make this change { MutexLocker locker(m_pendingReadingLock); if (m_pageURLsPendingImport.contains(pageURL)) { m_client->dispatchDidAddIconForPageURL(pageURL); m_pageURLsPendingImport.remove(pageURL);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -