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

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: try clearing bitmap pixels to see if it fixes some valgrind errors 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() {
satorux1 2012/07/31 07:26:30 might want to add DCHECK(BrowserThread::Currently
Daniel Erat 2012/07/31 16:44:12 Done.
26 base::SequencedWorkerPool* pool = BrowserThread::GetBlockingPool();
27 task_runner_ = pool->GetSequencedTaskRunner(pool->GetSequenceToken());
28 }
29
30 void ContactDatabase::DestroyOnUIThread() {
31 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
32 task_runner_->PostNonNestableTask(
33 FROM_HERE,
34 base::Bind(&ContactDatabase::DestroyFromTaskRunner,
35 base::Unretained(this)));
36 }
37
38 void ContactDatabase::Init(const FilePath& database_dir,
39 InitCallback callback) {
40 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
41 task_runner_->PostNonNestableTask(
42 FROM_HERE,
43 base::Bind(&ContactDatabase::InitFromTaskRunner,
44 base::Unretained(this),
45 database_dir, callback));
46 }
47
48 void ContactDatabase::SaveContacts(scoped_ptr<ContactPointers> contacts,
49 bool is_full_update,
50 SaveCallback callback) {
51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
52 task_runner_->PostNonNestableTask(
satorux1 2012/07/31 16:19:19 You might want to use PostTaskAndReply instead. Th
Daniel Erat 2012/07/31 16:44:12 Interesting. Setting the reply's parameters seems
satorux1 2012/07/31 17:34:26 No scoped_refptr please. :) Usually, the result is
Daniel Erat 2012/07/31 19:34:42 Nice! Done.
53 FROM_HERE,
54 base::Bind(&ContactDatabase::SaveContactsFromTaskRunner,
55 base::Unretained(this),
56 base::Passed(contacts.Pass()), is_full_update, callback));
57 }
58
59 void ContactDatabase::LoadContacts(LoadCallback callback) {
60 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
61 task_runner_->PostNonNestableTask(
62 FROM_HERE,
63 base::Bind(&ContactDatabase::LoadContactsFromTaskRunner,
64 base::Unretained(this),
65 callback));
66 }
67
68 ContactDatabase::~ContactDatabase() {
69 DCHECK(IsRunByTaskRunner());
70 }
71
72 bool ContactDatabase::IsRunByTaskRunner() const {
73 return BrowserThread::GetBlockingPool()->RunsTasksOnCurrentThread();
74 }
75
76 void ContactDatabase::DestroyFromTaskRunner() {
77 DCHECK(IsRunByTaskRunner());
78 delete this;
79 }
80
81 void ContactDatabase::InitFromTaskRunner(const FilePath& database_dir,
82 InitCallback callback) {
83 DCHECK(IsRunByTaskRunner());
84 bool success = InitInternal(database_dir);
85 BrowserThread::PostTask(
86 BrowserThread::UI, FROM_HERE, base::Bind(callback, success));
87 }
88
89 bool ContactDatabase::InitInternal(const FilePath& database_dir) {
90 DCHECK(IsRunByTaskRunner());
91 VLOG(1) << "Opening " << database_dir.value();
92
93 leveldb::Options options;
94 options.create_if_missing = true;
95 bool delete_and_retry_on_corruption = true;
96
97 while (true) {
98 leveldb::DB* db = NULL;
99 leveldb::Status status =
100 leveldb::DB::Open(options, database_dir.value(), &db);
101 if (status.ok()) {
102 CHECK(db);
103 db_.reset(db);
104 return true;
105 }
106
107 LOG(WARNING) << "Unable to open " << database_dir.value() << ": "
108 << status.ToString();
109
110 // Delete the existing database and try again (just once, though).
111 if (status.IsCorruption() && delete_and_retry_on_corruption) {
112 LOG(WARNING) << "Deleting possibly-corrupt database";
113 file_util::Delete(database_dir, true);
114 delete_and_retry_on_corruption = false;
115 } else {
116 break;
117 }
118 }
119
120 return false;
121 }
122
123 void ContactDatabase::SaveContactsFromTaskRunner(
124 scoped_ptr<ContactPointers> contacts,
125 bool is_full_update,
126 SaveCallback callback) {
127 DCHECK(IsRunByTaskRunner());
128 VLOG(1) << "Saving " << contacts->size() << " contact(s) to database as "
129 << (is_full_update ? "full" : "partial") << " update";
130
131 // If we're doing a full update, find all of the existing keys first so we can
132 // delete ones that aren't present in the new set of contacts.
133 std::set<std::string> keys_to_delete;
134 if (is_full_update) {
135 leveldb::ReadOptions options;
136 scoped_ptr<leveldb::Iterator> db_iterator(db_->NewIterator(options));
137 db_iterator->SeekToFirst();
138 while (db_iterator->Valid()) {
139 keys_to_delete.insert(db_iterator->key().ToString());
140 db_iterator->Next();
141 }
142 }
143
144 // TODO(derat): Serializing all of the contacts and so we can write them in a
145 // single batch may be expensive, memory-wise. Consider writing them in
146 // several batches instead. (To avoid using partial writes in the event of a
147 // crash, maybe add a dummy "write completed" contact that's removed in the
148 // first batch and added in the last.)
149 leveldb::WriteBatch updates;
150 for (ContactPointers::const_iterator it = contacts->begin();
151 it != contacts->end(); ++it) {
152 const contacts::Contact& contact = **it;
153 updates.Put(leveldb::Slice(contact.provider_id()),
154 leveldb::Slice(contact.SerializeAsString()));
155 keys_to_delete.erase(contact.provider_id());
156 }
157
158 for (std::set<std::string>::const_iterator it = keys_to_delete.begin();
159 it != keys_to_delete.end(); ++it) {
160 updates.Delete(leveldb::Slice(*it));
161 }
162
163 leveldb::WriteOptions options;
164 options.sync = true;
165 leveldb::Status status = db_->Write(options, &updates);
166 if (!status.ok())
167 LOG(WARNING) << "Failed writing contacts: " << status.ToString();
168
169 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
170 base::Bind(callback, status.ok()));
satorux1 2012/07/31 16:19:19 This may be potentially dangerous. by the time |ca
Daniel Erat 2012/07/31 16:44:12 I'm not convinced of the risk -- if we initiate de
satorux1 2012/07/31 17:34:26 Yes, RunCallback() just runs callback.Run(). "succ
Daniel Erat 2012/07/31 19:34:42 Makes sense; thanks for the explanation. Done (wi
171 }
172
173 void ContactDatabase::LoadContactsFromTaskRunner(
174 LoadCallback callback) {
175 DCHECK(IsRunByTaskRunner());
176 scoped_ptr<ScopedVector<Contact> > contacts(new ScopedVector<Contact>());
177 bool success = LoadContactsInternal(contacts.get());
178 BrowserThread::PostTask(
179 BrowserThread::UI, FROM_HERE,
180 base::Bind(callback, success, base::Passed(contacts.Pass())));
181 }
182
183 bool ContactDatabase::LoadContactsInternal(ScopedVector<Contact>* contacts) {
184 DCHECK(IsRunByTaskRunner());
185 DCHECK(contacts);
186 contacts->clear();
187
188 leveldb::ReadOptions options;
189 scoped_ptr<leveldb::Iterator> db_iterator(db_->NewIterator(options));
190 db_iterator->SeekToFirst();
191 while (db_iterator->Valid()) {
192 scoped_ptr<Contact> contact(new Contact);
193 leveldb::Slice value_slice = db_iterator->value();
194 if (!contact->ParseFromArray(value_slice.data(), value_slice.size())) {
195 LOG(WARNING) << "Unable to parse contact "
196 << db_iterator->key().ToString();
197 return false;
198 }
199 contacts->push_back(contact.release());
200 db_iterator->Next();
201 }
202 return true;
203 }
204
205 } // namespace contacts
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698