1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "sql/vfs_wrapper_fuchsia.h"
7 #include "base/check_op.h"
8 #include "base/containers/fixed_flat_set.h"
9 #include "base/containers/flat_map.h"
10 #include "base/containers/flat_set.h"
11 #include "base/logging.h"
12 #include "base/no_destructor.h"
13 #include "base/notreached.h"
14 #include "base/synchronization/lock.h"
15 #include "base/thread_annotations.h"
16 #include "sql/vfs_wrapper.h"
24 // Used to track the pointers to different VfsFile instances that hold shared
25 // locks on the same underlying file. The pointer is only used as a unique id
26 // for the VfsFile instance. The contents are never accessed.
27 base::flat_set<VfsFile*> readers = {};
28 // Used to track a VfsFile instance that holds a reserved/pending/exclusive
29 // lock for writing. The pointer is only used as a unique id for the VfsFile
30 // instance. The contents are never accessed.
31 VfsFile* writer = nullptr;
34 // Singleton that stores and mutates state as described in
35 // https://www.sqlite.org/lockingv3.html
36 class FuchsiaFileLockManager {
38 FuchsiaFileLockManager() = default;
40 // Returns lock manager for the current process.
41 static FuchsiaFileLockManager* Instance() {
42 static base::NoDestructor<FuchsiaFileLockManager> lock_manager;
43 return lock_manager.get();
46 int Lock(VfsFile* vfs_file, int requested_lock) {
47 DCHECK_GT(requested_lock, SQLITE_LOCK_NONE)
48 << "SQLITE_LOCK_NONE can only be set via Unlock";
49 base::AutoLock lock(lock_);
50 const auto file_lock_state = GetFileLockStateLocked(vfs_file);
52 // Allow any lock level since the lock isn't held.
53 if (file_lock_state.readers.empty() && file_lock_state.writer == nullptr) {
54 if (requested_lock == SQLITE_LOCK_SHARED) {
55 locked_files_[vfs_file->file_name] = {.lock_level = requested_lock,
56 .readers = {vfs_file}};
58 locked_files_[vfs_file->file_name] = {.lock_level = requested_lock,
65 if (requested_lock == SQLITE_LOCK_SHARED) {
66 if (file_lock_state.lock_level >= SQLITE_LOCK_PENDING) {
67 DVLOG(1) << "lock for file " << vfs_file->file_name
68 << " is held by a writer and cannot be shared.";
72 locked_files_[vfs_file->file_name].readers.insert(vfs_file);
76 if (file_lock_state.writer != nullptr &&
77 file_lock_state.writer != vfs_file) {
78 DVLOG(1) << "lock for file " << vfs_file->file_name
79 << " is already held by another writer.";
83 if (requested_lock == SQLITE_LOCK_EXCLUSIVE &&
84 (file_lock_state.readers.size() > 1 ||
85 (file_lock_state.readers.size() == 1 &&
86 !file_lock_state.readers.contains(vfs_file)))) {
87 DVLOG(1) << "lock for file " << vfs_file->file_name
88 << " is held by readers and can't yet be upgraded to exclusive.";
92 DCHECK(file_lock_state.writer == nullptr ||
93 file_lock_state.writer == vfs_file);
94 locked_files_[vfs_file->file_name].lock_level = requested_lock;
95 locked_files_[vfs_file->file_name].writer = vfs_file;
96 locked_files_[vfs_file->file_name].readers.erase(vfs_file);
97 DCHECK(locked_files_[vfs_file->file_name].lock_level <
98 SQLITE_LOCK_EXCLUSIVE ||
99 locked_files_[vfs_file->file_name].readers.empty());
103 int Unlock(VfsFile* vfs_file, int requested_lock) {
104 base::AutoLock lock(lock_);
105 const auto file_lock_state = GetFileLockStateLocked(vfs_file);
107 DCHECK_LE(requested_lock, file_lock_state.lock_level)
108 << "Attempted to unlock to a higher lock level, unlock can only "
111 // Shortcut if the caller doesn't currently hold a lock.
112 if (!file_lock_state.readers.contains(vfs_file) &&
113 file_lock_state.writer != vfs_file) {
114 DVLOG(1) << "caller can't unlock because it doesn't currently "
115 << "hold a lock for file " << vfs_file->file_name;
119 if (requested_lock == SQLITE_LOCK_NONE) {
120 locked_files_[vfs_file->file_name].readers.erase(vfs_file);
121 } else if (requested_lock == SQLITE_LOCK_SHARED) {
122 locked_files_[vfs_file->file_name].readers.insert(vfs_file);
125 if (requested_lock < SQLITE_LOCK_RESERVED &&
126 file_lock_state.writer == vfs_file) {
127 locked_files_[vfs_file->file_name].writer = nullptr;
130 // Check that `vfs_file` is correctly tracked given the `requested_lock`.
131 DCHECK(requested_lock == SQLITE_LOCK_SHARED ||
132 !locked_files_[vfs_file->file_name].readers.contains(vfs_file));
133 DCHECK_EQ(requested_lock > SQLITE_LOCK_SHARED,
134 locked_files_[vfs_file->file_name].writer == vfs_file);
136 // Mark lock level as shared if there are only shared usages.
137 if (!file_lock_state.readers.empty() && file_lock_state.writer == nullptr) {
138 locked_files_[vfs_file->file_name].lock_level = SQLITE_LOCK_SHARED;
142 // Remove lock if there are no usages left.
143 if (file_lock_state.readers.empty() && file_lock_state.writer == nullptr) {
144 DCHECK_EQ(requested_lock, SQLITE_LOCK_NONE);
145 locked_files_.erase(vfs_file->file_name);
149 if (file_lock_state.writer != vfs_file) {
150 DCHECK_GE(file_lock_state.lock_level, SQLITE_LOCK_RESERVED);
151 DCHECK_LE(requested_lock, SQLITE_LOCK_SHARED);
155 locked_files_[vfs_file->file_name].lock_level = requested_lock;
159 int CheckReservedLock(VfsFile* vfs_file, int* result) {
160 base::AutoLock lock(lock_);
161 const auto file_lock_state = GetFileLockStateLocked(vfs_file);
163 switch (file_lock_state.lock_level) {
164 case SQLITE_LOCK_NONE:
165 case SQLITE_LOCK_SHARED:
168 case SQLITE_LOCK_RESERVED:
169 case SQLITE_LOCK_PENDING:
170 case SQLITE_LOCK_EXCLUSIVE:
174 return SQLITE_IOERR_CHECKRESERVEDLOCK;
179 ~FuchsiaFileLockManager() = delete;
181 const FileLock& GetFileLockStateLocked(VfsFile* vfs_file)
182 EXCLUSIVE_LOCKS_REQUIRED(lock_) {
183 static const FileLock kUnlockedFileLock = {.lock_level = SQLITE_LOCK_NONE};
184 const auto file_lock_state_iter = locked_files_.find(vfs_file->file_name);
185 if (file_lock_state_iter == locked_files_.end()) {
186 return kUnlockedFileLock;
189 return file_lock_state_iter->second;
194 // Set of all currently locked files.
195 base::flat_map<std::string, FileLock> locked_files_ GUARDED_BY(lock_);
200 int Lock(sqlite3_file* sqlite_file, int file_lock) {
201 DCHECK(file_lock == SQLITE_LOCK_SHARED || file_lock == SQLITE_LOCK_RESERVED ||
202 file_lock == SQLITE_LOCK_PENDING ||
203 file_lock == SQLITE_LOCK_EXCLUSIVE);
205 auto* vfs_file = reinterpret_cast<VfsFile*>(sqlite_file);
206 return FuchsiaFileLockManager::Instance()->Lock(vfs_file, file_lock);
209 int Unlock(sqlite3_file* sqlite_file, int file_lock) {
210 auto* vfs_file = reinterpret_cast<VfsFile*>(sqlite_file);
211 return FuchsiaFileLockManager::Instance()->Unlock(vfs_file, file_lock);
214 int CheckReservedLock(sqlite3_file* sqlite_file, int* result) {
215 auto* vfs_file = reinterpret_cast<VfsFile*>(sqlite_file);
216 return FuchsiaFileLockManager::Instance()->CheckReservedLock(vfs_file,