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

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: Include most of a PersistentPrefStore implementation Created 6 years, 10 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/sequenced_task_runner.h"
12 #include "base/values.h"
13 #include "third_party/leveldatabase/env_chromium.h"
14 #include "third_party/leveldatabase/src/include/leveldb/db.h"
15
16 LevelDBPrefStore::LevelDBPrefStore(
17 const base::FilePath& filename,
18 base::SequencedTaskRunner* sequenced_task_runner)
19 : path_(filename),
20 sequenced_task_runner_(sequenced_task_runner),
21 original_task_runner_(base::MessageLoopProxy::current()),
22 read_only_(false),
23 initialized_(false),
24 read_error_(PREF_READ_ERROR_OTHER) {}
25
26 bool LevelDBPrefStore::GetValue(const std::string& key,
27 const base::Value** result) const {
28 DCHECK(initialized_);
29 const base::Value* tmp = NULL;
30 if (!prefs_.GetValue(key, &tmp)) {
31 return false;
32 }
33
34 if (result)
35 *result = tmp;
36 return true;
37 }
38
39 // Callers of GetMutableValue have to also call ReportValueChanged.
40 bool LevelDBPrefStore::GetMutableValue(const std::string& key,
41 base::Value** result) {
42 DCHECK(initialized_);
43 return prefs_.GetValue(key, result);
44 }
45
46 void LevelDBPrefStore::AddObserver(PrefStore::Observer* observer) {
47 observers_.AddObserver(observer);
48 }
49
50 void LevelDBPrefStore::RemoveObserver(PrefStore::Observer* observer) {
51 observers_.RemoveObserver(observer);
52 }
53
54 bool LevelDBPrefStore::HasObservers() const {
55 return observers_.might_have_observers();
56 }
57
58 bool LevelDBPrefStore::IsInitializationComplete() const { return initialized_; }
59
60 void LevelDBPrefStore::PersistOnFileThread(const std::string& key,
61 const std::string& value) {
62 DCHECK(sequenced_task_runner_->RunsTasksOnCurrentThread());
63
64 leveldb::Status status = db_->Put(leveldb::WriteOptions(), key, value);
65 // DCHECK is fine; the corresponding error is ignored in json_pref_store.
Mattias Nissler (ping if slow) 2014/02/25 10:51:44 nit: JsonPrefStore
dgrogan 2014/03/05 03:06:58 Done.
66 // There's also no API available to surface the error back up to the caller.
Mattias Nissler (ping if slow) 2014/02/25 10:51:44 Can you add a sentence explaining the consequences
dgrogan 2014/03/20 00:25:37 I think this is not ok, thanks for bringing it up.
67 DCHECK(status.ok()) << status.ToString();
68 }
69
70 void LevelDBPrefStore::RemoveOnFileThread(const std::string& key) {
71 DCHECK(sequenced_task_runner_->RunsTasksOnCurrentThread());
72
73 leveldb::Status status = db_->Delete(leveldb::WriteOptions(), key);
74 // DCHECK is fine; the corresponding error is ignored in json_pref_store.
Mattias Nissler (ping if slow) 2014/02/25 10:51:44 nit: JsonPrefStore
dgrogan 2014/03/05 03:06:58 Done.
75 DCHECK(status.ok()) << status.ToString();
76 }
77
78 void LevelDBPrefStore::RemoveFromUIThread(const std::string& key) {
79 if (read_only_)
80 return;
81 sequenced_task_runner_->PostTask(
82 FROM_HERE,
83 base::Bind(
84 &LevelDBPrefStore::RemoveOnFileThread,
85 make_scoped_refptr(this),
86 key));
87 }
88
89 void LevelDBPrefStore::PersistFromUIThread(const std::string& key,
90 base::Value* value) {
91 if (read_only_)
92 return;
93 std::string value_string;
94 JSONStringValueSerializer serializer(&value_string);
95 bool serialized_ok = serializer.Serialize(*value);
96 DCHECK(serialized_ok);
97
98 sequenced_task_runner_->PostTask(
99 FROM_HERE,
100 base::Bind(
101 &LevelDBPrefStore::PersistOnFileThread, this, key, value_string));
102 }
103
104 void LevelDBPrefStore::SetValue(const std::string& key, base::Value* value) {
105 DCHECK(initialized_);
106 DCHECK(value);
107 scoped_ptr<base::Value> new_value(value);
108 base::Value* old_value = NULL;
109 bool found = prefs_.GetValue(key, &old_value);
110 if (!found || !value->Equals(old_value)) {
111 PersistFromUIThread(key, new_value.get());
112 prefs_.SetValue(key, new_value.release());
113 NotifyObservers(key);
114 }
115 }
116
117 void LevelDBPrefStore::SetValueSilently(const std::string& key,
118 base::Value* value) {
119 DCHECK(initialized_);
120 DCHECK(value);
121 scoped_ptr<base::Value> new_value(value);
122 base::Value* old_value = NULL;
123 prefs_.GetValue(key, &old_value);
124 if (!old_value || !value->Equals(old_value)) {
125 PersistFromUIThread(key, new_value.get());
126 prefs_.SetValue(key, new_value.release());
127 }
128 }
129
130 void LevelDBPrefStore::RemoveValue(const std::string& key) {
131 DCHECK(initialized_);
132 if (prefs_.RemoveValue(key)) {
133 RemoveFromUIThread(key);
134 NotifyObservers(key);
135 }
136 }
137
138 bool LevelDBPrefStore::ReadOnly() const { return read_only_; }
139
140 PersistentPrefStore::PrefReadError LevelDBPrefStore::GetReadError() const {
141 return read_error_;
142 }
143
144 PersistentPrefStore::PrefReadError LevelDBPrefStore::ReadPrefs() {
145 DCHECK(!initialized_);
146 DCHECK(!db_);
147 if (path_.empty()) {
148 OnStorageRead(PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
149 return PREF_READ_ERROR_FILE_NOT_SPECIFIED;
150 }
151
152 bool no_dir = !base::PathExists(path_.DirName());
153
154 leveldb::DB* db;
155 leveldb::Options options;
156 options.create_if_missing = true;
157 leveldb::Status status =
158 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db);
159 if (!status.ok()) {
160 PrefReadError error = PREF_READ_ERROR_FILE_OTHER;
161 if (leveldb_env::IsCorruption(status)) {
162 error = PREF_READ_ERROR_LEVELDB_CORRUPTION;
163 // TODO(dgrogan): Call RepairDB.
164 } else if (leveldb_env::IsIOError(status)) {
165 error = PREF_READ_ERROR_LEVELDB_IO_ERROR;
166 }
167 // TODO(dgrogan): Add UMA histogram?
Mattias Nissler (ping if slow) 2014/02/25 10:51:44 I think the PREF_READ_ERROR values are already his
168 OnStorageRead(error, no_dir);
169 return error;
170 }
171 db_.reset(db);
172
173 scoped_ptr<leveldb::Iterator> it(db->NewIterator(leveldb::ReadOptions()));
174 for (it->SeekToFirst(); it->Valid(); it->Next()) {
175 const std::string value_string = it->value().ToString();
176 JSONStringValueSerializer deserializer(value_string);
177 std::string error_message;
178 int error_code;
179 base::Value* json_value =
180 deserializer.Deserialize(&error_code, &error_message);
181 if (json_value)
182 prefs_.SetValue(it->key().ToString(), json_value);
183 else
184 NOTREACHED() << error_message;
185 }
186
187 PersistentPrefStore::PrefReadError error = PREF_READ_ERROR_NONE;
188 if (!it->status().ok()) {
189 // TODO(dgrogan): Add error histogram.
Mattias Nissler (ping if slow) 2014/02/25 10:51:44 see comment above
190 error = PREF_READ_ERROR_FILE_OTHER;
191 }
192 OnStorageRead(error, no_dir);
193 return error;
194 }
195
196 void LevelDBPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
197 DCHECK_EQ(false, initialized_);
198 error_delegate_.reset(error_delegate);
199 if (path_.empty()) {
200 OnStorageRead(PREF_READ_ERROR_FILE_NOT_SPECIFIED, false);
201 return;
202 }
203
204 sequenced_task_runner_->PostTask(FROM_HERE, base::Bind(
205 base::IgnoreResult(&LevelDBPrefStore::ReadPrefs), this));
206 }
207
208 void LevelDBPrefStore::CommitPendingWrite() {
209 // No-op, key-value pairs are committed as they are set.
210 // TODO(dgrogan): If this doesn't perform well because, for example, the
211 // extensions prefs are changed frequently then we'll have to track a set of
212 // keys that have changed and write them to disk every k seconds.
213 }
214
215 void LevelDBPrefStore::ReportValueChanged(const std::string& key) {
216 base::Value* new_value = NULL;
217 DCHECK(prefs_.GetValue(key, &new_value));
218 PersistFromUIThread(key, new_value);
219 NotifyObservers(key);
220 }
221
222 void LevelDBPrefStore::NotifyObservers(const std::string& key) {
223 FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
224 }
225
226 void LevelDBPrefStore::OnStorageRead(PersistentPrefStore::PrefReadError error,
227 bool no_dir) {
228 if (!original_task_runner_->RunsTasksOnCurrentThread()) {
229 // When db is opened async, this function needs to be called on original
230 // thread.
231 original_task_runner_->PostTask(FROM_HERE,
232 base::Bind(&LevelDBPrefStore::OnStorageRead, this, error, no_dir));
233 }
234 read_error_ = error;
235
236 if (no_dir) {
237 FOR_EACH_OBSERVER(
238 PrefStore::Observer, observers_, OnInitializationCompleted(false));
239 return;
240 }
241
242 initialized_ = true;
243
244 switch (error) {
245 case PREF_READ_ERROR_FILE_OTHER:
246 case PREF_READ_ERROR_LEVELDB_IO_ERROR:
247 read_only_ = true;
248 break;
249 case PREF_READ_ERROR_NONE:
250 break;
251 default:
252 NOTREACHED() << "Unknown error: " << error;
253 }
254
255 // TODO(dgrogan): Call pref_filter_->FilterOnLoad
256
257 if (error_delegate_.get() && error != PREF_READ_ERROR_NONE)
258 error_delegate_->OnError(error);
259
260 FOR_EACH_OBSERVER(
261 PrefStore::Observer, observers_, OnInitializationCompleted(true));
262 }
263
264 LevelDBPrefStore::~LevelDBPrefStore() {}
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698