Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(777)

Side by Side Diff: base/prefs/leveldb_pref_store.cc

Issue 169323003: Implementation of leveldb-backed PrefStore (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: now with FileThreadDeserializer Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/prefs/leveldb_pref_store.h"
6
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "base/file_util.h"
10 #include "base/json/json_string_value_serializer.h"
11 #include "base/location.h"
12 #include "base/sequenced_task_runner.h"
13 #include "base/threading/thread_restrictions.h"
14 #include "base/values.h"
15 #include "third_party/leveldatabase/env_chromium.h"
16 #include "third_party/leveldatabase/src/include/leveldb/db.h"
17
18 // This class gets created on the UI thread but used exclusively on the FILE
19 // thread.
20 class LevelDBPrefStore::FileThreadSerializer {
21 public:
22 FileThreadSerializer(scoped_ptr<leveldb::DB> db) : db_(db.Pass()) {
23 }
24 void WriteValue(const std::string& key, const std::string& value) {
25 leveldb::Status status = db_->Put(leveldb::WriteOptions(), key, value);
26 // DCHECK is fine; the corresponding error is ignored in JsonPrefStore.
27 // There's also no API available to surface the error back up to the caller.
28 DCHECK(status.ok()) << status.ToString();
29 }
30 void RemoveValue(const std::string& key) {
31 leveldb::Status status = db_->Delete(leveldb::WriteOptions(), key);
32 // DCHECK is fine; the corresponding error is ignored in JsonPrefStore.
33 DCHECK(status.ok()) << status.ToString();
34 }
35
36 private:
37 scoped_ptr<leveldb::DB> db_;
38 DISALLOW_COPY_AND_ASSIGN(FileThreadSerializer);
39 };
40
41 class FileThreadDeserializer
42 : public base::RefCountedThreadSafe<FileThreadDeserializer> {
Mattias Nissler (ping if slow) 2014/03/05 18:28:15 See https://groups.google.com/a/chromium.org/d/msg
dgrogan 2014/03/06 03:03:40 Done.
43 public:
44 FileThreadDeserializer(LevelDBPrefStore* delegate,
45 base::SequencedTaskRunner* sequenced_task_runner)
46 : no_dir_(false),
47 error_(PersistentPrefStore::PREF_READ_ERROR_NONE),
48 delegate_(delegate),
49 sequenced_task_runner_(sequenced_task_runner),
50 origin_loop_proxy_(base::MessageLoopProxy::current()) {
51 }
52
53 void Start(const base::FilePath& path) {
54 DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
55 sequenced_task_runner_->PostTask(
56 FROM_HERE,
57 base::Bind(&FileThreadDeserializer::ReadFileAndReport, this, path));
58 }
59
60 void ReadFileAndReport(const base::FilePath& path) {
61 DCHECK(sequenced_task_runner_->RunsTasksOnCurrentThread());
62
63 value_map_ = DoReading(path, &error_, &no_dir_, &db_);
64
65 origin_loop_proxy_->PostTask(
66 FROM_HERE,
67 base::Bind(&FileThreadDeserializer::ReportOnOriginThread, this));
68 }
69
70 // Reports deserialization result on the origin thread.
71 void ReportOnOriginThread() {
72 DCHECK(origin_loop_proxy_->BelongsToCurrentThread());
73 delegate_->OnStorageRead(db_, value_map_.Pass(), error_, no_dir_);
74 }
75
76 static scoped_ptr<PrefValueMap> DoReading(
77 const base::FilePath& path,
78 PersistentPrefStore::PrefReadError* error,
79 bool* no_dir,
80 leveldb::DB** db) {
81 base::ThreadRestrictions::AssertIOAllowed();
82
83 *no_dir = !base::PathExists(path.DirName());
84
85 leveldb::Options options;
86 options.create_if_missing = true;
87 leveldb::Status status =
88 leveldb::DB::Open(options, path.AsUTF8Unsafe(), db);
89 DCHECK(status.ok()) << status.ToString();
90 if (!status.ok()) {
91 *error = PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER;
92 if (leveldb_env::IsCorruption(status)) {
93 *error = PersistentPrefStore::PREF_READ_ERROR_LEVELDB_CORRUPTION;
94 // TODO(dgrogan): Call RepairDB.
95 } else if (leveldb_env::IsIOError(status)) {
96 *error = PersistentPrefStore::PREF_READ_ERROR_LEVELDB_IO_ERROR;
97 }
98 // TODO(dgrogan): Add UMA histogram?
99 return scoped_ptr<PrefValueMap>();
100 }
101
102 scoped_ptr<PrefValueMap> value_map(new PrefValueMap);
103 scoped_ptr<leveldb::Iterator> it(
104 (*db)->NewIterator(leveldb::ReadOptions()));
105 for (it->SeekToFirst(); it->Valid(); it->Next()) {
106 const std::string value_string = it->value().ToString();
107 JSONStringValueSerializer deserializer(value_string);
108 std::string error_message;
109 int error_code;
110 base::Value* json_value =
111 deserializer.Deserialize(&error_code, &error_message);
112 if (json_value)
113 value_map->SetValue(it->key().ToString(), json_value);
114 else
115 NOTREACHED() << error_message;
116 }
117
118 if (!it->status().ok()) {
119 // TODO(dgrogan): Add error histogram.
120 *error = PersistentPrefStore::PREF_READ_ERROR_FILE_OTHER;
121 }
122 *error = PersistentPrefStore::PREF_READ_ERROR_NONE;
123 return value_map.Pass();
124 }
125
126 private:
127 friend class base::RefCountedThreadSafe<FileThreadDeserializer>;
128 ~FileThreadDeserializer() {}
129
130 bool no_dir_;
131 PersistentPrefStore::PrefReadError error_;
132 scoped_ptr<PrefValueMap> value_map_;
133 leveldb::DB* db_;
134 const scoped_refptr<LevelDBPrefStore> delegate_;
135 const scoped_refptr<base::SequencedTaskRunner> sequenced_task_runner_;
136 const scoped_refptr<base::MessageLoopProxy> origin_loop_proxy_;
137 };
138
139 LevelDBPrefStore::LevelDBPrefStore(
140 const base::FilePath& filename,
141 base::SequencedTaskRunner* sequenced_task_runner)
142 : path_(filename),
143 sequenced_task_runner_(sequenced_task_runner),
144 original_task_runner_(base::MessageLoopProxy::current()),
145 read_only_(false),
146 initialized_(false),
147 read_error_(PREF_READ_ERROR_OTHER) {
148 }
149
150 LevelDBPrefStore::~LevelDBPrefStore() {
151 sequenced_task_runner_->DeleteSoon(FROM_HERE, helper_.release());
152 }
153
154 bool LevelDBPrefStore::GetValue(const std::string& key,
155 const base::Value** result) const {
156 DCHECK(initialized_);
157 const base::Value* tmp = NULL;
158 if (!prefs_.GetValue(key, &tmp)) {
159 return false;
160 }
161
162 if (result)
163 *result = tmp;
164 return true;
165 }
166
167 // Callers of GetMutableValue have to also call ReportValueChanged.
168 bool LevelDBPrefStore::GetMutableValue(const std::string& key,
169 base::Value** result) {
170 DCHECK(initialized_);
171 return prefs_.GetValue(key, result);
172 }
173
174 void LevelDBPrefStore::AddObserver(PrefStore::Observer* observer) {
175 observers_.AddObserver(observer);
176 }
177
178 void LevelDBPrefStore::RemoveObserver(PrefStore::Observer* observer) {
179 observers_.RemoveObserver(observer);
180 }
181
182 bool LevelDBPrefStore::HasObservers() const {
183 return observers_.might_have_observers();
184 }
185
186 bool LevelDBPrefStore::IsInitializationComplete() const { return initialized_; }
187
188 void LevelDBPrefStore::RemoveFromUIThread(const std::string& key) {
189 if (read_only_)
190 return;
191 DCHECK(helper_);
192 sequenced_task_runner_->PostTask(
193 FROM_HERE,
194 base::Bind(&LevelDBPrefStore::FileThreadSerializer::RemoveValue,
195 base::Unretained(helper_.get()),
196 key));
197 }
198
199 void LevelDBPrefStore::PersistFromUIThread(const std::string& key,
200 base::Value* value) {
201 if (read_only_)
202 return;
203 std::string value_string;
204 JSONStringValueSerializer serializer(&value_string);
205 bool serialized_ok = serializer.Serialize(*value);
206 DCHECK(serialized_ok);
207
208 DCHECK(helper_);
209 sequenced_task_runner_->PostTask(
210 FROM_HERE,
211 base::Bind(&LevelDBPrefStore::FileThreadSerializer::WriteValue,
212 base::Unretained(helper_.get()),
213 key,
214 value_string));
215 }
216
217 void LevelDBPrefStore::SetValue(const std::string& key, base::Value* value) {
218 DCHECK(initialized_);
219 DCHECK(value);
220 scoped_ptr<base::Value> new_value(value);
221 base::Value* old_value = NULL;
222 bool found = prefs_.GetValue(key, &old_value);
223 if (!found || !value->Equals(old_value)) {
224 PersistFromUIThread(key, new_value.get());
225 prefs_.SetValue(key, new_value.release());
226 NotifyObservers(key);
227 }
228 }
229
230 void LevelDBPrefStore::SetValueSilently(const std::string& key,
231 base::Value* value) {
232 DCHECK(initialized_);
233 DCHECK(value);
234 scoped_ptr<base::Value> new_value(value);
235 base::Value* old_value = NULL;
236 prefs_.GetValue(key, &old_value);
237 if (!old_value || !value->Equals(old_value)) {
238 PersistFromUIThread(key, new_value.get());
239 prefs_.SetValue(key, new_value.release());
240 }
241 }
242
243 void LevelDBPrefStore::RemoveValue(const std::string& key) {
244 DCHECK(initialized_);
245 if (prefs_.RemoveValue(key)) {
246 RemoveFromUIThread(key);
247 NotifyObservers(key);
248 }
249 }
250
251 bool LevelDBPrefStore::ReadOnly() const { return read_only_; }
252
253 PersistentPrefStore::PrefReadError LevelDBPrefStore::GetReadError() const {
254 return read_error_;
255 }
256
257 PersistentPrefStore::PrefReadError LevelDBPrefStore::ReadPrefs() {
258 DCHECK(!initialized_);
259 if (path_.empty()) {
260 OnStorageRead(NULL,
261 scoped_ptr<PrefValueMap>(),
262 PREF_READ_ERROR_FILE_NOT_SPECIFIED,
263 false);
264 return PREF_READ_ERROR_FILE_NOT_SPECIFIED;
265 }
266
267 PrefReadError error;
268 bool no_dir;
269 leveldb::DB* db = NULL;
270 scoped_ptr<PrefValueMap> map =
271 FileThreadDeserializer::DoReading(path_, &error, &no_dir, &db);
272 OnStorageRead(db, map.Pass(), error, no_dir);
273 return error;
274 }
275
276 void LevelDBPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
277 DCHECK_EQ(false, initialized_);
278 error_delegate_.reset(error_delegate);
279 if (path_.empty()) {
280 OnStorageRead(NULL,
281 scoped_ptr<PrefValueMap>(),
282 PREF_READ_ERROR_FILE_NOT_SPECIFIED,
283 false);
284 return;
285 }
286 scoped_refptr<FileThreadDeserializer> deserializer(
287 new FileThreadDeserializer(this, sequenced_task_runner_.get()));
288 deserializer->Start(path_);
289 }
290
291 void LevelDBPrefStore::CommitPendingWrite() {
292 // No-op, key-value pairs are committed as they are set.
293 // TODO(dgrogan): If this doesn't perform well because, for example, the
294 // extensions prefs are changed frequently then we'll have to track a set of
295 // keys that have changed and write them to disk every k seconds.
296 }
297
298 void LevelDBPrefStore::ReportValueChanged(const std::string& key) {
299 base::Value* new_value = NULL;
300 DCHECK(prefs_.GetValue(key, &new_value));
301 PersistFromUIThread(key, new_value);
302 NotifyObservers(key);
303 }
304
305 void LevelDBPrefStore::NotifyObservers(const std::string& key) {
306 FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
307 }
308
309 void LevelDBPrefStore::OnStorageRead(leveldb::DB* db_owned,
310 scoped_ptr<PrefValueMap> value,
311 PersistentPrefStore::PrefReadError error,
312 bool no_dir) {
313 scoped_ptr<leveldb::DB> db(db_owned);
314 read_error_ = error;
315
316 if (no_dir) {
317 FOR_EACH_OBSERVER(
318 PrefStore::Observer, observers_, OnInitializationCompleted(false));
319 return;
320 }
321
322 initialized_ = true;
323
324 switch (error) {
325 case PREF_READ_ERROR_FILE_OTHER:
326 case PREF_READ_ERROR_LEVELDB_IO_ERROR:
327 case PREF_READ_ERROR_LEVELDB_CORRUPTION:
328 case PREF_READ_ERROR_FILE_NOT_SPECIFIED:
329 DCHECK(db.get() == NULL);
330 DCHECK(value.get() == NULL);
331 read_only_ = true;
332 break;
333 case PREF_READ_ERROR_NONE:
334 helper_.reset(new FileThreadSerializer(db.Pass()));
335 prefs_.Swap(value.get());
336 break;
337 default:
338 NOTREACHED() << "Unknown error: " << error;
339 }
340
341 // TODO(dgrogan): Call pref_filter_->FilterOnLoad
342
343 if (error_delegate_.get() && error != PREF_READ_ERROR_NONE)
344 error_delegate_->OnError(error);
345
346 FOR_EACH_OBSERVER(
347 PrefStore::Observer, observers_, OnInitializationCompleted(true));
348 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698