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.
4
5 #ifndef COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_
6 #define COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_
7
8 #include <string>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/files/file_path.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/sequenced_task_runner.h"
16 #include "base/strings/string_util.h"
17 #include "base/threading/sequenced_worker_pool.h"
18 #include "base/threading/thread_checker.h"
19 #include "components/leveldb_proto/leveldb_database.h"
20 #include "components/leveldb_proto/proto_database.h"
21
22 namespace leveldb_proto {
23
24 typedef std::vector<std::pair<std::string, std::string> > KeyValueVector;
25 typedef std::vector<std::string> KeyVector;
26
27 // When the ProtoDatabaseImpl instance is deleted, in-progress asynchronous
28 // operations will be completed and the corresponding callbacks will be called.
29 // Construction/calls/destruction should all happen on the same thread.
30 template <typename T>
31 class ProtoDatabaseImpl : public ProtoDatabase<T> {
32 public:
33 // All blocking calls/disk access will happen on the provided |task_runner|.
34 explicit ProtoDatabaseImpl(
35 scoped_refptr<base::SequencedTaskRunner> task_runner);
36
37 virtual ~ProtoDatabaseImpl();
38
39 // ProtoDatabase implementation.
40 // TODO(cjhopman): Perhaps Init() shouldn't be exposed to users and not just
41 // part of the constructor
42 virtual void Init(const base::FilePath& database_dir,
43 typename ProtoDatabase<T>::InitCallback callback) OVERRIDE;
44 virtual void UpdateEntries(
45 scoped_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
46 scoped_ptr<KeyVector> keys_to_remove,
47 typename ProtoDatabase<T>::UpdateCallback callback) OVERRIDE;
48 virtual void LoadEntries(
49 typename ProtoDatabase<T>::LoadCallback callback) OVERRIDE;
50
51 // Allow callers to provide their own Database implementation.
52 void InitWithDatabase(scoped_ptr<LevelDB> database,
53 const base::FilePath& database_dir,
54 typename ProtoDatabase<T>::InitCallback callback);
55
56 private:
57 base::ThreadChecker thread_checker_;
58
59 // Used to run blocking tasks in-order.
60 scoped_refptr<base::SequencedTaskRunner> task_runner_;
61
62 scoped_ptr<LevelDB> db_;
63
64 DISALLOW_COPY_AND_ASSIGN(ProtoDatabaseImpl);
65 };
66
67 namespace {
68
69 template <typename T>
RunInitCallback(typename ProtoDatabase<T>::InitCallback callback,const bool * success)70 void RunInitCallback(typename ProtoDatabase<T>::InitCallback callback,
71 const bool* success) {
72 callback.Run(*success);
73 }
74
75 template <typename T>
RunUpdateCallback(typename ProtoDatabase<T>::UpdateCallback callback,const bool * success)76 void RunUpdateCallback(typename ProtoDatabase<T>::UpdateCallback callback,
77 const bool* success) {
78 callback.Run(*success);
79 }
80
81 template <typename T>
RunLoadCallback(typename ProtoDatabase<T>::LoadCallback callback,const bool * success,scoped_ptr<std::vector<T>> entries)82 void RunLoadCallback(typename ProtoDatabase<T>::LoadCallback callback,
83 const bool* success, scoped_ptr<std::vector<T> > entries) {
84 callback.Run(*success, entries.Pass());
85 }
86
InitFromTaskRunner(LevelDB * database,const base::FilePath & database_dir,bool * success)87 void InitFromTaskRunner(LevelDB* database, const base::FilePath& database_dir,
88 bool* success) {
89 DCHECK(success);
90
91 // TODO(cjhopman): Histogram for database size.
92 *success = database->Init(database_dir);
93 }
94
95 template <typename T>
UpdateEntriesFromTaskRunner(LevelDB * database,scoped_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,scoped_ptr<KeyVector> keys_to_remove,bool * success)96 void UpdateEntriesFromTaskRunner(
97 LevelDB* database,
98 scoped_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
99 scoped_ptr<KeyVector> keys_to_remove, bool* success) {
100 DCHECK(success);
101 // Serialize the values from Proto to string before passing on to database.
102 KeyValueVector pairs_to_save;
103 for (typename ProtoDatabase<T>::KeyEntryVector::iterator it =
104 entries_to_save->begin();
105 it != entries_to_save->end(); ++it) {
106 pairs_to_save.push_back(
107 std::make_pair(it->first, it->second.SerializeAsString()));
108 }
109 *success = database->Save(pairs_to_save, *keys_to_remove);
110 }
111
112 template <typename T>
LoadEntriesFromTaskRunner(LevelDB * database,std::vector<T> * entries,bool * success)113 void LoadEntriesFromTaskRunner(LevelDB* database, std::vector<T>* entries,
114 bool* success) {
115 DCHECK(success);
116 DCHECK(entries);
117
118 entries->clear();
119 std::vector<std::string> loaded_entries;
120 *success = database->Load(&loaded_entries);
121 for (std::vector<std::string>::iterator it = loaded_entries.begin();
122 it != loaded_entries.end(); ++it) {
123 T entry;
124 if (!entry.ParseFromString(*it)) {
125 DLOG(WARNING) << "Unable to parse leveldb_proto entry " << *it;
126 // TODO(cjhopman): Decide what to do about un-parseable entries.
127 }
128 entries->push_back(entry);
129 }
130 }
131
132 } // namespace
133
134 template <typename T>
ProtoDatabaseImpl(scoped_refptr<base::SequencedTaskRunner> task_runner)135 ProtoDatabaseImpl<T>::ProtoDatabaseImpl(
136 scoped_refptr<base::SequencedTaskRunner> task_runner)
137 : task_runner_(task_runner) {}
138
139 template <typename T>
~ProtoDatabaseImpl()140 ProtoDatabaseImpl<T>::~ProtoDatabaseImpl() {
141 DCHECK(thread_checker_.CalledOnValidThread());
142 if (!task_runner_->DeleteSoon(FROM_HERE, db_.release())) {
143 DLOG(WARNING) << "DOM distiller database will not be deleted.";
144 }
145 }
146
147 template <typename T>
Init(const base::FilePath & database_dir,typename ProtoDatabase<T>::InitCallback callback)148 void ProtoDatabaseImpl<T>::Init(
149 const base::FilePath& database_dir,
150 typename ProtoDatabase<T>::InitCallback callback) {
151 DCHECK(thread_checker_.CalledOnValidThread());
152 InitWithDatabase(scoped_ptr<LevelDB>(new LevelDB()), database_dir, callback);
153 }
154
155 template <typename T>
InitWithDatabase(scoped_ptr<LevelDB> database,const base::FilePath & database_dir,typename ProtoDatabase<T>::InitCallback callback)156 void ProtoDatabaseImpl<T>::InitWithDatabase(
157 scoped_ptr<LevelDB> database, const base::FilePath& database_dir,
158 typename ProtoDatabase<T>::InitCallback callback) {
159 DCHECK(thread_checker_.CalledOnValidThread());
160 DCHECK(!db_);
161 DCHECK(database);
162 db_.reset(database.release());
163 bool* success = new bool(false);
164 task_runner_->PostTaskAndReply(
165 FROM_HERE, base::Bind(InitFromTaskRunner, base::Unretained(db_.get()),
166 database_dir, success),
167 base::Bind(RunInitCallback<T>, callback, base::Owned(success)));
168 }
169
170 template <typename T>
UpdateEntries(scoped_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,scoped_ptr<KeyVector> keys_to_remove,typename ProtoDatabase<T>::UpdateCallback callback)171 void ProtoDatabaseImpl<T>::UpdateEntries(
172 scoped_ptr<typename ProtoDatabase<T>::KeyEntryVector> entries_to_save,
173 scoped_ptr<KeyVector> keys_to_remove,
174 typename ProtoDatabase<T>::UpdateCallback callback) {
175 DCHECK(thread_checker_.CalledOnValidThread());
176 bool* success = new bool(false);
177 task_runner_->PostTaskAndReply(
178 FROM_HERE,
179 base::Bind(UpdateEntriesFromTaskRunner<T>, base::Unretained(db_.get()),
180 base::Passed(&entries_to_save), base::Passed(&keys_to_remove),
181 success),
182 base::Bind(RunUpdateCallback<T>, callback, base::Owned(success)));
183 }
184
185 template <typename T>
LoadEntries(typename ProtoDatabase<T>::LoadCallback callback)186 void ProtoDatabaseImpl<T>::LoadEntries(
187 typename ProtoDatabase<T>::LoadCallback callback) {
188 DCHECK(thread_checker_.CalledOnValidThread());
189 bool* success = new bool(false);
190
191 scoped_ptr<std::vector<T> > entries(new std::vector<T>());
192 // Get this pointer before entries is base::Passed() so we can use it below.
193 std::vector<T>* entries_ptr = entries.get();
194
195 task_runner_->PostTaskAndReply(
196 FROM_HERE, base::Bind(LoadEntriesFromTaskRunner<T>,
197 base::Unretained(db_.get()), entries_ptr, success),
198 base::Bind(RunLoadCallback<T>, callback, base::Owned(success),
199 base::Passed(&entries)));
200 }
201
202 } // namespace leveldb_proto
203
204 #endif // COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_
205