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

Side by Side Diff: chrome/browser/chromeos/contacts/contact_database.cc

Issue 10832064: contacts: Add contacts::ContactDatabase. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: add callback-running methods Created 8 years, 4 months 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
(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
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/contacts/contact_database.h ('k') | chrome/browser/chromeos/contacts/contact_database_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698