OLD | NEW |
---|---|
(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 "chrome/browser/chromeos/contacts/contact_database.h" | |
6 | |
7 #include <set> | |
8 | |
9 #include "base/file_util.h" | |
10 #include "base/sequenced_task_runner.h" | |
11 #include "base/threading/sequenced_worker_pool.h" | |
12 #include "chrome/browser/chromeos/contacts/contact.pb.h" | |
13 #include "content/public/browser/browser_thread.h" | |
14 #include "leveldb/db.h" | |
15 #include "leveldb/iterator.h" | |
16 #include "leveldb/options.h" | |
17 #include "leveldb/slice.h" | |
18 #include "leveldb/status.h" | |
19 #include "leveldb/write_batch.h" | |
20 | |
21 using content::BrowserThread; | |
22 | |
23 namespace contacts { | |
24 | |
25 ContactDatabase::ContactDatabase() : weak_ptr_factory_(this) { | |
26 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
27 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool(); | |
28 task_runner_ = pool->GetSequencedTaskRunner(pool->GetSequenceToken()); | |
29 } | |
30 | |
31 void ContactDatabase::DestroyOnUIThread() { | |
32 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
33 weak_ptr_factory_.InvalidateWeakPtrs(); | |
34 task_runner_->PostNonNestableTask( | |
35 FROM_HERE, | |
36 base::Bind(&ContactDatabase::DestroyFromTaskRunner, | |
37 base::Unretained(this))); | |
38 } | |
39 | |
40 void ContactDatabase::Init(const FilePath& database_dir, | |
41 InitCallback callback) { | |
42 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
43 bool* success = new bool(false); | |
44 task_runner_->PostTaskAndReply( | |
45 FROM_HERE, | |
46 base::Bind(&ContactDatabase::InitFromTaskRunner, | |
47 base::Unretained(this), | |
48 database_dir, success), | |
satorux1
2012/07/31 21:19:27
nit: sky would say align parameters vertically (on
Daniel Erat
2012/07/31 21:22:14
Done. I was torn on this -- I sort of like groupi
| |
49 base::Bind(&ContactDatabase::RunInitCallback, | |
50 weak_ptr_factory_.GetWeakPtr(), | |
51 callback, base::Owned(success))); | |
52 } | |
53 | |
54 void ContactDatabase::SaveContacts(scoped_ptr<ContactPointers> contacts, | |
55 bool is_full_update, | |
56 SaveCallback callback) { | |
57 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
58 bool* success = new bool(false); | |
59 task_runner_->PostTaskAndReply( | |
60 FROM_HERE, | |
61 base::Bind(&ContactDatabase::SaveContactsFromTaskRunner, | |
62 base::Unretained(this), | |
63 base::Passed(contacts.Pass()), is_full_update, success), | |
64 base::Bind(&ContactDatabase::RunSaveCallback, | |
65 weak_ptr_factory_.GetWeakPtr(), | |
66 callback, base::Owned(success))); | |
67 } | |
68 | |
69 void ContactDatabase::LoadContacts(LoadCallback callback) { | |
70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
71 bool* success = new bool(false); | |
72 scoped_ptr<ScopedVector<Contact> > contacts(new ScopedVector<Contact>); | |
73 ScopedVector<Contact>* contacts_ptr = contacts.get(); | |
74 task_runner_->PostTaskAndReply( | |
75 FROM_HERE, | |
76 base::Bind(&ContactDatabase::LoadContactsFromTaskRunner, | |
77 base::Unretained(this), | |
78 success, contacts_ptr), | |
79 base::Bind(&ContactDatabase::RunLoadCallback, | |
80 weak_ptr_factory_.GetWeakPtr(), | |
81 callback, | |
82 base::Owned(success), | |
83 base::Passed(contacts.Pass()))); | |
84 } | |
85 | |
86 ContactDatabase::~ContactDatabase() { | |
87 DCHECK(IsRunByTaskRunner()); | |
88 } | |
89 | |
90 bool ContactDatabase::IsRunByTaskRunner() const { | |
91 return BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread(); | |
92 } | |
93 | |
94 void ContactDatabase::DestroyFromTaskRunner() { | |
95 DCHECK(IsRunByTaskRunner()); | |
96 delete this; | |
97 } | |
98 | |
99 void ContactDatabase::RunInitCallback(InitCallback callback, | |
100 const bool* success) { | |
101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
102 callback.Run(*success); | |
103 } | |
104 | |
105 void ContactDatabase::RunSaveCallback(SaveCallback callback, | |
106 const bool* success) { | |
107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
108 callback.Run(*success); | |
109 } | |
110 | |
111 void ContactDatabase::RunLoadCallback( | |
112 LoadCallback callback, | |
113 const bool* success, | |
114 scoped_ptr<ScopedVector<Contact> > contacts) { | |
115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
116 callback.Run(*success, contacts.Pass()); | |
117 } | |
118 | |
119 void ContactDatabase::InitFromTaskRunner(const FilePath& database_dir, | |
120 bool* success) { | |
121 DCHECK(IsRunByTaskRunner()); | |
122 DCHECK(success); | |
123 VLOG(1) << "Opening " << database_dir.value(); | |
124 | |
125 *success = false; | |
126 | |
127 leveldb::Options options; | |
128 options.create_if_missing = true; | |
129 bool delete_and_retry_on_corruption = true; | |
130 | |
131 while (true) { | |
132 leveldb::DB* db = NULL; | |
133 leveldb::Status status = | |
134 leveldb::DB::Open(options, database_dir.value(), &db); | |
135 if (status.ok()) { | |
136 CHECK(db); | |
137 db_.reset(db); | |
138 *success = true; | |
139 return; | |
140 } | |
141 | |
142 LOG(WARNING) << "Unable to open " << database_dir.value() << ": " | |
143 << status.ToString(); | |
144 | |
145 // Delete the existing database and try again (just once, though). | |
146 if (status.IsCorruption() && delete_and_retry_on_corruption) { | |
147 LOG(WARNING) << "Deleting possibly-corrupt database"; | |
148 file_util::Delete(database_dir, true); | |
149 delete_and_retry_on_corruption = false; | |
150 } else { | |
151 break; | |
152 } | |
153 } | |
154 } | |
155 | |
156 void ContactDatabase::SaveContactsFromTaskRunner( | |
157 scoped_ptr<ContactPointers> contacts, | |
158 bool is_full_update, | |
159 bool* success) { | |
160 DCHECK(IsRunByTaskRunner()); | |
161 DCHECK(success); | |
162 VLOG(1) << "Saving " << contacts->size() << " contact(s) to database as " | |
163 << (is_full_update ? "full" : "partial") << " update"; | |
164 | |
165 *success = false; | |
166 | |
167 // If we're doing a full update, find all of the existing keys first so we can | |
168 // delete ones that aren't present in the new set of contacts. | |
169 std::set<std::string> keys_to_delete; | |
170 if (is_full_update) { | |
171 leveldb::ReadOptions options; | |
172 scoped_ptr<leveldb::Iterator> db_iterator(db_->NewIterator(options)); | |
173 db_iterator->SeekToFirst(); | |
174 while (db_iterator->Valid()) { | |
175 keys_to_delete.insert(db_iterator->key().ToString()); | |
176 db_iterator->Next(); | |
177 } | |
178 } | |
179 | |
180 // TODO(derat): Serializing all of the contacts and so we can write them in a | |
181 // single batch may be expensive, memory-wise. Consider writing them in | |
182 // several batches instead. (To avoid using partial writes in the event of a | |
183 // crash, maybe add a dummy "write completed" contact that's removed in the | |
184 // first batch and added in the last.) | |
185 leveldb::WriteBatch updates; | |
186 for (ContactPointers::const_iterator it = contacts->begin(); | |
187 it != contacts->end(); ++it) { | |
188 const contacts::Contact& contact = **it; | |
189 updates.Put(leveldb::Slice(contact.provider_id()), | |
190 leveldb::Slice(contact.SerializeAsString())); | |
191 keys_to_delete.erase(contact.provider_id()); | |
192 } | |
193 | |
194 for (std::set<std::string>::const_iterator it = keys_to_delete.begin(); | |
195 it != keys_to_delete.end(); ++it) { | |
196 updates.Delete(leveldb::Slice(*it)); | |
197 } | |
198 | |
199 leveldb::WriteOptions options; | |
200 options.sync = true; | |
201 leveldb::Status status = db_->Write(options, &updates); | |
202 if (status.ok()) | |
203 *success = true; | |
204 else | |
205 LOG(WARNING) << "Failed writing contacts: " << status.ToString(); | |
206 } | |
207 | |
208 void ContactDatabase::LoadContactsFromTaskRunner( | |
209 bool* success, | |
210 ScopedVector<Contact>* contacts_out) { | |
211 DCHECK(IsRunByTaskRunner()); | |
212 DCHECK(success); | |
213 DCHECK(contacts_out); | |
214 | |
215 *success = false; | |
216 contacts_out->clear(); | |
217 | |
218 leveldb::ReadOptions options; | |
219 scoped_ptr<leveldb::Iterator> db_iterator(db_->NewIterator(options)); | |
220 db_iterator->SeekToFirst(); | |
221 while (db_iterator->Valid()) { | |
222 scoped_ptr<Contact> contact(new Contact); | |
223 leveldb::Slice value_slice = db_iterator->value(); | |
224 if (!contact->ParseFromArray(value_slice.data(), value_slice.size())) { | |
225 LOG(WARNING) << "Unable to parse contact " | |
226 << db_iterator->key().ToString(); | |
227 return; | |
228 } | |
229 contacts_out->push_back(contact.release()); | |
230 db_iterator->Next(); | |
231 } | |
232 | |
233 *success = true; | |
234 } | |
235 | |
236 } // namespace contacts | |
OLD | NEW |