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 #ifndef COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_
6 #define COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_
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 "base/strings/string_split.h"
20 #include "components/leveldb_proto/leveldb_database.h"
21 #include "components/leveldb_proto/proto_database.h"
23 namespace leveldb_proto
{
25 typedef base::StringPairs KeyValueVector
;
26 typedef std::vector
<std::string
> KeyVector
;
28 // When the ProtoDatabaseImpl instance is deleted, in-progress asynchronous
29 // operations will be completed and the corresponding callbacks will be called.
30 // Construction/calls/destruction should all happen on the same thread.
32 class ProtoDatabaseImpl
: public ProtoDatabase
<T
> {
34 // All blocking calls/disk access will happen on the provided |task_runner|.
35 explicit ProtoDatabaseImpl(
36 scoped_refptr
<base::SequencedTaskRunner
> task_runner
);
38 ~ProtoDatabaseImpl() override
;
40 // ProtoDatabase implementation.
41 // TODO(cjhopman): Perhaps Init() shouldn't be exposed to users and not just
42 // part of the constructor
43 void Init(const base::FilePath
& database_dir
,
44 typename ProtoDatabase
<T
>::InitCallback callback
) override
;
46 scoped_ptr
<typename ProtoDatabase
<T
>::KeyEntryVector
> entries_to_save
,
47 scoped_ptr
<KeyVector
> keys_to_remove
,
48 typename ProtoDatabase
<T
>::UpdateCallback callback
) override
;
49 void LoadEntries(typename ProtoDatabase
<T
>::LoadCallback callback
) override
;
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
);
57 base::ThreadChecker thread_checker_
;
59 // Used to run blocking tasks in-order.
60 scoped_refptr
<base::SequencedTaskRunner
> task_runner_
;
62 scoped_ptr
<LevelDB
> db_
;
64 DISALLOW_COPY_AND_ASSIGN(ProtoDatabaseImpl
);
70 void RunInitCallback(typename ProtoDatabase
<T
>::InitCallback callback
,
71 const bool* success
) {
72 callback
.Run(*success
);
76 void RunUpdateCallback(typename ProtoDatabase
<T
>::UpdateCallback callback
,
77 const bool* success
) {
78 callback
.Run(*success
);
82 void RunLoadCallback(typename ProtoDatabase
<T
>::LoadCallback callback
,
83 const bool* success
, scoped_ptr
<std::vector
<T
> > entries
) {
84 callback
.Run(*success
, entries
.Pass());
87 void InitFromTaskRunner(LevelDB
* database
, const base::FilePath
& database_dir
,
91 // TODO(cjhopman): Histogram for database size.
92 *success
= database
->Init(database_dir
);
96 void UpdateEntriesFromTaskRunner(
98 scoped_ptr
<typename ProtoDatabase
<T
>::KeyEntryVector
> entries_to_save
,
99 scoped_ptr
<KeyVector
> keys_to_remove
, bool* 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()));
109 *success
= database
->Save(pairs_to_save
, *keys_to_remove
);
112 template <typename T
>
113 void LoadEntriesFromTaskRunner(LevelDB
* database
, std::vector
<T
>* entries
,
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
) {
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.
128 entries
->push_back(entry
);
134 template <typename T
>
135 ProtoDatabaseImpl
<T
>::ProtoDatabaseImpl(
136 scoped_refptr
<base::SequencedTaskRunner
> task_runner
)
137 : task_runner_(task_runner
) {}
139 template <typename T
>
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.";
147 template <typename T
>
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
);
155 template <typename T
>
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());
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
)));
170 template <typename T
>
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(
179 base::Bind(UpdateEntriesFromTaskRunner
<T
>, base::Unretained(db_
.get()),
180 base::Passed(&entries_to_save
), base::Passed(&keys_to_remove
),
182 base::Bind(RunUpdateCallback
<T
>, callback
, base::Owned(success
)));
185 template <typename T
>
186 void ProtoDatabaseImpl
<T
>::LoadEntries(
187 typename ProtoDatabase
<T
>::LoadCallback callback
) {
188 DCHECK(thread_checker_
.CalledOnValidThread());
189 bool* success
= new bool(false);
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();
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
)));
202 } // namespace leveldb_proto
204 #endif // COMPONENTS_LEVELDB_PROTO_PROTO_DATABASE_IMPL_H_