AbstractDatabase::AbstractDatabase(ScriptExecutionContext* context, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize) : m_scriptExecutionContext(context) , m_name(name.crossThreadString()) , m_expectedVersion(expectedVersion.crossThreadString()) , m_displayName(displayName.crossThreadString()) , m_estimatedSize(estimatedSize) , m_guid(0) , m_opened(false) , m_new(false) { ASSERT(context->isContextThread()); m_contextThreadSecurityOrigin = m_scriptExecutionContext->securityOrigin(); m_databaseAuthorizer = DatabaseAuthorizer::create(infoTableName); if (m_name.isNull()) m_name = ""; { MutexLocker locker(guidMutex()); m_guid = guidForOriginAndName(securityOrigin()->toString(), name); HashSet<AbstractDatabase*>* hashSet = guidToDatabaseMap().get(m_guid); if (!hashSet) { hashSet = new HashSet<AbstractDatabase*>; guidToDatabaseMap().set(m_guid, hashSet); } hashSet->add(this); } m_filename = DatabaseTracker::tracker().fullPathForDatabase(securityOrigin(), m_name); DatabaseTracker::tracker().addOpenDatabase(this); }
static GuidDatabaseMap& guidToDatabaseMap() { // Ensure the the mutex is locked. ASSERT(!guidMutex().tryLock()); DEFINE_STATIC_LOCAL(GuidDatabaseMap, map, ()); return map; }
void DatabaseBackendBase::setCachedVersion(const String& actualVersion) { // Update the in memory database version map. std::lock_guard<std::mutex> locker(guidMutex()); updateGuidVersionMap(m_guid, actualVersion); }
DatabaseBackendBase::DatabaseBackendBase(PassRefPtr<DatabaseBackendContext> databaseContext, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, DatabaseType databaseType) : m_databaseContext(databaseContext) , m_name(name.isolatedCopy()) , m_expectedVersion(expectedVersion.isolatedCopy()) , m_displayName(displayName.isolatedCopy()) , m_estimatedSize(estimatedSize) , m_opened(false) , m_new(false) , m_isSyncDatabase(databaseType == DatabaseType::Sync) { m_contextThreadSecurityOrigin = m_databaseContext->securityOrigin()->isolatedCopy(); m_databaseAuthorizer = DatabaseAuthorizer::create(infoTableName); if (m_name.isNull()) m_name = emptyString(); { std::lock_guard<std::mutex> locker(guidMutex()); m_guid = guidForOriginAndName(securityOrigin()->toString(), name); std::unique_ptr<HashSet<DatabaseBackendBase*>>& hashSet = guidToDatabaseMap().add(m_guid, nullptr).iterator->value; if (!hashSet) hashSet = std::make_unique<HashSet<DatabaseBackendBase*>>(); hashSet->add(this); } m_filename = DatabaseManager::manager().fullPathForDatabase(securityOrigin(), m_name); }
DatabaseBackendBase::DatabaseBackendBase(PassRefPtr<DatabaseBackendContext> databaseContext, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize, DatabaseType databaseType) : m_databaseContext(databaseContext) , m_name(name.isolatedCopy()) , m_expectedVersion(expectedVersion.isolatedCopy()) , m_displayName(displayName.isolatedCopy()) , m_estimatedSize(estimatedSize) , m_guid(0) , m_opened(false) , m_new(false) , m_isSyncDatabase(databaseType == DatabaseType::Sync) { m_contextThreadSecurityOrigin = m_databaseContext->securityOrigin()->isolatedCopy(); m_databaseAuthorizer = DatabaseAuthorizer::create(infoTableName); if (m_name.isNull()) m_name = ""; { MutexLocker locker(guidMutex()); m_guid = guidForOriginAndName(securityOrigin()->toString(), name); HashSet<DatabaseBackendBase*>* hashSet = guidToDatabaseMap().get(m_guid); if (!hashSet) { hashSet = new HashSet<DatabaseBackendBase*>; guidToDatabaseMap().set(m_guid, hashSet); } hashSet->add(this); } m_filename = DatabaseManager::manager().fullPathForDatabase(securityOrigin(), m_name); }
static GuidVersionMap& guidToVersionMap() { // Ensure the the mutex is locked. ASSERT(guidMutex().locked()); DEFINE_STATIC_LOCAL_NOASSERT(GuidVersionMap, map, ()); return map; }
// NOTE: Caller must lock guidMutex(). static inline void updateGuidVersionMap(int guid, String newVersion) { // Ensure the the mutex is locked. ASSERT(!guidMutex().tryLock()); // Note: It is not safe to put an empty string into the guidToVersionMap() map. // That's because the map is cross-thread, but empty strings are per-thread. // The copy() function makes a version of the string you can use on the current // thread, but we need a string we can keep in a cross-thread data structure. // FIXME: This is a quite-awkward restriction to have to program with. // Map null string to empty string (see comment above). guidToVersionMap().set(guid, newVersion.isEmpty() ? String() : newVersion.threadsafeCopy()); }
static int guidForOriginAndName(const String& origin, const String& name) { // Ensure the the mutex is locked. ASSERT(!guidMutex().tryLock()); String stringID = origin + "/" + name; typedef HashMap<String, int> IDGuidMap; DEFINE_STATIC_LOCAL(IDGuidMap, stringIdentifierToGUIDMap, ()); int guid = stringIdentifierToGUIDMap.get(stringID); if (!guid) { static int currentNewGUID = 1; guid = currentNewGUID++; stringIdentifierToGUIDMap.set(stringID, guid); } return guid; }
void AbstractDatabase::closeDatabase() { if (!m_opened) return; m_sqliteDatabase.close(); m_opened = false; { MutexLocker locker(guidMutex()); HashSet<AbstractDatabase*>* hashSet = guidToDatabaseMap().get(m_guid); ASSERT(hashSet); ASSERT(hashSet->contains(this)); hashSet->remove(this); if (hashSet->isEmpty()) { guidToDatabaseMap().remove(m_guid); delete hashSet; guidToVersionMap().remove(m_guid); } } }
void DatabaseBackendBase::closeDatabase() { if (!m_opened) return; m_sqliteDatabase.close(); m_opened = false; // See comment at the top this file regarding calling removeOpenDatabase(). DatabaseTracker::tracker().removeOpenDatabase(this); { std::lock_guard<std::mutex> locker(guidMutex()); auto it = guidToDatabaseMap().find(m_guid); ASSERT(it != guidToDatabaseMap().end()); ASSERT(it->value); ASSERT(it->value->contains(this)); it->value->remove(this); if (it->value->isEmpty()) { guidToDatabaseMap().remove(it); guidToVersionMap().remove(m_guid); } } }
void DatabaseBackendBase::closeDatabase() { if (!m_opened) return; m_sqliteDatabase.close(); m_opened = false; // See comment at the top this file regarding calling removeOpenDatabase(). DatabaseTracker::tracker().removeOpenDatabase(this); { MutexLocker locker(guidMutex()); HashSet<DatabaseBackendBase*>* hashSet = guidToDatabaseMap().get(m_guid); ASSERT(hashSet); ASSERT(hashSet->contains(this)); hashSet->remove(this); if (hashSet->isEmpty()) { guidToDatabaseMap().remove(m_guid); delete hashSet; guidToVersionMap().remove(m_guid); } } }
Database::Database(DatabaseContext* databaseContext, const String& name, const String& expectedVersion, const String& displayName, unsigned long estimatedSize) : m_databaseContext(databaseContext) , m_name(name.isolatedCopy()) , m_expectedVersion(expectedVersion.isolatedCopy()) , m_displayName(displayName.isolatedCopy()) , m_estimatedSize(estimatedSize) , m_guid(0) , m_opened(false) , m_new(false) , m_transactionInProgress(false) , m_isTransactionQueueEnabled(true) { m_contextThreadSecurityOrigin = m_databaseContext->securityOrigin()->isolatedCopy(); m_databaseAuthorizer = DatabaseAuthorizer::create(infoTableName); if (m_name.isNull()) m_name = ""; { SafePointAwareMutexLocker locker(guidMutex()); m_guid = guidForOriginAndName(securityOrigin()->toString(), name); HashSet<Database*>* hashSet = guidToDatabaseMap().get(m_guid); if (!hashSet) { hashSet = new HashSet<Database*>; guidToDatabaseMap().set(m_guid, hashSet); } hashSet->add(this); } m_filename = DatabaseManager::manager().fullPathForDatabase(securityOrigin(), m_name); m_databaseThreadSecurityOrigin = m_contextThreadSecurityOrigin->isolatedCopy(); ASSERT(m_databaseContext->databaseThread()); ASSERT(m_databaseContext->isContextThread()); }
String AbstractDatabase::getCachedVersion() const { MutexLocker locker(guidMutex()); return guidToVersionMap().get(m_guid).threadsafeCopy(); }
String DatabaseBackendBase::getCachedVersion() const { MutexLocker locker(guidMutex()); return guidToVersionMap().get(m_guid).isolatedCopy(); }
bool DatabaseBackendBase::performOpenAndVerify(bool shouldSetVersionInNewDatabase, DatabaseError& error, String& errorMessage) { DoneCreatingDatabaseOnExitCaller onExitCaller(this); ASSERT(errorMessage.isEmpty()); ASSERT(error == DatabaseError::None); // Better not have any errors already. error = DatabaseError::InvalidDatabaseState; // Presumed failure. We'll clear it if we succeed below. const int maxSqliteBusyWaitTime = 30000; if (!m_sqliteDatabase.open(m_filename, true)) { reportOpenDatabaseResult(1, INVALID_STATE_ERR, m_sqliteDatabase.lastError()); errorMessage = formatErrorMessage("unable to open database", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); return false; } if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) LOG_ERROR("Unable to turn on incremental auto-vacuum (%d %s)", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); String currentVersion; { MutexLocker locker(guidMutex()); GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid); if (entry != guidToVersionMap().end()) { // Map null string to empty string (see updateGuidVersionMap()). currentVersion = entry->value.isNull() ? emptyString() : entry->value.isolatedCopy(); LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data()); // Note: In multi-process browsers the cached value may be inaccurate, but // we cannot read the actual version from the database without potentially // inducing a form of deadlock, a busytimeout error when trying to // access the database. So we'll use the cached value if we're unable to read // the value from the database file without waiting. // FIXME: Add an async openDatabase method to the DatabaseAPI. const int noSqliteBusyWaitTime = 0; m_sqliteDatabase.setBusyTimeout(noSqliteBusyWaitTime); String versionFromDatabase; if (getVersionFromDatabase(versionFromDatabase, false)) { currentVersion = versionFromDatabase; updateGuidVersionMap(m_guid, currentVersion); } m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); } else { LOG(StorageAPI, "No cached version for guid %i", m_guid); SQLiteTransaction transaction(m_sqliteDatabase); transaction.begin(); if (!transaction.inProgress()) { reportOpenDatabaseResult(2, INVALID_STATE_ERR, m_sqliteDatabase.lastError()); errorMessage = formatErrorMessage("unable to open database, failed to start transaction", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); m_sqliteDatabase.close(); return false; } String tableName(infoTableName); if (!m_sqliteDatabase.tableExists(tableName)) { m_new = true; if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + tableName + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) { reportOpenDatabaseResult(3, INVALID_STATE_ERR, m_sqliteDatabase.lastError()); errorMessage = formatErrorMessage("unable to open database, failed to create 'info' table", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); transaction.rollback(); m_sqliteDatabase.close(); return false; } } else if (!getVersionFromDatabase(currentVersion, false)) { reportOpenDatabaseResult(4, INVALID_STATE_ERR, m_sqliteDatabase.lastError()); errorMessage = formatErrorMessage("unable to open database, failed to read current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); transaction.rollback(); m_sqliteDatabase.close(); return false; } if (currentVersion.length()) { LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data()); } else if (!m_new || shouldSetVersionInNewDatabase) { LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); if (!setVersionInDatabase(m_expectedVersion, false)) { reportOpenDatabaseResult(5, INVALID_STATE_ERR, m_sqliteDatabase.lastError()); errorMessage = formatErrorMessage("unable to open database, failed to write current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); transaction.rollback(); m_sqliteDatabase.close(); return false; } currentVersion = m_expectedVersion; } updateGuidVersionMap(m_guid, currentVersion); transaction.commit(); } } if (currentVersion.isNull()) { LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data()); currentVersion = ""; } // If the expected version isn't the empty string, ensure that the current database version we have matches that version. Otherwise, set an exception. // If the expected version is the empty string, then we always return with whatever version of the database we have. if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length() && m_expectedVersion != currentVersion) { reportOpenDatabaseResult(6, INVALID_STATE_ERR, 0); errorMessage = "unable to open database, version mismatch, '" + m_expectedVersion + "' does not match the currentVersion of '" + currentVersion + "'"; m_sqliteDatabase.close(); return false; } ASSERT(m_databaseAuthorizer); m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer); // See comment at the top this file regarding calling addOpenDatabase(). DatabaseTracker::tracker().addOpenDatabase(this); m_opened = true; // Declare success: error = DatabaseError::None; // Clear the presumed error from above. onExitCaller.setOpenSucceeded(); if (m_new && !shouldSetVersionInNewDatabase) m_expectedVersion = ""; // The caller provided a creationCallback which will set the expected version. reportOpenDatabaseResult(0, -1, 0); // OK return true; }
String Database::getCachedVersion() const { SafePointAwareMutexLocker locker(guidMutex()); return guidToVersionMap().get(m_guid).isolatedCopy(); }
bool AbstractDatabase::performOpenAndVerify(bool shouldSetVersionInNewDatabase, ExceptionCode& ec) { const int maxSqliteBusyWaitTime = 30000; if (!m_sqliteDatabase.open(m_filename, true)) { LOG_ERROR("Unable to open database at path %s", m_filename.ascii().data()); ec = INVALID_STATE_ERR; return false; } if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) LOG_ERROR("Unable to turn on incremental auto-vacuum for database %s", m_filename.ascii().data()); m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); String currentVersion; { MutexLocker locker(guidMutex()); GuidVersionMap::iterator entry = guidToVersionMap().find(m_guid); if (entry != guidToVersionMap().end()) { // Map null string to empty string (see updateGuidVersionMap()). currentVersion = entry->second.isNull() ? String("") : entry->second.threadsafeCopy(); LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data()); #if PLATFORM(CHROMIUM) // Note: In multi-process browsers the cached value may be inaccurate, but // we cannot read the actual version from the database without potentially // inducing a form of deadlock, a busytimeout error when trying to // access the database. So we'll use the cached value if we're unable to read // the value from the database file without waiting. // FIXME: Add an async openDatabase method to the DatabaseAPI. const int noSqliteBusyWaitTime = 0; m_sqliteDatabase.setBusyTimeout(noSqliteBusyWaitTime); String versionFromDatabase; if (getVersionFromDatabase(versionFromDatabase, false)) { currentVersion = versionFromDatabase; updateGuidVersionMap(m_guid, currentVersion); } m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); #endif } else { LOG(StorageAPI, "No cached version for guid %i", m_guid); SQLiteTransaction transaction(m_sqliteDatabase); transaction.begin(); if (!transaction.inProgress()) { LOG_ERROR("Unable to begin transaction while opening %s", databaseDebugName().ascii().data()); ec = INVALID_STATE_ERR; m_sqliteDatabase.close(); return false; } String tableName(infoTableName); if (!m_sqliteDatabase.tableExists(tableName)) { m_new = true; if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + tableName + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) { LOG_ERROR("Unable to create table %s in database %s", infoTableName, databaseDebugName().ascii().data()); ec = INVALID_STATE_ERR; transaction.rollback(); m_sqliteDatabase.close(); return false; } } else if (!getVersionFromDatabase(currentVersion, false)) { LOG_ERROR("Failed to get current version from database %s", databaseDebugName().ascii().data()); ec = INVALID_STATE_ERR; transaction.rollback(); m_sqliteDatabase.close(); return false; } if (currentVersion.length()) { LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data()); } else if (!m_new || shouldSetVersionInNewDatabase) { LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); if (!setVersionInDatabase(m_expectedVersion, false)) { LOG_ERROR("Failed to set version %s in database %s", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); ec = INVALID_STATE_ERR; transaction.rollback(); m_sqliteDatabase.close(); return false; } currentVersion = m_expectedVersion; } updateGuidVersionMap(m_guid, currentVersion); transaction.commit(); } } if (currentVersion.isNull()) { LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data()); currentVersion = ""; } // If the expected version isn't the empty string, ensure that the current database version we have matches that version. Otherwise, set an exception. // If the expected version is the empty string, then we always return with whatever version of the database we have. if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length() && m_expectedVersion != currentVersion) { LOG(StorageAPI, "page expects version %s from database %s, which actually has version name %s - openDatabase() call will fail", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data(), currentVersion.ascii().data()); ec = INVALID_STATE_ERR; m_sqliteDatabase.close(); return false; } ASSERT(m_databaseAuthorizer); m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer); m_opened = true; if (m_new && !shouldSetVersionInNewDatabase) m_expectedVersion = ""; // The caller provided a creationCallback which will set the expected version. return true; }
bool DatabaseBackendBase::performOpenAndVerify(bool shouldSetVersionInNewDatabase, DatabaseError& error, String& errorMessage) { DoneCreatingDatabaseOnExitCaller onExitCaller(this); ASSERT(errorMessage.isEmpty()); ASSERT(error == DatabaseError::None); // Better not have any errors already. error = DatabaseError::InvalidDatabaseState; // Presumed failure. We'll clear it if we succeed below. const int maxSqliteBusyWaitTime = 30000; #if PLATFORM(IOS) { // Make sure we wait till the background removal of the empty database files finished before trying to open any database. MutexLocker locker(DatabaseTracker::openDatabaseMutex()); } #endif SQLiteTransactionInProgressAutoCounter transactionCounter; if (!m_sqliteDatabase.open(m_filename, true)) { errorMessage = formatErrorMessage("unable to open database", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); return false; } if (!m_sqliteDatabase.turnOnIncrementalAutoVacuum()) LOG_ERROR("Unable to turn on incremental auto-vacuum (%d %s)", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); m_sqliteDatabase.setBusyTimeout(maxSqliteBusyWaitTime); String currentVersion; { std::lock_guard<std::mutex> locker(guidMutex()); auto entry = guidToVersionMap().find(m_guid); if (entry != guidToVersionMap().end()) { // Map null string to empty string (see updateGuidVersionMap()). currentVersion = entry->value.isNull() ? emptyString() : entry->value.isolatedCopy(); LOG(StorageAPI, "Current cached version for guid %i is %s", m_guid, currentVersion.ascii().data()); } else { LOG(StorageAPI, "No cached version for guid %i", m_guid); SQLiteTransaction transaction(m_sqliteDatabase); transaction.begin(); if (!transaction.inProgress()) { errorMessage = formatErrorMessage("unable to open database, failed to start transaction", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); m_sqliteDatabase.close(); return false; } String tableName(infoTableName); if (!m_sqliteDatabase.tableExists(tableName)) { m_new = true; if (!m_sqliteDatabase.executeCommand("CREATE TABLE " + tableName + " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE,value TEXT NOT NULL ON CONFLICT FAIL);")) { errorMessage = formatErrorMessage("unable to open database, failed to create 'info' table", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); transaction.rollback(); m_sqliteDatabase.close(); return false; } } else if (!getVersionFromDatabase(currentVersion, false)) { errorMessage = formatErrorMessage("unable to open database, failed to read current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); transaction.rollback(); m_sqliteDatabase.close(); return false; } if (currentVersion.length()) { LOG(StorageAPI, "Retrieved current version %s from database %s", currentVersion.ascii().data(), databaseDebugName().ascii().data()); } else if (!m_new || shouldSetVersionInNewDatabase) { LOG(StorageAPI, "Setting version %s in database %s that was just created", m_expectedVersion.ascii().data(), databaseDebugName().ascii().data()); if (!setVersionInDatabase(m_expectedVersion, false)) { errorMessage = formatErrorMessage("unable to open database, failed to write current version", m_sqliteDatabase.lastError(), m_sqliteDatabase.lastErrorMsg()); transaction.rollback(); m_sqliteDatabase.close(); return false; } currentVersion = m_expectedVersion; } updateGuidVersionMap(m_guid, currentVersion); transaction.commit(); } } if (currentVersion.isNull()) { LOG(StorageAPI, "Database %s does not have its version set", databaseDebugName().ascii().data()); currentVersion = ""; } // If the expected version isn't the empty string, ensure that the current database version we have matches that version. Otherwise, set an exception. // If the expected version is the empty string, then we always return with whatever version of the database we have. if ((!m_new || shouldSetVersionInNewDatabase) && m_expectedVersion.length() && m_expectedVersion != currentVersion) { errorMessage = "unable to open database, version mismatch, '" + m_expectedVersion + "' does not match the currentVersion of '" + currentVersion + "'"; m_sqliteDatabase.close(); return false; } ASSERT(m_databaseAuthorizer); m_sqliteDatabase.setAuthorizer(m_databaseAuthorizer); // See comment at the top this file regarding calling addOpenDatabase(). DatabaseTracker::tracker().addOpenDatabase(this); m_opened = true; // Declare success: error = DatabaseError::None; // Clear the presumed error from above. onExitCaller.setOpenSucceeded(); if (m_new && !shouldSetVersionInNewDatabase) m_expectedVersion = ""; // The caller provided a creationCallback which will set the expected version. return true; }
void AbstractDatabase::setCachedVersion(const String& actualVersion) { // Update the in memory database version map. MutexLocker locker(guidMutex()); updateGuidVersionMap(m_guid, actualVersion); }
String DatabaseBackendBase::getCachedVersion() const { std::lock_guard<std::mutex> locker(guidMutex()); return guidToVersionMap().get(m_guid).isolatedCopy(); }