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

Side by Side Diff: components/dom_distiller/core/dom_distiller_database.cc

Issue 56193004: Re-work the thread restrictions on the DOM distiller database (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 1 month 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
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/dom_distiller/core/dom_distiller_database.h" 5 #include "components/dom_distiller/core/dom_distiller_database.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/file_util.h" 8 #include "base/file_util.h"
9 #include "base/message_loop/message_loop.h" 9 #include "base/message_loop/message_loop.h"
10 #include "base/sequenced_task_runner.h" 10 #include "base/sequenced_task_runner.h"
11 #include "base/strings/string_util.h" 11 #include "base/strings/string_util.h"
12 #include "base/threading/sequenced_worker_pool.h" 12 #include "base/threading/sequenced_worker_pool.h"
13 #include "components/dom_distiller/core/article_entry.h" 13 #include "components/dom_distiller/core/article_entry.h"
14 #include "third_party/leveldatabase/src/include/leveldb/db.h" 14 #include "third_party/leveldatabase/src/include/leveldb/db.h"
15 #include "third_party/leveldatabase/src/include/leveldb/iterator.h" 15 #include "third_party/leveldatabase/src/include/leveldb/iterator.h"
16 #include "third_party/leveldatabase/src/include/leveldb/options.h" 16 #include "third_party/leveldatabase/src/include/leveldb/options.h"
17 #include "third_party/leveldatabase/src/include/leveldb/slice.h" 17 #include "third_party/leveldatabase/src/include/leveldb/slice.h"
18 #include "third_party/leveldatabase/src/include/leveldb/status.h" 18 #include "third_party/leveldatabase/src/include/leveldb/status.h"
19 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" 19 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
20 20
21 using base::MessageLoop; 21 using base::MessageLoop;
22 using base::SequencedTaskRunner; 22 using base::SequencedTaskRunner;
23 23
24 namespace dom_distiller { 24 namespace dom_distiller {
25 25
26 DomDistillerDatabase::LevelDB::LevelDB() {} 26 DomDistillerDatabase::LevelDB::LevelDB() {
27 thread_checker_.DetachFromThread();
28 }
27 29
28 DomDistillerDatabase::LevelDB::~LevelDB() {} 30 DomDistillerDatabase::LevelDB::~LevelDB() {}
Nico 2013/11/03 23:29:52 should the destructor check too? (the class has a
cjhopman 2013/11/04 18:40:59 Done.
29 31
30 bool DomDistillerDatabase::LevelDB::Init(const base::FilePath& database_dir) { 32 bool DomDistillerDatabase::LevelDB::Init(const base::FilePath& database_dir) {
33 thread_checker_.CalledOnValidThread();
Nico 2013/11/03 23:29:52 This does nothing as is. The function just returns
cjhopman 2013/11/04 18:40:59 Ha, that's amusing. Done.
34
31 leveldb::Options options; 35 leveldb::Options options;
32 options.create_if_missing = true; 36 options.create_if_missing = true;
33 options.max_open_files = 0; // Use minimum. 37 options.max_open_files = 0; // Use minimum.
34 38
35 std::string path = database_dir.AsUTF8Unsafe(); 39 std::string path = database_dir.AsUTF8Unsafe();
36 40
37 leveldb::DB* db = NULL; 41 leveldb::DB* db = NULL;
38 leveldb::Status status = leveldb::DB::Open(options, path, &db); 42 leveldb::Status status = leveldb::DB::Open(options, path, &db);
39 if (status.IsCorruption()) { 43 if (status.IsCorruption()) {
40 LOG(WARNING) << "Deleting possibly-corrupt database"; 44 LOG(WARNING) << "Deleting possibly-corrupt database";
41 base::DeleteFile(database_dir, true); 45 base::DeleteFile(database_dir, true);
42 status = leveldb::DB::Open(options, path, &db); 46 status = leveldb::DB::Open(options, path, &db);
43 } 47 }
44 48
45 if (status.ok()) { 49 if (status.ok()) {
46 CHECK(db); 50 CHECK(db);
47 db_.reset(db); 51 db_.reset(db);
48 return true; 52 return true;
49 } 53 }
50 54
51 LOG(WARNING) << "Unable to open " << database_dir.value() << ": " 55 LOG(WARNING) << "Unable to open " << database_dir.value() << ": "
52 << status.ToString(); 56 << status.ToString();
53 return false; 57 return false;
54 } 58 }
55 59
56 bool DomDistillerDatabase::LevelDB::Save(const EntryVector& entries) { 60 bool DomDistillerDatabase::LevelDB::Save(const EntryVector& entries) {
61 thread_checker_.CalledOnValidThread();
62
57 leveldb::WriteBatch updates; 63 leveldb::WriteBatch updates;
58 for (EntryVector::const_iterator it = entries.begin(); it != entries.end(); 64 for (EntryVector::const_iterator it = entries.begin(); it != entries.end();
59 ++it) { 65 ++it) {
60 updates.Put(leveldb::Slice(it->entry_id()), 66 updates.Put(leveldb::Slice(it->entry_id()),
61 leveldb::Slice(it->SerializeAsString())); 67 leveldb::Slice(it->SerializeAsString()));
62 } 68 }
63 69
64 leveldb::WriteOptions options; 70 leveldb::WriteOptions options;
65 options.sync = true; 71 options.sync = true;
66 leveldb::Status status = db_->Write(options, &updates); 72 leveldb::Status status = db_->Write(options, &updates);
67 if (status.ok()) 73 if (status.ok())
68 return true; 74 return true;
69 75
70 LOG(WARNING) << "Failed writing dom_distiller entries: " << status.ToString(); 76 LOG(WARNING) << "Failed writing dom_distiller entries: " << status.ToString();
71 return false; 77 return false;
72 } 78 }
73 79
74 bool DomDistillerDatabase::LevelDB::Load(EntryVector* entries) { 80 bool DomDistillerDatabase::LevelDB::Load(EntryVector* entries) {
81 thread_checker_.CalledOnValidThread();
82
75 leveldb::ReadOptions options; 83 leveldb::ReadOptions options;
76 scoped_ptr<leveldb::Iterator> db_iterator(db_->NewIterator(options)); 84 scoped_ptr<leveldb::Iterator> db_iterator(db_->NewIterator(options));
77 for (db_iterator->SeekToFirst(); db_iterator->Valid(); db_iterator->Next()) { 85 for (db_iterator->SeekToFirst(); db_iterator->Valid(); db_iterator->Next()) {
78 leveldb::Slice value_slice = db_iterator->value(); 86 leveldb::Slice value_slice = db_iterator->value();
79 87
80 ArticleEntry entry; 88 ArticleEntry entry;
81 if (!entry.ParseFromArray(value_slice.data(), value_slice.size())) { 89 if (!entry.ParseFromArray(value_slice.data(), value_slice.size())) {
82 LOG(WARNING) << "Unable to parse dom_distiller entry " 90 LOG(WARNING) << "Unable to parse dom_distiller entry "
83 << db_iterator->key().ToString(); 91 << db_iterator->key().ToString();
84 // TODO(cjhopman): Decide what to do about un-parseable entries. 92 // TODO(cjhopman): Decide what to do about un-parseable entries.
85 } 93 }
86 entries->push_back(entry); 94 entries->push_back(entry);
87 } 95 }
88 return true; 96 return true;
89 } 97 }
90 98
91 DomDistillerDatabase::DomDistillerDatabase(
92 scoped_refptr<base::SequencedTaskRunner> task_runner)
93 : task_runner_(task_runner), weak_ptr_factory_(this) {
94 main_loop_ = MessageLoop::current();
95 }
96
97 void DomDistillerDatabase::Destroy() {
98 DCHECK(IsRunOnMainLoop());
99 weak_ptr_factory_.InvalidateWeakPtrs();
100 task_runner_->PostNonNestableTask(
101 FROM_HERE,
102 base::Bind(&DomDistillerDatabase::DestroyFromTaskRunner,
103 base::Unretained(this)));
104 }
105
106 void DomDistillerDatabase::Init(const base::FilePath& database_dir,
107 InitCallback callback) {
108 InitWithDatabase(scoped_ptr<Database>(new LevelDB()), database_dir, callback);
109 }
110
111 namespace { 99 namespace {
112 100
113 void RunInitCallback(DomDistillerDatabaseInterface::InitCallback callback, 101 void RunInitCallback(DomDistillerDatabaseInterface::InitCallback callback,
114 const bool* success) { 102 const bool* success) {
115 callback.Run(*success); 103 callback.Run(*success);
116 } 104 }
117 105
118 void RunSaveCallback(DomDistillerDatabaseInterface::SaveCallback callback, 106 void RunSaveCallback(DomDistillerDatabaseInterface::SaveCallback callback,
119 const bool* success) { 107 const bool* success) {
120 callback.Run(*success); 108 callback.Run(*success);
121 } 109 }
122 110
123 void RunLoadCallback(DomDistillerDatabaseInterface::LoadCallback callback, 111 void RunLoadCallback(DomDistillerDatabaseInterface::LoadCallback callback,
124 const bool* success, 112 const bool* success,
125 scoped_ptr<EntryVector> entries) { 113 scoped_ptr<EntryVector> entries) {
126 callback.Run(*success, entries.Pass()); 114 callback.Run(*success, entries.Pass());
127 } 115 }
128 116
117 void InitFromTaskRunner(DomDistillerDatabase::Database* database,
118 const base::FilePath& database_dir,
119 bool* success) {
120 DCHECK(success);
121
122 VLOG(1) << "Opening " << database_dir.value();
Nico 2013/11/03 23:29:52 Is this useful? In general, stuff shouldn't log st
cjhopman 2013/11/04 18:40:59 I've gone over the LOG/VLOG and removed/quieted th
123
124 // TODO(cjhopman): Histogram for database size.
125 *success = database->Init(database_dir);
126 }
127
128 void SaveEntriesFromTaskRunner(DomDistillerDatabase::Database* database,
129 scoped_ptr<EntryVector> entries,
130 bool* success) {
131 DCHECK(success);
132 VLOG(1) << "Saving " << entries->size() << " entry(ies) to database ";
133 *success = database->Save(*entries);
134 }
135
136 void LoadEntriesFromTaskRunner(DomDistillerDatabase::Database* database,
137 EntryVector* entries,
138 bool* success) {
139 DCHECK(success);
140 DCHECK(entries);
141
142 entries->clear();
143 *success = database->Load(entries);
144 }
145
129 } // namespace 146 } // namespace
130 147
148 DomDistillerDatabase::DomDistillerDatabase(
149 scoped_refptr<base::SequencedTaskRunner> task_runner)
150 : task_runner_(task_runner) {
151 }
152
153 void DomDistillerDatabase::Init(const base::FilePath& database_dir,
154 InitCallback callback) {
155 thread_checker_.CalledOnValidThread();
156 InitWithDatabase(scoped_ptr<Database>(new LevelDB()), database_dir, callback);
157 }
158
131 void DomDistillerDatabase::InitWithDatabase(scoped_ptr<Database> database, 159 void DomDistillerDatabase::InitWithDatabase(scoped_ptr<Database> database,
132 const base::FilePath& database_dir, 160 const base::FilePath& database_dir,
133 InitCallback callback) { 161 InitCallback callback) {
Nico 2013/11/03 23:29:52 Does this intentionally not use thread_checker_ ?
cjhopman 2013/11/04 18:40:59 Hm. I was using thread_checker_ to enforce that us
134 DCHECK(IsRunOnMainLoop());
135 DCHECK(!db_); 162 DCHECK(!db_);
136 DCHECK(database); 163 DCHECK(database);
137 db_.reset(database.release()); 164 db_.reset(database.release());
138 bool* success = new bool(false); 165 bool* success = new bool(false);
139 task_runner_->PostTaskAndReply( 166 task_runner_->PostTaskAndReply(
140 FROM_HERE, 167 FROM_HERE,
141 base::Bind(&DomDistillerDatabase::InitFromTaskRunner, 168 base::Bind(InitFromTaskRunner,
142 base::Unretained(this), 169 base::Unretained(db_.get()),
143 database_dir, 170 database_dir,
144 success), 171 success),
145 base::Bind(RunInitCallback, callback, base::Owned(success))); 172 base::Bind(RunInitCallback, callback, base::Owned(success)));
146 } 173 }
147 174
148 void DomDistillerDatabase::SaveEntries(scoped_ptr<EntryVector> entries, 175 void DomDistillerDatabase::SaveEntries(scoped_ptr<EntryVector> entries,
149 SaveCallback callback) { 176 SaveCallback callback) {
150 DCHECK(IsRunOnMainLoop()); 177 thread_checker_.CalledOnValidThread();
151 bool* success = new bool(false); 178 bool* success = new bool(false);
152 task_runner_->PostTaskAndReply( 179 task_runner_->PostTaskAndReply(
153 FROM_HERE, 180 FROM_HERE,
154 base::Bind(&DomDistillerDatabase::SaveEntriesFromTaskRunner, 181 base::Bind(SaveEntriesFromTaskRunner,
155 base::Unretained(this), 182 base::Unretained(db_.get()),
156 base::Passed(&entries), 183 base::Passed(&entries),
157 success), 184 success),
158 base::Bind(RunSaveCallback, callback, base::Owned(success))); 185 base::Bind(RunSaveCallback, callback, base::Owned(success)));
159 } 186 }
160 187
161 void DomDistillerDatabase::LoadEntries(LoadCallback callback) { 188 void DomDistillerDatabase::LoadEntries(LoadCallback callback) {
162 DCHECK(IsRunOnMainLoop()); 189 thread_checker_.CalledOnValidThread();
163
164 bool* success = new bool(false); 190 bool* success = new bool(false);
165 191
166 scoped_ptr<EntryVector> entries(new EntryVector()); 192 scoped_ptr<EntryVector> entries(new EntryVector());
167 // Get this pointer before entries is base::Passed() so we can use it below. 193 // Get this pointer before entries is base::Passed() so we can use it below.
168 EntryVector* entries_ptr = entries.get(); 194 EntryVector* entries_ptr = entries.get();
169 195
170 task_runner_->PostTaskAndReply( 196 task_runner_->PostTaskAndReply(
171 FROM_HERE, 197 FROM_HERE,
172 base::Bind(&DomDistillerDatabase::LoadEntriesFromTaskRunner, 198 base::Bind(LoadEntriesFromTaskRunner,
173 base::Unretained(this), 199 base::Unretained(db_.get()),
174 entries_ptr, 200 entries_ptr,
175 success), 201 success),
176 base::Bind(RunLoadCallback, 202 base::Bind(RunLoadCallback,
177 callback, 203 callback,
178 base::Owned(success), 204 base::Owned(success),
179 base::Passed(&entries))); 205 base::Passed(&entries)));
180 } 206 }
181 207
182 DomDistillerDatabase::~DomDistillerDatabase() { DCHECK(IsRunByTaskRunner()); } 208 DomDistillerDatabase::~DomDistillerDatabase() {
183 209 if (!task_runner_->DeleteSoon(FROM_HERE, db_.release())) {
184 bool DomDistillerDatabase::IsRunByTaskRunner() const { 210 LOG(WARNING) << "DOM distiller database will not be deleted.";
185 return task_runner_->RunsTasksOnCurrentThread(); 211 }
186 }
187
188 bool DomDistillerDatabase::IsRunOnMainLoop() const {
189 return MessageLoop::current() == main_loop_;
190 }
191
192 void DomDistillerDatabase::DestroyFromTaskRunner() {
193 DCHECK(IsRunByTaskRunner());
194 delete this;
195 }
196
197 void DomDistillerDatabase::InitFromTaskRunner(
198 const base::FilePath& database_dir,
199 bool* success) {
200 DCHECK(IsRunByTaskRunner());
201 DCHECK(success);
202
203 VLOG(1) << "Opening " << database_dir.value();
204
205 // TODO(cjhopman): Histogram for database size.
206 *success = db_->Init(database_dir);
207 }
208
209 void DomDistillerDatabase::SaveEntriesFromTaskRunner(
210 scoped_ptr<EntryVector> entries,
211 bool* success) {
212 DCHECK(IsRunByTaskRunner());
213 DCHECK(success);
214 VLOG(1) << "Saving " << entries->size() << " entry(ies) to database ";
215 *success = db_->Save(*entries);
216 }
217
218 void DomDistillerDatabase::LoadEntriesFromTaskRunner(EntryVector* entries,
219 bool* success) {
220 DCHECK(IsRunByTaskRunner());
221 DCHECK(success);
222 DCHECK(entries);
223
224 entries->clear();
225 *success = db_->Load(entries);
226 } 212 }
227 213
228 } // namespace dom_distiller 214 } // namespace dom_distiller
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698