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

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

Powered by Google App Engine
This is Rietveld 408576698