1 // Copyright 2014 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.
5 #include "chrome/browser/prefs/leveldb_pref_store.h"
8 #include "base/callback.h"
9 #include "base/files/file_util.h"
10 #include "base/json/json_string_value_serializer.h"
11 #include "base/location.h"
12 #include "base/metrics/sparse_histogram.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/task_runner_util.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "base/time/time.h"
17 #include "base/values.h"
18 #include "third_party/leveldatabase/env_chromium.h"
19 #include "third_party/leveldatabase/src/include/leveldb/db.h"
20 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
28 DESTROY_FAILED
= 1 << 3,
29 REPAIR_FAILED
= 1 << 4,
33 FILE_NOT_SPECIFIED
= 1 << 8,
36 PersistentPrefStore::PrefReadError
IntToPrefReadError(int error
) {
38 if (error
== FILE_NOT_SPECIFIED
)
39 return PersistentPrefStore::PREF_READ_ERROR_FILE_NOT_SPECIFIED
;
41 return PersistentPrefStore::PREF_READ_ERROR_NONE
;
43 return PersistentPrefStore::PREF_READ_ERROR_LEVELDB_IO
;
45 return PersistentPrefStore::PREF_READ_ERROR_LEVELDB_CORRUPTION
;
46 return PersistentPrefStore::PREF_READ_ERROR_LEVELDB_CORRUPTION_READ_ONLY
;
51 struct LevelDBPrefStore::ReadingResults
{
52 ReadingResults() : no_dir(true), error(0) {}
54 scoped_ptr
<leveldb::DB
> db
;
55 scoped_ptr
<PrefValueMap
> value_map
;
59 // An instance of this class is created on the UI thread but is used
60 // exclusively on the FILE thread.
61 class LevelDBPrefStore::FileThreadSerializer
{
63 explicit FileThreadSerializer(scoped_ptr
<leveldb::DB
> db
) : db_(db
.Pass()) {}
65 std::map
<std::string
, std::string
>* keys_to_set
,
66 std::set
<std::string
>* keys_to_delete
) {
67 DCHECK(keys_to_set
->size() > 0 || keys_to_delete
->size() > 0);
68 leveldb::WriteBatch batch
;
69 for (std::map
<std::string
, std::string
>::iterator iter
=
71 iter
!= keys_to_set
->end();
73 batch
.Put(iter
->first
, iter
->second
);
76 for (std::set
<std::string
>::iterator iter
= keys_to_delete
->begin();
77 iter
!= keys_to_delete
->end();
82 leveldb::Status status
= db_
->Write(leveldb::WriteOptions(), &batch
);
84 // DCHECK is fine; the corresponding error is ignored in JsonPrefStore.
85 // There's also no API available to surface the error back up to the caller.
86 // TODO(dgrogan): UMA?
87 DCHECK(status
.ok()) << status
.ToString();
91 scoped_ptr
<leveldb::DB
> db_
;
92 DISALLOW_COPY_AND_ASSIGN(FileThreadSerializer
);
95 bool MoveDirectoryAside(const base::FilePath
& path
) {
96 base::FilePath bad_path
= path
.AppendASCII(".bad");
97 if (!base::Move(path
, bad_path
)) {
98 base::DeleteFile(bad_path
, true);
105 void LevelDBPrefStore::OpenDB(const base::FilePath
& path
,
106 ReadingResults
* reading_results
) {
107 DCHECK_EQ(0, reading_results
->error
);
108 leveldb::Options options
;
109 options
.create_if_missing
= true;
112 leveldb::Status status
=
113 leveldb::DB::Open(options
, path
.AsUTF8Unsafe(), &db
);
115 reading_results
->db
.reset(db
);
116 reading_results
->error
|= OPENED
;
119 if (leveldb_env::IsIOError(status
)) {
120 reading_results
->error
|= IO_ERROR
;
123 if (reading_results
->error
& DESTROYED
)
126 DCHECK(!(reading_results
->error
& REPAIR_FAILED
));
127 if (!(reading_results
->error
& REPAIRED
)) {
128 status
= leveldb::RepairDB(path
.AsUTF8Unsafe(), options
);
130 reading_results
->error
|= REPAIRED
;
133 reading_results
->error
|= REPAIR_FAILED
;
135 if (!MoveDirectoryAside(path
)) {
136 status
= leveldb::DestroyDB(path
.AsUTF8Unsafe(), options
);
138 reading_results
->error
|= DESTROY_FAILED
;
142 reading_results
->error
|= DESTROYED
;
144 DCHECK(reading_results
->error
);
145 DCHECK(!((reading_results
->error
& OPENED
) &&
146 (reading_results
->error
& DESTROY_FAILED
)));
147 DCHECK(reading_results
->error
!= (REPAIR_FAILED
| OPENED
));
151 scoped_ptr
<LevelDBPrefStore::ReadingResults
> LevelDBPrefStore::DoReading(
152 const base::FilePath
& path
) {
153 base::ThreadRestrictions::AssertIOAllowed();
155 scoped_ptr
<ReadingResults
> reading_results(new ReadingResults
);
157 reading_results
->no_dir
= !base::PathExists(path
.DirName());
158 OpenDB(path
, reading_results
.get());
159 if (!reading_results
->db
) {
160 DCHECK(!(reading_results
->error
& OPENED
));
161 return reading_results
.Pass();
164 DCHECK(reading_results
->error
& OPENED
);
165 reading_results
->value_map
.reset(new PrefValueMap
);
166 scoped_ptr
<leveldb::Iterator
> it(
167 reading_results
->db
->NewIterator(leveldb::ReadOptions()));
168 // TODO(dgrogan): Is it really necessary to check it->status() each iteration?
169 for (it
->SeekToFirst(); it
->Valid() && it
->status().ok(); it
->Next()) {
170 const std::string value_string
= it
->value().ToString();
171 JSONStringValueSerializer
deserializer(value_string
);
172 std::string error_message
;
174 base::Value
* json_value
=
175 deserializer
.Deserialize(&error_code
, &error_message
);
177 reading_results
->value_map
->SetValue(it
->key().ToString(), json_value
);
179 DLOG(ERROR
) << "Invalid json for key " << it
->key().ToString()
180 << ": " << error_message
;
181 reading_results
->error
|= DATA_LOST
;
185 if (!it
->status().ok())
186 reading_results
->error
|= ITER_NOT_OK
;
188 return reading_results
.Pass();
191 LevelDBPrefStore::LevelDBPrefStore(
192 const base::FilePath
& filename
,
193 base::SequencedTaskRunner
* sequenced_task_runner
)
195 sequenced_task_runner_(sequenced_task_runner
),
196 original_task_runner_(base::MessageLoopProxy::current()),
199 read_error_(PREF_READ_ERROR_NONE
),
200 weak_ptr_factory_(this) {}
202 LevelDBPrefStore::~LevelDBPrefStore() {
203 CommitPendingWrite();
204 sequenced_task_runner_
->DeleteSoon(FROM_HERE
, serializer_
.release());
207 bool LevelDBPrefStore::GetValue(const std::string
& key
,
208 const base::Value
** result
) const {
209 DCHECK(initialized_
);
210 const base::Value
* tmp
= NULL
;
211 if (!prefs_
.GetValue(key
, &tmp
)) {
220 // Callers of GetMutableValue have to also call ReportValueChanged.
221 bool LevelDBPrefStore::GetMutableValue(const std::string
& key
,
222 base::Value
** result
) {
223 DCHECK(initialized_
);
224 return prefs_
.GetValue(key
, result
);
227 void LevelDBPrefStore::AddObserver(PrefStore::Observer
* observer
) {
228 observers_
.AddObserver(observer
);
231 void LevelDBPrefStore::RemoveObserver(PrefStore::Observer
* observer
) {
232 observers_
.RemoveObserver(observer
);
235 bool LevelDBPrefStore::HasObservers() const {
236 return observers_
.might_have_observers();
239 bool LevelDBPrefStore::IsInitializationComplete() const { return initialized_
; }
241 void LevelDBPrefStore::PersistFromUIThread() {
246 scoped_ptr
<std::set
<std::string
> > keys_to_delete(new std::set
<std::string
>);
247 keys_to_delete
->swap(keys_to_delete_
);
249 scoped_ptr
<std::map
<std::string
, std::string
> > keys_to_set(
250 new std::map
<std::string
, std::string
>);
251 keys_to_set
->swap(keys_to_set_
);
253 sequenced_task_runner_
->PostTask(
255 base::Bind(&LevelDBPrefStore::FileThreadSerializer::WriteToDatabase
,
256 base::Unretained(serializer_
.get()),
257 base::Owned(keys_to_set
.release()),
258 base::Owned(keys_to_delete
.release())));
261 void LevelDBPrefStore::ScheduleWrite() {
262 if (!timer_
.IsRunning()) {
263 timer_
.Start(FROM_HERE
,
264 base::TimeDelta::FromSeconds(10),
266 &LevelDBPrefStore::PersistFromUIThread
);
270 void LevelDBPrefStore::SetValue(const std::string
& key
, base::Value
* value
) {
271 SetValueInternal(key
, value
, true /*notify*/);
274 void LevelDBPrefStore::SetValueSilently(const std::string
& key
,
275 base::Value
* value
) {
276 SetValueInternal(key
, value
, false /*notify*/);
279 static std::string
Serialize(base::Value
* value
) {
280 std::string value_string
;
281 JSONStringValueSerializer
serializer(&value_string
);
282 bool serialized_ok
= serializer
.Serialize(*value
);
283 DCHECK(serialized_ok
);
287 void LevelDBPrefStore::SetValueInternal(const std::string
& key
,
290 DCHECK(initialized_
);
292 scoped_ptr
<base::Value
> new_value(value
);
293 base::Value
* old_value
= NULL
;
294 prefs_
.GetValue(key
, &old_value
);
295 if (!old_value
|| !value
->Equals(old_value
)) {
296 std::string value_string
= Serialize(value
);
297 prefs_
.SetValue(key
, new_value
.release());
298 MarkForInsertion(key
, value_string
);
300 NotifyObservers(key
);
304 void LevelDBPrefStore::RemoveValue(const std::string
& key
) {
305 DCHECK(initialized_
);
306 if (prefs_
.RemoveValue(key
)) {
307 MarkForDeletion(key
);
308 NotifyObservers(key
);
312 bool LevelDBPrefStore::ReadOnly() const { return read_only_
; }
314 PersistentPrefStore::PrefReadError
LevelDBPrefStore::GetReadError() const {
318 PersistentPrefStore::PrefReadError
LevelDBPrefStore::ReadPrefs() {
319 DCHECK(!initialized_
);
320 scoped_ptr
<ReadingResults
> reading_results
;
322 reading_results
.reset(new ReadingResults
);
323 reading_results
->error
= FILE_NOT_SPECIFIED
;
325 reading_results
= DoReading(path_
);
328 PrefReadError error
= IntToPrefReadError(reading_results
->error
);
329 OnStorageRead(reading_results
.Pass());
333 void LevelDBPrefStore::ReadPrefsAsync(ReadErrorDelegate
* error_delegate
) {
334 DCHECK_EQ(false, initialized_
);
335 error_delegate_
.reset(error_delegate
);
337 scoped_ptr
<ReadingResults
> reading_results(new ReadingResults
);
338 reading_results
->error
= FILE_NOT_SPECIFIED
;
339 OnStorageRead(reading_results
.Pass());
342 PostTaskAndReplyWithResult(sequenced_task_runner_
.get(),
344 base::Bind(&LevelDBPrefStore::DoReading
, path_
),
345 base::Bind(&LevelDBPrefStore::OnStorageRead
,
346 weak_ptr_factory_
.GetWeakPtr()));
349 void LevelDBPrefStore::CommitPendingWrite() {
350 if (timer_
.IsRunning()) {
352 PersistFromUIThread();
356 void LevelDBPrefStore::MarkForDeletion(const std::string
& key
) {
359 keys_to_delete_
.insert(key
);
360 // Need to erase in case there's a set operation in the same batch that would
361 // clobber this delete.
362 keys_to_set_
.erase(key
);
366 void LevelDBPrefStore::MarkForInsertion(const std::string
& key
,
367 const std::string
& value
) {
370 keys_to_set_
[key
] = value
;
371 // Need to erase in case there's a delete operation in the same batch that
372 // would clobber this set.
373 keys_to_delete_
.erase(key
);
377 void LevelDBPrefStore::ReportValueChanged(const std::string
& key
) {
378 base::Value
* new_value
= NULL
;
379 bool contains_value
= prefs_
.GetValue(key
, &new_value
);
380 DCHECK(contains_value
);
381 std::string value_string
= Serialize(new_value
);
382 MarkForInsertion(key
, value_string
);
383 NotifyObservers(key
);
386 void LevelDBPrefStore::NotifyObservers(const std::string
& key
) {
387 FOR_EACH_OBSERVER(PrefStore::Observer
, observers_
, OnPrefValueChanged(key
));
390 void LevelDBPrefStore::OnStorageRead(
391 scoped_ptr
<LevelDBPrefStore::ReadingResults
> reading_results
) {
392 UMA_HISTOGRAM_SPARSE_SLOWLY("LevelDBPrefStore.ReadErrors",
393 reading_results
->error
);
394 read_error_
= IntToPrefReadError(reading_results
->error
);
396 if (reading_results
->no_dir
) {
398 PrefStore::Observer
, observers_
, OnInitializationCompleted(false));
404 if (reading_results
->db
) {
405 DCHECK(reading_results
->value_map
);
406 serializer_
.reset(new FileThreadSerializer(reading_results
->db
.Pass()));
407 prefs_
.Swap(reading_results
->value_map
.get());
412 // TODO(dgrogan): Call pref_filter_->FilterOnLoad
414 if (error_delegate_
.get() && read_error_
!= PREF_READ_ERROR_NONE
)
415 error_delegate_
->OnError(read_error_
);
418 PrefStore::Observer
, observers_
, OnInitializationCompleted(true));