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

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: minor updates 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 "chrome/browser/chromeos/contacts/contact.pb.h"
11 #include "content/public/browser/browser_thread.h"
12 #include "leveldb/db.h"
13 #include "leveldb/iterator.h"
14 #include "leveldb/options.h"
15 #include "leveldb/slice.h"
16 #include "leveldb/status.h"
17 #include "leveldb/write_batch.h"
18
19 using content::BrowserThread;
20
21 namespace contacts {
22
23 ContactDatabase::ContactDatabase()
24 : condition_variable_(&lock_),
25 num_outstanding_file_operations_(0) {
26 }
27
28 ContactDatabase::~ContactDatabase() {
29 base::AutoLock auto_lock(lock_);
30 while (num_outstanding_file_operations_ > 0)
31 condition_variable_.Wait();
satorux1 2012/07/30 17:15:07 I'd suggest not to do this. In gdata_cache.cc we s
Daniel Erat 2012/07/30 22:12:09 Thanks, I didn't know about this, but it's very us
32 }
33
34 void ContactDatabase::Init(const FilePath& database_dir,
35 InitCallback callback) {
36 IncrementOutstandingFileOperations();
37 BrowserThread::PostTask(
38 BrowserThread::FILE, FROM_HERE,
satorux1 2012/07/30 17:15:07 FILE thread is supposed to be dead. Please use Seq
Daniel Erat 2012/07/30 22:12:09 Done.
39 base::Bind(&ContactDatabase::InitOnFileThread, AsWeakPtr(),
40 database_dir, callback));
41 }
42
43 void ContactDatabase::SaveContacts(scoped_ptr<ContactPointers> contacts,
44 bool is_full_update,
45 SaveCallback callback) {
satorux1 2012/07/30 17:15:07 You might want to add something like: DCHECK(Curr
Daniel Erat 2012/07/30 22:12:09 Done.
46 IncrementOutstandingFileOperations();
47 BrowserThread::PostTask(
48 BrowserThread::FILE, FROM_HERE,
49 base::Bind(&ContactDatabase::SaveContactsOnFileThread, AsWeakPtr(),
50 base::Passed(contacts.Pass()), is_full_update, callback));
51 }
52
53 void ContactDatabase::LoadContacts(LoadCallback callback) {
54 IncrementOutstandingFileOperations();
55 BrowserThread::PostTask(
56 BrowserThread::FILE, FROM_HERE,
57 base::Bind(&ContactDatabase::LoadContactsOnFileThread, AsWeakPtr(),
58 callback));
59 }
60
61 void ContactDatabase::InitOnFileThread(const FilePath& database_dir,
62 InitCallback callback) {
63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
64 bool success = InitInternal(database_dir);
65 BrowserThread::PostTask(
66 BrowserThread::UI, FROM_HERE, base::Bind(callback, success));
67 DecrementOutstandingFileOperations();
68 }
69
70 bool ContactDatabase::InitInternal(const FilePath& database_dir) {
71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
72 VLOG(1) << "Opening " << database_dir.value();
73
74 leveldb::Options options;
75 options.create_if_missing = true;
76 bool delete_and_retry_on_corruption = true;
77
78 while (true) {
79 leveldb::DB* db = NULL;
80 leveldb::Status status =
81 leveldb::DB::Open(options, database_dir.value(), &db);
82 if (status.ok()) {
83 CHECK(db);
84 db_.reset(db);
85 return true;
86 }
87
88 LOG(WARNING) << "Unable to open " << database_dir.value() << ": "
89 << status.ToString();
90
91 // Delete the existing database and try again (just once, though).
satorux1 2012/07/30 17:15:07 oh this is a nice technique!
92 if (status.IsCorruption() && delete_and_retry_on_corruption) {
93 LOG(WARNING) << "Deleting possibly-corrupt database";
94 file_util::Delete(database_dir, true);
95 delete_and_retry_on_corruption = false;
96 } else {
97 break;
98 }
99 }
100
101 return false;
102 }
103
104 void ContactDatabase::SaveContactsOnFileThread(
105 scoped_ptr<ContactPointers> contacts,
106 bool is_full_update,
107 SaveCallback callback) {
108 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
109 VLOG(1) << "Saving " << contacts->size() << " contact(s) to database as "
110 << (is_full_update ? "full" : "partial") << " update";
111
112 // If we're doing a full update, find all of the existing keys first so we can
113 // delete ones that aren't present in the new set of contacts.
114 std::set<std::string> keys_to_delete;
115 if (is_full_update) {
116 leveldb::ReadOptions options;
117 scoped_ptr<leveldb::Iterator> db_iterator(db_->NewIterator(options));
118 db_iterator->SeekToFirst();
119 while (db_iterator->Valid()) {
120 keys_to_delete.insert(db_iterator->key().ToString());
121 db_iterator->Next();
122 }
123 }
124
125 // TODO(derat): Serializing all of the contacts and so we can write them in a
126 // single batch may be expensive, memory-wise. Consider writing them in
127 // several batches instead. (To avoid using partial writes in the event of a
128 // crash, maybe add a dummy "write completed" contact that's removed in the
129 // first batch and added in the last.)
130 leveldb::WriteBatch updates;
131 for (ContactPointers::const_iterator it = contacts->begin();
132 it != contacts->end(); ++it) {
133 const contacts::Contact& contact = **it;
134 updates.Put(leveldb::Slice(contact.provider_id()),
135 leveldb::Slice(contact.SerializeAsString()));
136 keys_to_delete.erase(contact.provider_id());
137 }
138
139 for (std::set<std::string>::const_iterator it = keys_to_delete.begin();
140 it != keys_to_delete.end(); ++it) {
141 updates.Delete(leveldb::Slice(*it));
142 }
143
144 leveldb::WriteOptions options;
145 options.sync = true;
146 leveldb::Status status = db_->Write(options, &updates);
147 if (!status.ok())
148 LOG(WARNING) << "Failed writing contacts: " << status.ToString();
149
150 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
151 base::Bind(callback, status.ok()));
152 DecrementOutstandingFileOperations();
153 }
154
155 void ContactDatabase::LoadContactsOnFileThread(LoadCallback callback) {
156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
157 scoped_ptr<ScopedVector<Contact> > contacts(new ScopedVector<Contact>());
158 bool success = LoadContactsInternal(contacts.get());
159 BrowserThread::PostTask(
160 BrowserThread::UI, FROM_HERE,
161 base::Bind(callback, success, base::Passed(contacts.Pass())));
162 DecrementOutstandingFileOperations();
163 }
164
165 bool ContactDatabase::LoadContactsInternal(ScopedVector<Contact>* contacts) {
166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
167 DCHECK(contacts);
168 contacts->clear();
169
170 leveldb::ReadOptions options;
171 scoped_ptr<leveldb::Iterator> db_iterator(db_->NewIterator(options));
172 db_iterator->SeekToFirst();
173 while (db_iterator->Valid()) {
174 scoped_ptr<Contact> contact(new Contact);
175 leveldb::Slice value_slice = db_iterator->value();
176 if (!contact->ParseFromArray(value_slice.data(), value_slice.size())) {
177 LOG(WARNING) << "Unable to parse contact "
178 << db_iterator->key().ToString();
179 return false;
180 }
181 contacts->push_back(contact.release());
182 db_iterator->Next();
183 }
184 return true;
185 }
186
187 void ContactDatabase::IncrementOutstandingFileOperations() {
188 base::AutoLock auto_lock(lock_);
189 num_outstanding_file_operations_++;
190 }
191
192 void ContactDatabase::DecrementOutstandingFileOperations() {
193 base::AutoLock auto_lock(lock_);
194 DCHECK_GT(num_outstanding_file_operations_, 0);
195 num_outstanding_file_operations_--;
196 condition_variable_.Signal();
197 }
198
199 } // namespace contacts
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698