// Copyright 2019 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "sql/vfs_wrapper_fuchsia.h" #include "base/containers/flat_set.h" #include "base/logging.h" #include "base/no_destructor.h" #include "base/notreached.h" #include "base/synchronization/lock.h" #include "base/thread_annotations.h" #include "sql/vfs_wrapper.h" namespace sql { namespace { // Singleton that stores locks state. class FuchsiaFileLockManager { public: FuchsiaFileLockManager() = default; // Returns lock manager for the current process. static FuchsiaFileLockManager* Instance() { static base::NoDestructor lock_manager; return lock_manager.get(); } // Return true if the file was locked successfully. bool Lock(const std::string& name) { base::AutoLock lock(lock_); if (locked_files_.find(name) != locked_files_.end()) { DLOG(WARNING) << "File " << name << " is being used concurrently by multiple consumers."; return false; } locked_files_.insert(name); return true; } void Unlock(const std::string& name) { base::AutoLock lock(lock_); size_t removed = locked_files_.erase(name); DCHECK_EQ(removed, 1U); } bool IsLocked(const std::string& name) { base::AutoLock lock(lock_); return locked_files_.find(name) != locked_files_.end(); } private: ~FuchsiaFileLockManager() = delete; base::Lock lock_; // Set of all currently locked files. base::flat_set locked_files_ GUARDED_BY(lock_); }; } // namespace int FuchsiaVfsLock(sqlite3_file* sqlite_file, int file_lock) { DCHECK(file_lock == SQLITE_LOCK_SHARED || file_lock == SQLITE_LOCK_RESERVED || file_lock == SQLITE_LOCK_PENDING || file_lock == SQLITE_LOCK_EXCLUSIVE); VfsFile* vfs_file = reinterpret_cast(sqlite_file); if (vfs_file->lock_level == SQLITE_LOCK_NONE) { if (!FuchsiaFileLockManager::Instance()->Lock(vfs_file->file_name)) return SQLITE_BUSY; } vfs_file->lock_level = file_lock; return SQLITE_OK; } int FuchsiaVfsUnlock(sqlite3_file* sqlite_file, int file_lock) { VfsFile* vfs_file = reinterpret_cast(sqlite_file); // No-op if the file is already unlocked or at the requested mode. if (vfs_file->lock_level == file_lock || vfs_file->lock_level == SQLITE_LOCK_NONE) { return SQLITE_OK; } DCHECK(FuchsiaFileLockManager::Instance()->IsLocked(vfs_file->file_name)); if (file_lock == SQLITE_LOCK_NONE) { FuchsiaFileLockManager::Instance()->Unlock(vfs_file->file_name); } else { // Keep the file locked for the shared lock. DCHECK_EQ(file_lock, SQLITE_LOCK_SHARED); } vfs_file->lock_level = file_lock; return SQLITE_OK; } int FuchsiaVfsCheckReservedLock(sqlite3_file* sqlite_file, int* result) { VfsFile* vfs_file = reinterpret_cast(sqlite_file); switch (vfs_file->lock_level) { case SQLITE_LOCK_NONE: case SQLITE_LOCK_SHARED: // Fuchsia only has exclusive locks. If this sqlite3_file has a shared // lock, no other sqlite3_file can get any kind of lock. *result = 0; return SQLITE_OK; case SQLITE_LOCK_RESERVED: case SQLITE_LOCK_PENDING: case SQLITE_LOCK_EXCLUSIVE: *result = 1; return SQLITE_OK; default: NOTREACHED(); return SQLITE_IOERR_CHECKRESERVEDLOCK; } } } // namespace sql