123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- // 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/check_op.h"
- #include "base/containers/fixed_flat_set.h"
- #include "base/containers/flat_map.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 {
- struct FileLock {
- int lock_level;
- // Used to track the pointers to different VfsFile instances that hold shared
- // locks on the same underlying file. The pointer is only used as a unique id
- // for the VfsFile instance. The contents are never accessed.
- base::flat_set<VfsFile*> readers = {};
- // Used to track a VfsFile instance that holds a reserved/pending/exclusive
- // lock for writing. The pointer is only used as a unique id for the VfsFile
- // instance. The contents are never accessed.
- VfsFile* writer = nullptr;
- };
- // Singleton that stores and mutates state as described in
- // https://www.sqlite.org/lockingv3.html
- class FuchsiaFileLockManager {
- public:
- FuchsiaFileLockManager() = default;
- // Returns lock manager for the current process.
- static FuchsiaFileLockManager* Instance() {
- static base::NoDestructor<FuchsiaFileLockManager> lock_manager;
- return lock_manager.get();
- }
- int Lock(VfsFile* vfs_file, int requested_lock) {
- DCHECK_GT(requested_lock, SQLITE_LOCK_NONE)
- << "SQLITE_LOCK_NONE can only be set via Unlock";
- base::AutoLock lock(lock_);
- const auto file_lock_state = GetFileLockStateLocked(vfs_file);
- // Allow any lock level since the lock isn't held.
- if (file_lock_state.readers.empty() && file_lock_state.writer == nullptr) {
- if (requested_lock == SQLITE_LOCK_SHARED) {
- locked_files_[vfs_file->file_name] = {.lock_level = requested_lock,
- .readers = {vfs_file}};
- } else {
- locked_files_[vfs_file->file_name] = {.lock_level = requested_lock,
- .writer = vfs_file};
- }
- return SQLITE_OK;
- }
- if (requested_lock == SQLITE_LOCK_SHARED) {
- if (file_lock_state.lock_level >= SQLITE_LOCK_PENDING) {
- DVLOG(1) << "lock for file " << vfs_file->file_name
- << " is held by a writer and cannot be shared.";
- return SQLITE_BUSY;
- }
- locked_files_[vfs_file->file_name].readers.insert(vfs_file);
- return SQLITE_OK;
- }
- if (file_lock_state.writer != nullptr &&
- file_lock_state.writer != vfs_file) {
- DVLOG(1) << "lock for file " << vfs_file->file_name
- << " is already held by another writer.";
- return SQLITE_BUSY;
- }
- if (requested_lock == SQLITE_LOCK_EXCLUSIVE &&
- (file_lock_state.readers.size() > 1 ||
- (file_lock_state.readers.size() == 1 &&
- !file_lock_state.readers.contains(vfs_file)))) {
- DVLOG(1) << "lock for file " << vfs_file->file_name
- << " is held by readers and can't yet be upgraded to exclusive.";
- return SQLITE_BUSY;
- }
- DCHECK(file_lock_state.writer == nullptr ||
- file_lock_state.writer == vfs_file);
- locked_files_[vfs_file->file_name].lock_level = requested_lock;
- locked_files_[vfs_file->file_name].writer = vfs_file;
- locked_files_[vfs_file->file_name].readers.erase(vfs_file);
- DCHECK(locked_files_[vfs_file->file_name].lock_level <
- SQLITE_LOCK_EXCLUSIVE ||
- locked_files_[vfs_file->file_name].readers.empty());
- return SQLITE_OK;
- }
- int Unlock(VfsFile* vfs_file, int requested_lock) {
- base::AutoLock lock(lock_);
- const auto file_lock_state = GetFileLockStateLocked(vfs_file);
- DCHECK_LE(requested_lock, file_lock_state.lock_level)
- << "Attempted to unlock to a higher lock level, unlock can only "
- "decrement.";
- // Shortcut if the caller doesn't currently hold a lock.
- if (!file_lock_state.readers.contains(vfs_file) &&
- file_lock_state.writer != vfs_file) {
- DVLOG(1) << "caller can't unlock because it doesn't currently "
- << "hold a lock for file " << vfs_file->file_name;
- return SQLITE_OK;
- }
- if (requested_lock == SQLITE_LOCK_NONE) {
- locked_files_[vfs_file->file_name].readers.erase(vfs_file);
- } else if (requested_lock == SQLITE_LOCK_SHARED) {
- locked_files_[vfs_file->file_name].readers.insert(vfs_file);
- }
- if (requested_lock < SQLITE_LOCK_RESERVED &&
- file_lock_state.writer == vfs_file) {
- locked_files_[vfs_file->file_name].writer = nullptr;
- }
- // Check that `vfs_file` is correctly tracked given the `requested_lock`.
- DCHECK(requested_lock == SQLITE_LOCK_SHARED ||
- !locked_files_[vfs_file->file_name].readers.contains(vfs_file));
- DCHECK_EQ(requested_lock > SQLITE_LOCK_SHARED,
- locked_files_[vfs_file->file_name].writer == vfs_file);
- // Mark lock level as shared if there are only shared usages.
- if (!file_lock_state.readers.empty() && file_lock_state.writer == nullptr) {
- locked_files_[vfs_file->file_name].lock_level = SQLITE_LOCK_SHARED;
- return SQLITE_OK;
- }
- // Remove lock if there are no usages left.
- if (file_lock_state.readers.empty() && file_lock_state.writer == nullptr) {
- DCHECK_EQ(requested_lock, SQLITE_LOCK_NONE);
- locked_files_.erase(vfs_file->file_name);
- return SQLITE_OK;
- }
- if (file_lock_state.writer != vfs_file) {
- DCHECK_GE(file_lock_state.lock_level, SQLITE_LOCK_RESERVED);
- DCHECK_LE(requested_lock, SQLITE_LOCK_SHARED);
- return SQLITE_OK;
- }
- locked_files_[vfs_file->file_name].lock_level = requested_lock;
- return SQLITE_OK;
- }
- int CheckReservedLock(VfsFile* vfs_file, int* result) {
- base::AutoLock lock(lock_);
- const auto file_lock_state = GetFileLockStateLocked(vfs_file);
- switch (file_lock_state.lock_level) {
- case SQLITE_LOCK_NONE:
- case SQLITE_LOCK_SHARED:
- *result = 0;
- return SQLITE_OK;
- case SQLITE_LOCK_RESERVED:
- case SQLITE_LOCK_PENDING:
- case SQLITE_LOCK_EXCLUSIVE:
- *result = 1;
- return SQLITE_OK;
- default:
- return SQLITE_IOERR_CHECKRESERVEDLOCK;
- }
- }
- private:
- ~FuchsiaFileLockManager() = delete;
- const FileLock& GetFileLockStateLocked(VfsFile* vfs_file)
- EXCLUSIVE_LOCKS_REQUIRED(lock_) {
- static const FileLock kUnlockedFileLock = {.lock_level = SQLITE_LOCK_NONE};
- const auto file_lock_state_iter = locked_files_.find(vfs_file->file_name);
- if (file_lock_state_iter == locked_files_.end()) {
- return kUnlockedFileLock;
- }
- return file_lock_state_iter->second;
- }
- base::Lock lock_;
- // Set of all currently locked files.
- base::flat_map<std::string, FileLock> locked_files_ GUARDED_BY(lock_);
- };
- } // namespace
- int Lock(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);
- auto* vfs_file = reinterpret_cast<VfsFile*>(sqlite_file);
- return FuchsiaFileLockManager::Instance()->Lock(vfs_file, file_lock);
- }
- int Unlock(sqlite3_file* sqlite_file, int file_lock) {
- auto* vfs_file = reinterpret_cast<VfsFile*>(sqlite_file);
- return FuchsiaFileLockManager::Instance()->Unlock(vfs_file, file_lock);
- }
- int CheckReservedLock(sqlite3_file* sqlite_file, int* result) {
- auto* vfs_file = reinterpret_cast<VfsFile*>(sqlite_file);
- return FuchsiaFileLockManager::Instance()->CheckReservedLock(vfs_file,
- result);
- }
- } // namespace sql
|