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

Side by Side Diff: chrome/browser/sync/syncable/syncable.cc

Issue 9699057: [Sync] Move 'sync' target to sync/ (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address Tim's comments Created 8 years, 9 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
« no previous file with comments | « chrome/browser/sync/syncable/syncable.h ('k') | chrome/browser/sync/syncable/syncable-inl.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/sync/syncable/syncable.h"
6
7 #include <algorithm>
8 #include <cstring>
9 #include <functional>
10 #include <iomanip>
11 #include <iterator>
12 #include <limits>
13 #include <set>
14 #include <string>
15
16 #include "base/basictypes.h"
17 #include "base/debug/trace_event.h"
18 #include "base/compiler_specific.h"
19 #include "base/debug/trace_event.h"
20 #include "base/file_util.h"
21 #include "base/hash_tables.h"
22 #include "base/location.h"
23 #include "base/logging.h"
24 #include "base/memory/scoped_ptr.h"
25 #include "base/perftimer.h"
26 #include "base/stl_util.h"
27 #include "base/string_number_conversions.h"
28 #include "base/string_util.h"
29 #include "base/time.h"
30 #include "base/utf_string_conversions.h"
31 #include "base/values.h"
32 #include "chrome/browser/sync/protocol/proto_value_conversions.h"
33 #include "chrome/browser/sync/protocol/service_constants.h"
34 #include "chrome/browser/sync/syncable/directory_backing_store.h"
35 #include "chrome/browser/sync/syncable/directory_change_delegate.h"
36 #include "chrome/browser/sync/syncable/in_memory_directory_backing_store.h"
37 #include "chrome/browser/sync/syncable/model_type.h"
38 #include "chrome/browser/sync/syncable/on_disk_directory_backing_store.h"
39 #include "chrome/browser/sync/syncable/syncable-inl.h"
40 #include "chrome/browser/sync/syncable/syncable_changes_version.h"
41 #include "chrome/browser/sync/syncable/syncable_columns.h"
42 #include "chrome/browser/sync/syncable/syncable_enum_conversions.h"
43 #include "chrome/browser/sync/syncable/transaction_observer.h"
44 #include "chrome/browser/sync/util/logging.h"
45 #include "chrome/browser/sync/util/cryptographer.h"
46 #include "net/base/escape.h"
47
48 namespace {
49
50 enum InvariantCheckLevel {
51 OFF = 0,
52 VERIFY_IN_MEMORY = 1,
53 FULL_DB_VERIFICATION = 2
54 };
55
56 const InvariantCheckLevel kInvariantCheckLevel = VERIFY_IN_MEMORY;
57
58 // Max number of milliseconds to spend checking syncable entry invariants
59 const int kInvariantCheckMaxMs = 50;
60
61 // This function checks to see if the given list of Metahandles has any nodes
62 // whose PREV_ID, PARENT_ID or NEXT_ID values refer to ID values that do not
63 // actually exist. Returns true on success.
64 //
65 // This function is "Unsafe" because it does not attempt to acquire any locks
66 // that may be protecting this list that gets passed in. The caller is
67 // responsible for ensuring that no one modifies this list while the function is
68 // running.
69 bool VerifyReferenceIntegrityUnsafe(const syncable::MetahandlesIndex &index) {
70 TRACE_EVENT0("sync", "SyncDatabaseIntegrityCheck");
71 using namespace syncable;
72 typedef base::hash_set<std::string> IdsSet;
73
74 IdsSet ids_set;
75 bool is_ok = true;
76
77 for (MetahandlesIndex::const_iterator it = index.begin();
78 it != index.end(); ++it) {
79 EntryKernel* entry = *it;
80 bool is_duplicate_id = !(ids_set.insert(entry->ref(ID).value()).second);
81 is_ok = is_ok && !is_duplicate_id;
82 }
83
84 IdsSet::iterator end = ids_set.end();
85 for (MetahandlesIndex::const_iterator it = index.begin();
86 it != index.end(); ++it) {
87 EntryKernel* entry = *it;
88 bool prev_exists = (ids_set.find(entry->ref(PREV_ID).value()) != end);
89 bool parent_exists = (ids_set.find(entry->ref(PARENT_ID).value()) != end);
90 bool next_exists = (ids_set.find(entry->ref(NEXT_ID).value()) != end);
91 is_ok = is_ok && prev_exists && parent_exists && next_exists;
92 }
93 return is_ok;
94 }
95
96 } // namespace
97
98 using std::string;
99 using browser_sync::Encryptor;
100 using browser_sync::ReportUnrecoverableErrorFunction;
101 using browser_sync::UnrecoverableErrorHandler;
102
103 namespace syncable {
104
105 namespace {
106
107 // Function to handle runtime failures on syncable code. Rather than crashing,
108 // if the |condition| is false the following will happen:
109 // 1. Sets unrecoverable error on transaction.
110 // 2. Returns false.
111 bool SyncAssert(bool condition,
112 const tracked_objects::Location& location,
113 const char* msg,
114 BaseTransaction* trans) {
115 if (!condition) {
116 trans->OnUnrecoverableError(location, msg);
117 return false;
118 }
119 return true;
120 }
121
122 } // namespace
123
124 #define ENUM_CASE(x) case x: return #x; break
125
126 std::string WriterTagToString(WriterTag writer_tag) {
127 switch (writer_tag) {
128 ENUM_CASE(INVALID);
129 ENUM_CASE(SYNCER);
130 ENUM_CASE(AUTHWATCHER);
131 ENUM_CASE(UNITTEST);
132 ENUM_CASE(VACUUM_AFTER_SAVE);
133 ENUM_CASE(PURGE_ENTRIES);
134 ENUM_CASE(SYNCAPI);
135 };
136 NOTREACHED();
137 return "";
138 }
139
140 #undef ENUM_CASE
141
142 WriteTransactionInfo::WriteTransactionInfo(
143 int64 id,
144 tracked_objects::Location location,
145 WriterTag writer,
146 ImmutableEntryKernelMutationMap mutations)
147 : id(id),
148 location_string(location.ToString()),
149 writer(writer),
150 mutations(mutations) {}
151
152 WriteTransactionInfo::WriteTransactionInfo()
153 : id(-1), writer(INVALID) {}
154
155 WriteTransactionInfo::~WriteTransactionInfo() {}
156
157 base::DictionaryValue* WriteTransactionInfo::ToValue(
158 size_t max_mutations_size) const {
159 DictionaryValue* dict = new DictionaryValue();
160 dict->SetString("id", base::Int64ToString(id));
161 dict->SetString("location", location_string);
162 dict->SetString("writer", WriterTagToString(writer));
163 Value* mutations_value = NULL;
164 const size_t mutations_size = mutations.Get().size();
165 if (mutations_size <= max_mutations_size) {
166 mutations_value = EntryKernelMutationMapToValue(mutations.Get());
167 } else {
168 mutations_value =
169 Value::CreateStringValue(
170 base::Uint64ToString(static_cast<uint64>(mutations_size)) +
171 " mutations");
172 }
173 dict->Set("mutations", mutations_value);
174 return dict;
175 }
176
177 DictionaryValue* EntryKernelMutationToValue(
178 const EntryKernelMutation& mutation) {
179 DictionaryValue* dict = new DictionaryValue();
180 dict->Set("original", mutation.original.ToValue());
181 dict->Set("mutated", mutation.mutated.ToValue());
182 return dict;
183 }
184
185 ListValue* EntryKernelMutationMapToValue(
186 const EntryKernelMutationMap& mutations) {
187 ListValue* list = new ListValue();
188 for (EntryKernelMutationMap::const_iterator it = mutations.begin();
189 it != mutations.end(); ++it) {
190 list->Append(EntryKernelMutationToValue(it->second));
191 }
192 return list;
193 }
194
195 namespace {
196
197 // A ScopedIndexUpdater temporarily removes an entry from an index,
198 // and restores it to the index when the scope exits. This simplifies
199 // the common pattern where items need to be removed from an index
200 // before updating the field.
201 //
202 // This class is parameterized on the Indexer traits type, which
203 // must define a Comparator and a static bool ShouldInclude
204 // function for testing whether the item ought to be included
205 // in the index.
206 template<typename Indexer>
207 class ScopedIndexUpdater {
208 public:
209 ScopedIndexUpdater(const ScopedKernelLock& proof_of_lock,
210 EntryKernel* entry,
211 typename Index<Indexer>::Set* index)
212 : entry_(entry),
213 index_(index) {
214 // First call to ShouldInclude happens before the field is updated.
215 if (Indexer::ShouldInclude(entry_)) {
216 // TODO(lipalani): Replace this CHECK with |SyncAssert| by refactorting
217 // this class into a function.
218 CHECK(index_->erase(entry_));
219 }
220 }
221
222 ~ScopedIndexUpdater() {
223 // Second call to ShouldInclude happens after the field is updated.
224 if (Indexer::ShouldInclude(entry_)) {
225 // TODO(lipalani): Replace this CHECK with |SyncAssert| by refactorting
226 // this class into a function.
227 CHECK(index_->insert(entry_).second);
228 }
229 }
230 private:
231 // The entry that was temporarily removed from the index.
232 EntryKernel* entry_;
233 // The index which we are updating.
234 typename Index<Indexer>::Set* const index_;
235 };
236
237 // Helper function to add an item to the index, if it ought to be added.
238 template<typename Indexer>
239 void InitializeIndexEntry(EntryKernel* entry,
240 typename Index<Indexer>::Set* index) {
241 if (Indexer::ShouldInclude(entry)) {
242 index->insert(entry);
243 }
244 }
245
246 } // namespace
247
248 ///////////////////////////////////////////////////////////////////////////
249 // Comparator and filter functions for the indices.
250
251 // static
252 bool ClientTagIndexer::ShouldInclude(const EntryKernel* a) {
253 return !a->ref(UNIQUE_CLIENT_TAG).empty();
254 }
255
256 bool ParentIdAndHandleIndexer::Comparator::operator() (
257 const syncable::EntryKernel* a,
258 const syncable::EntryKernel* b) const {
259 int cmp = a->ref(PARENT_ID).compare(b->ref(PARENT_ID));
260 if (cmp != 0)
261 return cmp < 0;
262
263 int64 a_position = a->ref(SERVER_POSITION_IN_PARENT);
264 int64 b_position = b->ref(SERVER_POSITION_IN_PARENT);
265 if (a_position != b_position)
266 return a_position < b_position;
267
268 cmp = a->ref(ID).compare(b->ref(ID));
269 return cmp < 0;
270 }
271
272 // static
273 bool ParentIdAndHandleIndexer::ShouldInclude(const EntryKernel* a) {
274 // This index excludes deleted items and the root item. The root
275 // item is excluded so that it doesn't show up as a child of itself.
276 return !a->ref(IS_DEL) && !a->ref(ID).IsRoot();
277 }
278
279 ///////////////////////////////////////////////////////////////////////////
280 // EntryKernel
281
282 EntryKernel::EntryKernel() : dirty_(false) {
283 // Everything else should already be default-initialized.
284 for (int i = INT64_FIELDS_BEGIN; i < INT64_FIELDS_END; ++i) {
285 int64_fields[i] = 0;
286 }
287 }
288
289 EntryKernel::~EntryKernel() {}
290
291 syncable::ModelType EntryKernel::GetServerModelType() const {
292 ModelType specifics_type = GetModelTypeFromSpecifics(ref(SERVER_SPECIFICS));
293 if (specifics_type != UNSPECIFIED)
294 return specifics_type;
295 if (ref(ID).IsRoot())
296 return TOP_LEVEL_FOLDER;
297 // Loose check for server-created top-level folders that aren't
298 // bound to a particular model type.
299 if (!ref(UNIQUE_SERVER_TAG).empty() && ref(SERVER_IS_DIR))
300 return TOP_LEVEL_FOLDER;
301
302 return UNSPECIFIED;
303 }
304
305 bool EntryKernel::ContainsString(const std::string& lowercase_query) const {
306 // TODO(lipalani) - figure out what to do if the node is encrypted.
307 const sync_pb::EntitySpecifics& specifics = ref(SPECIFICS);
308 std::string temp;
309 // The protobuf serialized string contains the original strings. So
310 // we will just serialize it and search it.
311 specifics.SerializeToString(&temp);
312
313 // Now convert to lower case.
314 StringToLowerASCII(&temp);
315
316 if (temp.find(lowercase_query) != std::string::npos)
317 return true;
318
319 // Now go through all the string fields to see if the value is there.
320 for (int i = STRING_FIELDS_BEGIN; i < STRING_FIELDS_END; ++i) {
321 if (StringToLowerASCII(ref(static_cast<StringField>(i))).find(
322 lowercase_query) != std::string::npos)
323 return true;
324 }
325
326 for (int i = ID_FIELDS_BEGIN; i < ID_FIELDS_END; ++i) {
327 const Id& id = ref(static_cast<IdField>(i));
328 if (id.ContainsStringCaseInsensitive(lowercase_query)) {
329 return true;
330 }
331 }
332 return false;
333 }
334
335 namespace {
336
337 // Utility function to loop through a set of enum values and add the
338 // field keys/values in the kernel to the given dictionary.
339 //
340 // V should be convertible to Value.
341 template <class T, class U, class V>
342 void SetFieldValues(const EntryKernel& kernel,
343 DictionaryValue* dictionary_value,
344 const char* (*enum_key_fn)(T),
345 V* (*enum_value_fn)(U),
346 int field_key_min, int field_key_max) {
347 DCHECK_LE(field_key_min, field_key_max);
348 for (int i = field_key_min; i <= field_key_max; ++i) {
349 T field = static_cast<T>(i);
350 const std::string& key = enum_key_fn(field);
351 V* value = enum_value_fn(kernel.ref(field));
352 dictionary_value->Set(key, value);
353 }
354 }
355
356 // Helper functions for SetFieldValues().
357
358 StringValue* Int64ToValue(int64 i) {
359 return Value::CreateStringValue(base::Int64ToString(i));
360 }
361
362 StringValue* TimeToValue(const base::Time& t) {
363 return Value::CreateStringValue(browser_sync::GetTimeDebugString(t));
364 }
365
366 StringValue* IdToValue(const Id& id) {
367 return id.ToValue();
368 }
369
370 } // namespace
371
372 DictionaryValue* EntryKernel::ToValue() const {
373 DictionaryValue* kernel_info = new DictionaryValue();
374 kernel_info->SetBoolean("isDirty", is_dirty());
375 kernel_info->Set("serverModelType", ModelTypeToValue(GetServerModelType()));
376
377 // Int64 fields.
378 SetFieldValues(*this, kernel_info,
379 &GetMetahandleFieldString, &Int64ToValue,
380 INT64_FIELDS_BEGIN, META_HANDLE);
381 SetFieldValues(*this, kernel_info,
382 &GetBaseVersionString, &Int64ToValue,
383 META_HANDLE + 1, BASE_VERSION);
384 SetFieldValues(*this, kernel_info,
385 &GetInt64FieldString, &Int64ToValue,
386 BASE_VERSION + 1, INT64_FIELDS_END - 1);
387
388 // Time fields.
389 SetFieldValues(*this, kernel_info,
390 &GetTimeFieldString, &TimeToValue,
391 TIME_FIELDS_BEGIN, TIME_FIELDS_END - 1);
392
393 // ID fields.
394 SetFieldValues(*this, kernel_info,
395 &GetIdFieldString, &IdToValue,
396 ID_FIELDS_BEGIN, ID_FIELDS_END - 1);
397
398 // Bit fields.
399 SetFieldValues(*this, kernel_info,
400 &GetIndexedBitFieldString, &Value::CreateBooleanValue,
401 BIT_FIELDS_BEGIN, INDEXED_BIT_FIELDS_END - 1);
402 SetFieldValues(*this, kernel_info,
403 &GetIsDelFieldString, &Value::CreateBooleanValue,
404 INDEXED_BIT_FIELDS_END, IS_DEL);
405 SetFieldValues(*this, kernel_info,
406 &GetBitFieldString, &Value::CreateBooleanValue,
407 IS_DEL + 1, BIT_FIELDS_END - 1);
408
409 // String fields.
410 {
411 // Pick out the function overload we want.
412 StringValue* (*string_to_value)(const std::string&) =
413 &Value::CreateStringValue;
414 SetFieldValues(*this, kernel_info,
415 &GetStringFieldString, string_to_value,
416 STRING_FIELDS_BEGIN, STRING_FIELDS_END - 1);
417 }
418
419 // Proto fields.
420 SetFieldValues(*this, kernel_info,
421 &GetProtoFieldString, &browser_sync::EntitySpecificsToValue,
422 PROTO_FIELDS_BEGIN, PROTO_FIELDS_END - 1);
423
424 // Bit temps.
425 SetFieldValues(*this, kernel_info,
426 &GetBitTempString, &Value::CreateBooleanValue,
427 BIT_TEMPS_BEGIN, BIT_TEMPS_END - 1);
428
429 return kernel_info;
430 }
431
432 ///////////////////////////////////////////////////////////////////////////
433 // Directory
434
435 // static
436 const FilePath::CharType Directory::kSyncDatabaseFilename[] =
437 FILE_PATH_LITERAL("SyncData.sqlite3");
438
439 void Directory::InitKernelForTest(
440 const std::string& name,
441 DirectoryChangeDelegate* delegate,
442 const browser_sync::WeakHandle<TransactionObserver>&
443 transaction_observer) {
444 DCHECK(!kernel_);
445 kernel_ = new Kernel(name, KernelLoadInfo(), delegate, transaction_observer);
446 }
447
448 Directory::PersistedKernelInfo::PersistedKernelInfo()
449 : next_id(0) {
450 for (int i = FIRST_REAL_MODEL_TYPE; i < MODEL_TYPE_COUNT; ++i) {
451 reset_download_progress(ModelTypeFromInt(i));
452 }
453 }
454
455 Directory::PersistedKernelInfo::~PersistedKernelInfo() {}
456
457 void Directory::PersistedKernelInfo::reset_download_progress(
458 ModelType model_type) {
459 download_progress[model_type].set_data_type_id(
460 GetSpecificsFieldNumberFromModelType(model_type));
461 // An empty-string token indicates no prior knowledge.
462 download_progress[model_type].set_token(std::string());
463 }
464
465 Directory::SaveChangesSnapshot::SaveChangesSnapshot()
466 : kernel_info_status(KERNEL_SHARE_INFO_INVALID) {
467 }
468
469 Directory::SaveChangesSnapshot::~SaveChangesSnapshot() {}
470
471 Directory::Kernel::Kernel(
472 const std::string& name,
473 const KernelLoadInfo& info, DirectoryChangeDelegate* delegate,
474 const browser_sync::WeakHandle<TransactionObserver>&
475 transaction_observer)
476 : refcount(1),
477 next_write_transaction_id(0),
478 name(name),
479 metahandles_index(new Directory::MetahandlesIndex),
480 ids_index(new Directory::IdsIndex),
481 parent_id_child_index(new Directory::ParentIdChildIndex),
482 client_tag_index(new Directory::ClientTagIndex),
483 unsynced_metahandles(new MetahandleSet),
484 dirty_metahandles(new MetahandleSet),
485 metahandles_to_purge(new MetahandleSet),
486 info_status(Directory::KERNEL_SHARE_INFO_VALID),
487 persisted_info(info.kernel_info),
488 cache_guid(info.cache_guid),
489 next_metahandle(info.max_metahandle + 1),
490 delegate(delegate),
491 transaction_observer(transaction_observer) {
492 DCHECK(delegate);
493 DCHECK(transaction_observer.IsInitialized());
494 }
495
496 void Directory::Kernel::AddRef() {
497 base::subtle::NoBarrier_AtomicIncrement(&refcount, 1);
498 }
499
500 void Directory::Kernel::Release() {
501 if (!base::subtle::NoBarrier_AtomicIncrement(&refcount, -1))
502 delete this;
503 }
504
505 Directory::Kernel::~Kernel() {
506 CHECK_EQ(0, refcount);
507 delete unsynced_metahandles;
508 delete dirty_metahandles;
509 delete metahandles_to_purge;
510 delete parent_id_child_index;
511 delete client_tag_index;
512 delete ids_index;
513 STLDeleteElements(metahandles_index);
514 delete metahandles_index;
515 }
516
517 Directory::Directory(
518 Encryptor* encryptor,
519 UnrecoverableErrorHandler* unrecoverable_error_handler,
520 ReportUnrecoverableErrorFunction report_unrecoverable_error_function)
521 : cryptographer_(encryptor),
522 kernel_(NULL),
523 store_(NULL),
524 unrecoverable_error_handler_(unrecoverable_error_handler),
525 report_unrecoverable_error_function_(
526 report_unrecoverable_error_function),
527 unrecoverable_error_set_(false) {
528 }
529
530 Directory::~Directory() {
531 Close();
532 }
533
534 DirOpenResult Directory::Open(
535 const FilePath& file_path, const string& name,
536 DirectoryChangeDelegate* delegate,
537 const browser_sync::WeakHandle<TransactionObserver>&
538 transaction_observer) {
539 TRACE_EVENT0("sync", "SyncDatabaseOpen");
540
541 FilePath db_path(file_path);
542 file_util::AbsolutePath(&db_path);
543 DirectoryBackingStore* store = new OnDiskDirectoryBackingStore(name, db_path);
544
545 const DirOpenResult result =
546 OpenImpl(store, name, delegate, transaction_observer);
547
548 if (OPENED != result)
549 Close();
550 return result;
551 }
552
553 DirOpenResult Directory::OpenInMemoryForTest(
554 const string& name, DirectoryChangeDelegate* delegate,
555 const browser_sync::WeakHandle<TransactionObserver>&
556 transaction_observer) {
557
558 DirectoryBackingStore* store = new InMemoryDirectoryBackingStore(name);
559
560 const DirOpenResult result =
561 OpenImpl(store, name, delegate, transaction_observer);
562 if (OPENED != result)
563 Close();
564 return result;
565 }
566
567 void Directory::InitializeIndices() {
568 MetahandlesIndex::iterator it = kernel_->metahandles_index->begin();
569 for (; it != kernel_->metahandles_index->end(); ++it) {
570 EntryKernel* entry = *it;
571 InitializeIndexEntry<ParentIdAndHandleIndexer>(entry,
572 kernel_->parent_id_child_index);
573 InitializeIndexEntry<IdIndexer>(entry, kernel_->ids_index);
574 InitializeIndexEntry<ClientTagIndexer>(entry, kernel_->client_tag_index);
575 const int64 metahandle = entry->ref(META_HANDLE);
576 if (entry->ref(IS_UNSYNCED))
577 kernel_->unsynced_metahandles->insert(metahandle);
578 if (entry->ref(IS_UNAPPLIED_UPDATE)) {
579 const ModelType type = entry->GetServerModelType();
580 kernel_->unapplied_update_metahandles[type].insert(metahandle);
581 }
582 DCHECK(!entry->is_dirty());
583 }
584 }
585
586 DirOpenResult Directory::OpenImpl(
587 DirectoryBackingStore* store,
588 const string& name,
589 DirectoryChangeDelegate* delegate,
590 const browser_sync::WeakHandle<TransactionObserver>&
591 transaction_observer) {
592 DCHECK_EQ(static_cast<DirectoryBackingStore*>(NULL), store_);
593 store_ = store;
594
595 KernelLoadInfo info;
596 // Temporary indices before kernel_ initialized in case Load fails. We 0(1)
597 // swap these later.
598 MetahandlesIndex metas_bucket;
599 DirOpenResult result = store_->Load(&metas_bucket, &info);
600 if (OPENED != result)
601 return result;
602
603 if (!VerifyReferenceIntegrityUnsafe(metas_bucket))
604 return FAILED_LOGICAL_CORRUPTION;
605
606 kernel_ = new Kernel(name, info, delegate, transaction_observer);
607 kernel_->metahandles_index->swap(metas_bucket);
608 InitializeIndices();
609 return OPENED;
610 }
611
612 void Directory::Close() {
613 if (store_)
614 delete store_;
615 store_ = NULL;
616 if (kernel_) {
617 bool del = !base::subtle::NoBarrier_AtomicIncrement(&kernel_->refcount, -1);
618 DCHECK(del) << "Kernel should only have a single ref";
619 if (del)
620 delete kernel_;
621 kernel_ = NULL;
622 }
623 }
624
625 void Directory::OnUnrecoverableError(const BaseTransaction* trans,
626 const tracked_objects::Location& location,
627 const std::string & message) {
628 DCHECK(trans != NULL);
629 unrecoverable_error_set_ = true;
630 unrecoverable_error_handler_->OnUnrecoverableError(location,
631 message);
632 }
633
634
635 EntryKernel* Directory::GetEntryById(const Id& id) {
636 ScopedKernelLock lock(this);
637 return GetEntryById(id, &lock);
638 }
639
640 EntryKernel* Directory::GetEntryById(const Id& id,
641 ScopedKernelLock* const lock) {
642 DCHECK(kernel_);
643 // Find it in the in memory ID index.
644 kernel_->needle.put(ID, id);
645 IdsIndex::iterator id_found = kernel_->ids_index->find(&kernel_->needle);
646 if (id_found != kernel_->ids_index->end()) {
647 return *id_found;
648 }
649 return NULL;
650 }
651
652 EntryKernel* Directory::GetEntryByClientTag(const string& tag) {
653 ScopedKernelLock lock(this);
654 DCHECK(kernel_);
655 // Find it in the ClientTagIndex.
656 kernel_->needle.put(UNIQUE_CLIENT_TAG, tag);
657 ClientTagIndex::iterator found = kernel_->client_tag_index->find(
658 &kernel_->needle);
659 if (found != kernel_->client_tag_index->end()) {
660 return *found;
661 }
662 return NULL;
663 }
664
665 EntryKernel* Directory::GetEntryByServerTag(const string& tag) {
666 ScopedKernelLock lock(this);
667 DCHECK(kernel_);
668 // We don't currently keep a separate index for the tags. Since tags
669 // only exist for server created items that are the first items
670 // to be created in a store, they should have small metahandles.
671 // So, we just iterate over the items in sorted metahandle order,
672 // looking for a match.
673 MetahandlesIndex& set = *kernel_->metahandles_index;
674 for (MetahandlesIndex::iterator i = set.begin(); i != set.end(); ++i) {
675 if ((*i)->ref(UNIQUE_SERVER_TAG) == tag) {
676 return *i;
677 }
678 }
679 return NULL;
680 }
681
682 EntryKernel* Directory::GetEntryByHandle(int64 metahandle) {
683 ScopedKernelLock lock(this);
684 return GetEntryByHandle(metahandle, &lock);
685 }
686
687 EntryKernel* Directory::GetEntryByHandle(int64 metahandle,
688 ScopedKernelLock* lock) {
689 // Look up in memory
690 kernel_->needle.put(META_HANDLE, metahandle);
691 MetahandlesIndex::iterator found =
692 kernel_->metahandles_index->find(&kernel_->needle);
693 if (found != kernel_->metahandles_index->end()) {
694 // Found it in memory. Easy.
695 return *found;
696 }
697 return NULL;
698 }
699
700 bool Directory::GetChildHandlesById(
701 BaseTransaction* trans, const Id& parent_id,
702 Directory::ChildHandles* result) {
703 if (!SyncAssert(this == trans->directory(), FROM_HERE,
704 "Directories don't match", trans))
705 return false;
706 result->clear();
707
708 ScopedKernelLock lock(this);
709 AppendChildHandles(lock, parent_id, result);
710 return true;
711 }
712
713 bool Directory::GetChildHandlesByHandle(
714 BaseTransaction* trans, int64 handle,
715 Directory::ChildHandles* result) {
716 if (!SyncAssert(this == trans->directory(), FROM_HERE,
717 "Directories don't match", trans))
718 return false;
719
720 result->clear();
721
722 ScopedKernelLock lock(this);
723 EntryKernel* kernel = GetEntryByHandle(handle, &lock);
724 if (!kernel)
725 return true;
726
727 AppendChildHandles(lock, kernel->ref(ID), result);
728 return true;
729 }
730
731 EntryKernel* Directory::GetRootEntry() {
732 return GetEntryById(Id());
733 }
734
735 bool Directory::InsertEntry(WriteTransaction* trans, EntryKernel* entry) {
736 ScopedKernelLock lock(this);
737 return InsertEntry(trans, entry, &lock);
738 }
739
740 bool Directory::InsertEntry(WriteTransaction* trans,
741 EntryKernel* entry,
742 ScopedKernelLock* lock) {
743 DCHECK(NULL != lock);
744 if (!SyncAssert(NULL != entry, FROM_HERE, "Entry is null", trans))
745 return false;
746
747 static const char error[] = "Entry already in memory index.";
748 if (!SyncAssert(kernel_->metahandles_index->insert(entry).second,
749 FROM_HERE,
750 error,
751 trans))
752 return false;
753
754 if (!entry->ref(IS_DEL)) {
755 if (!SyncAssert(kernel_->parent_id_child_index->insert(entry).second,
756 FROM_HERE,
757 error,
758 trans)) {
759 return false;
760 }
761 }
762 if (!SyncAssert(kernel_->ids_index->insert(entry).second,
763 FROM_HERE,
764 error,
765 trans))
766 return false;
767
768 // Should NEVER be created with a client tag.
769 if (!SyncAssert(entry->ref(UNIQUE_CLIENT_TAG).empty(), FROM_HERE,
770 "Client should be empty", trans))
771 return false;
772
773 return true;
774 }
775
776 bool Directory::ReindexId(WriteTransaction* trans,
777 EntryKernel* const entry,
778 const Id& new_id) {
779 ScopedKernelLock lock(this);
780 if (NULL != GetEntryById(new_id, &lock))
781 return false;
782
783 {
784 // Update the indices that depend on the ID field.
785 ScopedIndexUpdater<IdIndexer> updater_a(lock, entry, kernel_->ids_index);
786 ScopedIndexUpdater<ParentIdAndHandleIndexer> updater_b(lock, entry,
787 kernel_->parent_id_child_index);
788 entry->put(ID, new_id);
789 }
790 return true;
791 }
792
793 bool Directory::ReindexParentId(WriteTransaction* trans,
794 EntryKernel* const entry,
795 const Id& new_parent_id) {
796 ScopedKernelLock lock(this);
797
798 {
799 // Update the indices that depend on the PARENT_ID field.
800 ScopedIndexUpdater<ParentIdAndHandleIndexer> index_updater(lock, entry,
801 kernel_->parent_id_child_index);
802 entry->put(PARENT_ID, new_parent_id);
803 }
804 return true;
805 }
806
807 bool Directory::unrecoverable_error_set(const BaseTransaction* trans) const {
808 DCHECK(trans != NULL);
809 return unrecoverable_error_set_;
810 }
811
812 void Directory::ClearDirtyMetahandles() {
813 kernel_->transaction_mutex.AssertAcquired();
814 kernel_->dirty_metahandles->clear();
815 }
816
817 bool Directory::SafeToPurgeFromMemory(WriteTransaction* trans,
818 const EntryKernel* const entry) const {
819 bool safe = entry->ref(IS_DEL) && !entry->is_dirty() &&
820 !entry->ref(SYNCING) && !entry->ref(IS_UNAPPLIED_UPDATE) &&
821 !entry->ref(IS_UNSYNCED);
822
823 if (safe) {
824 int64 handle = entry->ref(META_HANDLE);
825 const ModelType type = entry->GetServerModelType();
826 if (!SyncAssert(kernel_->dirty_metahandles->count(handle) == 0U,
827 FROM_HERE,
828 "Dirty metahandles should be empty", trans))
829 return false;
830 // TODO(tim): Bug 49278.
831 if (!SyncAssert(!kernel_->unsynced_metahandles->count(handle),
832 FROM_HERE,
833 "Unsynced handles should be empty",
834 trans))
835 return false;
836 if (!SyncAssert(!kernel_->unapplied_update_metahandles[type].count(handle),
837 FROM_HERE,
838 "Unapplied metahandles should be empty",
839 trans))
840 return false;
841 }
842
843 return safe;
844 }
845
846 void Directory::TakeSnapshotForSaveChanges(SaveChangesSnapshot* snapshot) {
847 ReadTransaction trans(FROM_HERE, this);
848 ScopedKernelLock lock(this);
849
850 // If there is an unrecoverable error then just bail out.
851 if (unrecoverable_error_set(&trans))
852 return;
853
854 // Deep copy dirty entries from kernel_->metahandles_index into snapshot and
855 // clear dirty flags.
856 for (MetahandleSet::const_iterator i = kernel_->dirty_metahandles->begin();
857 i != kernel_->dirty_metahandles->end(); ++i) {
858 EntryKernel* entry = GetEntryByHandle(*i, &lock);
859 if (!entry)
860 continue;
861 // Skip over false positives; it happens relatively infrequently.
862 if (!entry->is_dirty())
863 continue;
864 snapshot->dirty_metas.insert(snapshot->dirty_metas.end(), *entry);
865 DCHECK_EQ(1U, kernel_->dirty_metahandles->count(*i));
866 // We don't bother removing from the index here as we blow the entire thing
867 // in a moment, and it unnecessarily complicates iteration.
868 entry->clear_dirty(NULL);
869 }
870 ClearDirtyMetahandles();
871
872 // Set purged handles.
873 DCHECK(snapshot->metahandles_to_purge.empty());
874 snapshot->metahandles_to_purge.swap(*(kernel_->metahandles_to_purge));
875
876 // Fill kernel_info_status and kernel_info.
877 snapshot->kernel_info = kernel_->persisted_info;
878 // To avoid duplicates when the process crashes, we record the next_id to be
879 // greater magnitude than could possibly be reached before the next save
880 // changes. In other words, it's effectively impossible for the user to
881 // generate 65536 new bookmarks in 3 seconds.
882 snapshot->kernel_info.next_id -= 65536;
883 snapshot->kernel_info_status = kernel_->info_status;
884 // This one we reset on failure.
885 kernel_->info_status = KERNEL_SHARE_INFO_VALID;
886 }
887
888 bool Directory::SaveChanges() {
889 bool success = false;
890 DCHECK(store_);
891
892 base::AutoLock scoped_lock(kernel_->save_changes_mutex);
893
894 // Snapshot and save.
895 SaveChangesSnapshot snapshot;
896 TakeSnapshotForSaveChanges(&snapshot);
897 success = store_->SaveChanges(snapshot);
898
899 // Handle success or failure.
900 if (success)
901 success = VacuumAfterSaveChanges(snapshot);
902 else
903 HandleSaveChangesFailure(snapshot);
904 return success;
905 }
906
907 bool Directory::VacuumAfterSaveChanges(const SaveChangesSnapshot& snapshot) {
908 if (snapshot.dirty_metas.empty())
909 return true;
910
911 // Need a write transaction as we are about to permanently purge entries.
912 WriteTransaction trans(FROM_HERE, VACUUM_AFTER_SAVE, this);
913 ScopedKernelLock lock(this);
914 // Now drop everything we can out of memory.
915 for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin();
916 i != snapshot.dirty_metas.end(); ++i) {
917 kernel_->needle.put(META_HANDLE, i->ref(META_HANDLE));
918 MetahandlesIndex::iterator found =
919 kernel_->metahandles_index->find(&kernel_->needle);
920 EntryKernel* entry = (found == kernel_->metahandles_index->end() ?
921 NULL : *found);
922 if (entry && SafeToPurgeFromMemory(&trans, entry)) {
923 // We now drop deleted metahandles that are up to date on both the client
924 // and the server.
925 size_t num_erased = 0;
926 num_erased = kernel_->ids_index->erase(entry);
927 DCHECK_EQ(1u, num_erased);
928 num_erased = kernel_->metahandles_index->erase(entry);
929 DCHECK_EQ(1u, num_erased);
930
931 // Might not be in it
932 num_erased = kernel_->client_tag_index->erase(entry);
933 DCHECK_EQ(entry->ref(UNIQUE_CLIENT_TAG).empty(), !num_erased);
934 if (!SyncAssert(!kernel_->parent_id_child_index->count(entry),
935 FROM_HERE,
936 "Deleted entry still present",
937 (&trans)))
938 return false;
939 delete entry;
940 }
941 if (trans.unrecoverable_error_set())
942 return false;
943 }
944 return true;
945 }
946
947 void Directory::PurgeEntriesWithTypeIn(ModelTypeSet types) {
948 if (types.Empty())
949 return;
950
951 {
952 WriteTransaction trans(FROM_HERE, PURGE_ENTRIES, this);
953 {
954 ScopedKernelLock lock(this);
955 MetahandlesIndex::iterator it = kernel_->metahandles_index->begin();
956 while (it != kernel_->metahandles_index->end()) {
957 const sync_pb::EntitySpecifics& local_specifics = (*it)->ref(SPECIFICS);
958 const sync_pb::EntitySpecifics& server_specifics =
959 (*it)->ref(SERVER_SPECIFICS);
960 ModelType local_type = GetModelTypeFromSpecifics(local_specifics);
961 ModelType server_type = GetModelTypeFromSpecifics(server_specifics);
962
963 // Note the dance around incrementing |it|, since we sometimes erase().
964 if ((IsRealDataType(local_type) && types.Has(local_type)) ||
965 (IsRealDataType(server_type) && types.Has(server_type))) {
966 if (!UnlinkEntryFromOrder(*it, NULL, &lock))
967 return;
968
969 int64 handle = (*it)->ref(META_HANDLE);
970 kernel_->metahandles_to_purge->insert(handle);
971
972 size_t num_erased = 0;
973 EntryKernel* entry = *it;
974 num_erased = kernel_->ids_index->erase(entry);
975 DCHECK_EQ(1u, num_erased);
976 num_erased = kernel_->client_tag_index->erase(entry);
977 DCHECK_EQ(entry->ref(UNIQUE_CLIENT_TAG).empty(), !num_erased);
978 num_erased = kernel_->unsynced_metahandles->erase(handle);
979 DCHECK_EQ(entry->ref(IS_UNSYNCED), num_erased > 0);
980 num_erased =
981 kernel_->unapplied_update_metahandles[server_type].erase(handle);
982 DCHECK_EQ(entry->ref(IS_UNAPPLIED_UPDATE), num_erased > 0);
983 num_erased = kernel_->parent_id_child_index->erase(entry);
984 DCHECK_EQ(entry->ref(IS_DEL), !num_erased);
985 kernel_->metahandles_index->erase(it++);
986 delete entry;
987 } else {
988 ++it;
989 }
990 }
991
992 // Ensure meta tracking for these data types reflects the deleted state.
993 for (syncable::ModelTypeSet::Iterator it = types.First();
994 it.Good(); it.Inc()) {
995 set_initial_sync_ended_for_type_unsafe(it.Get(), false);
996 kernel_->persisted_info.reset_download_progress(it.Get());
997 }
998 }
999 }
1000 }
1001
1002 void Directory::HandleSaveChangesFailure(const SaveChangesSnapshot& snapshot) {
1003 ScopedKernelLock lock(this);
1004 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
1005
1006 // Because we optimistically cleared the dirty bit on the real entries when
1007 // taking the snapshot, we must restore it on failure. Not doing this could
1008 // cause lost data, if no other changes are made to the in-memory entries
1009 // that would cause the dirty bit to get set again. Setting the bit ensures
1010 // that SaveChanges will at least try again later.
1011 for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin();
1012 i != snapshot.dirty_metas.end(); ++i) {
1013 kernel_->needle.put(META_HANDLE, i->ref(META_HANDLE));
1014 MetahandlesIndex::iterator found =
1015 kernel_->metahandles_index->find(&kernel_->needle);
1016 if (found != kernel_->metahandles_index->end()) {
1017 (*found)->mark_dirty(kernel_->dirty_metahandles);
1018 }
1019 }
1020
1021 kernel_->metahandles_to_purge->insert(snapshot.metahandles_to_purge.begin(),
1022 snapshot.metahandles_to_purge.end());
1023 }
1024
1025 void Directory::GetDownloadProgress(
1026 ModelType model_type,
1027 sync_pb::DataTypeProgressMarker* value_out) const {
1028 ScopedKernelLock lock(this);
1029 return value_out->CopyFrom(
1030 kernel_->persisted_info.download_progress[model_type]);
1031 }
1032
1033 void Directory::GetDownloadProgressAsString(
1034 ModelType model_type,
1035 std::string* value_out) const {
1036 ScopedKernelLock lock(this);
1037 kernel_->persisted_info.download_progress[model_type].SerializeToString(
1038 value_out);
1039 }
1040
1041 size_t Directory::GetEntriesCount() const {
1042 ScopedKernelLock lock(this);
1043 return kernel_->metahandles_index ? kernel_->metahandles_index->size() : 0;
1044 }
1045
1046 void Directory::SetDownloadProgress(
1047 ModelType model_type,
1048 const sync_pb::DataTypeProgressMarker& new_progress) {
1049 ScopedKernelLock lock(this);
1050 kernel_->persisted_info.download_progress[model_type].CopyFrom(new_progress);
1051 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
1052 }
1053
1054 bool Directory::initial_sync_ended_for_type(ModelType type) const {
1055 ScopedKernelLock lock(this);
1056 return kernel_->persisted_info.initial_sync_ended.Has(type);
1057 }
1058
1059 template <class T> void Directory::TestAndSet(
1060 T* kernel_data, const T* data_to_set) {
1061 if (*kernel_data != *data_to_set) {
1062 *kernel_data = *data_to_set;
1063 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
1064 }
1065 }
1066
1067 void Directory::set_initial_sync_ended_for_type(ModelType type, bool x) {
1068 ScopedKernelLock lock(this);
1069 set_initial_sync_ended_for_type_unsafe(type, x);
1070 }
1071
1072 void Directory::set_initial_sync_ended_for_type_unsafe(ModelType type,
1073 bool x) {
1074 if (kernel_->persisted_info.initial_sync_ended.Has(type) == x)
1075 return;
1076 if (x) {
1077 kernel_->persisted_info.initial_sync_ended.Put(type);
1078 } else {
1079 kernel_->persisted_info.initial_sync_ended.Remove(type);
1080 }
1081 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
1082 }
1083
1084 void Directory::SetNotificationStateUnsafe(
1085 const std::string& notification_state) {
1086 if (notification_state == kernel_->persisted_info.notification_state)
1087 return;
1088 kernel_->persisted_info.notification_state = notification_state;
1089 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
1090 }
1091
1092 string Directory::store_birthday() const {
1093 ScopedKernelLock lock(this);
1094 return kernel_->persisted_info.store_birthday;
1095 }
1096
1097 void Directory::set_store_birthday(const string& store_birthday) {
1098 ScopedKernelLock lock(this);
1099 if (kernel_->persisted_info.store_birthday == store_birthday)
1100 return;
1101 kernel_->persisted_info.store_birthday = store_birthday;
1102 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
1103 }
1104
1105 std::string Directory::GetNotificationState() const {
1106 ScopedKernelLock lock(this);
1107 std::string notification_state = kernel_->persisted_info.notification_state;
1108 return notification_state;
1109 }
1110
1111 void Directory::SetNotificationState(const std::string& notification_state) {
1112 ScopedKernelLock lock(this);
1113 SetNotificationStateUnsafe(notification_state);
1114 }
1115
1116 string Directory::cache_guid() const {
1117 // No need to lock since nothing ever writes to it after load.
1118 return kernel_->cache_guid;
1119 }
1120
1121 browser_sync::Cryptographer* Directory::GetCryptographer(
1122 const BaseTransaction* trans) {
1123 DCHECK_EQ(this, trans->directory());
1124 return &cryptographer_;
1125 }
1126
1127 void Directory::GetAllMetaHandles(BaseTransaction* trans,
1128 MetahandleSet* result) {
1129 result->clear();
1130 ScopedKernelLock lock(this);
1131 MetahandlesIndex::iterator i;
1132 for (i = kernel_->metahandles_index->begin();
1133 i != kernel_->metahandles_index->end();
1134 ++i) {
1135 result->insert((*i)->ref(META_HANDLE));
1136 }
1137 }
1138
1139 void Directory::GetAllEntryKernels(BaseTransaction* trans,
1140 std::vector<const EntryKernel*>* result) {
1141 result->clear();
1142 ScopedKernelLock lock(this);
1143 result->insert(result->end(),
1144 kernel_->metahandles_index->begin(),
1145 kernel_->metahandles_index->end());
1146 }
1147
1148 void Directory::GetUnsyncedMetaHandles(BaseTransaction* trans,
1149 UnsyncedMetaHandles* result) {
1150 result->clear();
1151 ScopedKernelLock lock(this);
1152 copy(kernel_->unsynced_metahandles->begin(),
1153 kernel_->unsynced_metahandles->end(), back_inserter(*result));
1154 }
1155
1156 int64 Directory::unsynced_entity_count() const {
1157 ScopedKernelLock lock(this);
1158 return kernel_->unsynced_metahandles->size();
1159 }
1160
1161 FullModelTypeSet Directory::GetServerTypesWithUnappliedUpdates(
1162 BaseTransaction* trans) const {
1163 syncable::FullModelTypeSet server_types;
1164 ScopedKernelLock lock(this);
1165 for (int i = UNSPECIFIED; i < MODEL_TYPE_COUNT; ++i) {
1166 const ModelType type = ModelTypeFromInt(i);
1167 if (!kernel_->unapplied_update_metahandles[type].empty()) {
1168 server_types.Put(type);
1169 }
1170 }
1171 return server_types;
1172 }
1173
1174 void Directory::GetUnappliedUpdateMetaHandles(
1175 BaseTransaction* trans,
1176 FullModelTypeSet server_types,
1177 UnappliedUpdateMetaHandles* result) {
1178 result->clear();
1179 ScopedKernelLock lock(this);
1180 for (int i = UNSPECIFIED; i < MODEL_TYPE_COUNT; ++i) {
1181 const ModelType type = ModelTypeFromInt(i);
1182 if (server_types.Has(type)) {
1183 std::copy(kernel_->unapplied_update_metahandles[type].begin(),
1184 kernel_->unapplied_update_metahandles[type].end(),
1185 back_inserter(*result));
1186 }
1187 }
1188 }
1189
1190
1191 class IdFilter {
1192 public:
1193 virtual ~IdFilter() { }
1194 virtual bool ShouldConsider(const Id& id) const = 0;
1195 };
1196
1197
1198 class FullScanFilter : public IdFilter {
1199 public:
1200 virtual bool ShouldConsider(const Id& id) const {
1201 return true;
1202 }
1203 };
1204
1205 class SomeIdsFilter : public IdFilter {
1206 public:
1207 virtual bool ShouldConsider(const Id& id) const {
1208 return std::binary_search(ids_.begin(), ids_.end(), id);
1209 }
1210 std::vector<Id> ids_;
1211 };
1212
1213 bool Directory::CheckTreeInvariants(syncable::BaseTransaction* trans,
1214 const EntryKernelMutationMap& mutations) {
1215 MetahandleSet handles;
1216 SomeIdsFilter filter;
1217 filter.ids_.reserve(mutations.size());
1218 for (EntryKernelMutationMap::const_iterator it = mutations.begin(),
1219 end = mutations.end(); it != end; ++it) {
1220 filter.ids_.push_back(it->second.mutated.ref(ID));
1221 handles.insert(it->first);
1222 }
1223 std::sort(filter.ids_.begin(), filter.ids_.end());
1224 if (!CheckTreeInvariants(trans, handles, filter))
1225 return false;
1226 return true;
1227 }
1228
1229 bool Directory::CheckTreeInvariants(syncable::BaseTransaction* trans,
1230 bool full_scan) {
1231 // TODO(timsteele): This is called every time a WriteTransaction finishes.
1232 // The performance hit is substantial given that we now examine every single
1233 // syncable entry. Need to redesign this.
1234 MetahandleSet handles;
1235 GetAllMetaHandles(trans, &handles);
1236 if (full_scan) {
1237 FullScanFilter fullfilter;
1238 if (!CheckTreeInvariants(trans, handles, fullfilter))
1239 return false;
1240 } else {
1241 SomeIdsFilter filter;
1242 MetahandleSet::iterator i;
1243 for (i = handles.begin() ; i != handles.end() ; ++i) {
1244 Entry e(trans, GET_BY_HANDLE, *i);
1245 if (!SyncAssert(e.good(), FROM_HERE, "Entry is bad", trans))
1246 return false;
1247 filter.ids_.push_back(e.Get(ID));
1248 }
1249 std::sort(filter.ids_.begin(), filter.ids_.end());
1250 if (!CheckTreeInvariants(trans, handles, filter))
1251 return false;
1252 }
1253 return true;
1254 }
1255
1256 bool Directory::CheckTreeInvariants(syncable::BaseTransaction* trans,
1257 const MetahandleSet& handles,
1258 const IdFilter& idfilter) {
1259 const int64 max_ms = kInvariantCheckMaxMs;
1260 PerfTimer check_timer;
1261 MetahandleSet::const_iterator i;
1262 int entries_done = 0;
1263 for (i = handles.begin() ; i != handles.end() ; ++i) {
1264 int64 metahandle = *i;
1265 Entry e(trans, GET_BY_HANDLE, metahandle);
1266 if (!SyncAssert(e.good(), FROM_HERE, "Entry is bad", trans))
1267 return false;
1268 syncable::Id id = e.Get(ID);
1269 syncable::Id parentid = e.Get(PARENT_ID);
1270
1271 if (id.IsRoot()) {
1272 if (!SyncAssert(e.Get(IS_DIR), FROM_HERE,
1273 "Entry should be a directory",
1274 trans))
1275 return false;
1276 if (!SyncAssert(parentid.IsRoot(), FROM_HERE,
1277 "Entry should be root",
1278 trans))
1279 return false;
1280 if (!SyncAssert(!e.Get(IS_UNSYNCED), FROM_HERE,
1281 "Entry should be sycned",
1282 trans))
1283 return false;
1284 ++entries_done;
1285 continue;
1286 }
1287
1288 if (!e.Get(IS_DEL)) {
1289 if (!SyncAssert(id != parentid, FROM_HERE,
1290 "Id should be different from parent id.",
1291 trans))
1292 return false;
1293 if (!SyncAssert(!e.Get(NON_UNIQUE_NAME).empty(), FROM_HERE,
1294 "Non unique name should not be empty.",
1295 trans))
1296 return false;
1297 int safety_count = handles.size() + 1;
1298 while (!parentid.IsRoot()) {
1299 if (!idfilter.ShouldConsider(parentid))
1300 break;
1301 Entry parent(trans, GET_BY_ID, parentid);
1302 if (!SyncAssert(parent.good(), FROM_HERE,
1303 "Parent entry is not valid.",
1304 trans))
1305 return false;
1306 if (!SyncAssert(parent.Get(IS_DIR), FROM_HERE,
1307 "Parent should be a directory",
1308 trans))
1309 return false;
1310 if (!SyncAssert(!parent.Get(IS_DEL), FROM_HERE,
1311 "Parent should not have been marked for deletion.",
1312 trans))
1313 return false;
1314 if (!SyncAssert(handles.end() != handles.find(parent.Get(META_HANDLE)),
1315 FROM_HERE,
1316 "Parent should be in the index.",
1317 trans))
1318 return false;
1319 parentid = parent.Get(PARENT_ID);
1320 if (!SyncAssert(--safety_count > 0, FROM_HERE,
1321 "Count should be greater than zero.",
1322 trans))
1323 return false;
1324 }
1325 }
1326 int64 base_version = e.Get(BASE_VERSION);
1327 int64 server_version = e.Get(SERVER_VERSION);
1328 bool using_unique_client_tag = !e.Get(UNIQUE_CLIENT_TAG).empty();
1329 if (CHANGES_VERSION == base_version || 0 == base_version) {
1330 if (e.Get(IS_UNAPPLIED_UPDATE)) {
1331 // Must be a new item, or a de-duplicated unique client tag
1332 // that was created both locally and remotely.
1333 if (!using_unique_client_tag) {
1334 if (!SyncAssert(e.Get(IS_DEL), FROM_HERE,
1335 "The entry should not have been deleted.",
1336 trans))
1337 return false;
1338 }
1339 // It came from the server, so it must have a server ID.
1340 if (!SyncAssert(id.ServerKnows(), FROM_HERE,
1341 "The id should be from a server.",
1342 trans))
1343 return false;
1344 } else {
1345 if (e.Get(IS_DIR)) {
1346 // TODO(chron): Implement this mode if clients ever need it.
1347 // For now, you can't combine a client tag and a directory.
1348 if (!SyncAssert(!using_unique_client_tag, FROM_HERE,
1349 "Directory cannot have a client tag.",
1350 trans))
1351 return false;
1352 }
1353 // Should be an uncomitted item, or a successfully deleted one.
1354 if (!e.Get(IS_DEL)) {
1355 if (!SyncAssert(e.Get(IS_UNSYNCED), FROM_HERE,
1356 "The item should be unsynced.",
1357 trans))
1358 return false;
1359 }
1360 // If the next check failed, it would imply that an item exists
1361 // on the server, isn't waiting for application locally, but either
1362 // is an unsynced create or a sucessful delete in the local copy.
1363 // Either way, that's a mismatch.
1364 if (!SyncAssert(0 == server_version, FROM_HERE,
1365 "Server version should be zero.",
1366 trans))
1367 return false;
1368 // Items that aren't using the unique client tag should have a zero
1369 // base version only if they have a local ID. Items with unique client
1370 // tags are allowed to use the zero base version for undeletion and
1371 // de-duplication; the unique client tag trumps the server ID.
1372 if (!using_unique_client_tag) {
1373 if (!SyncAssert(!id.ServerKnows(), FROM_HERE,
1374 "Should be a client only id.",
1375 trans))
1376 return false;
1377 }
1378 }
1379 } else {
1380 if (!SyncAssert(id.ServerKnows(),
1381 FROM_HERE,
1382 "Should be a server id.",
1383 trans))
1384 return false;
1385 }
1386 ++entries_done;
1387 int64 elapsed_ms = check_timer.Elapsed().InMilliseconds();
1388 if (elapsed_ms > max_ms) {
1389 DVLOG(1) << "Cutting Invariant check short after " << elapsed_ms
1390 << "ms. Processed " << entries_done << "/" << handles.size()
1391 << " entries";
1392 return true;
1393 }
1394
1395 }
1396 return true;
1397 }
1398
1399 ///////////////////////////////////////////////////////////////////////////////
1400 // ScopedKernelLock
1401
1402 ScopedKernelLock::ScopedKernelLock(const Directory* dir)
1403 : scoped_lock_(dir->kernel_->mutex), dir_(const_cast<Directory*>(dir)) {
1404 }
1405
1406 ///////////////////////////////////////////////////////////////////////////
1407 // Transactions
1408
1409 void BaseTransaction::Lock() {
1410 TRACE_EVENT2("sync_lock_contention", "AcquireLock",
1411 "src_file", from_here_.file_name(),
1412 "src_func", from_here_.function_name());
1413
1414 dirkernel_->transaction_mutex.Acquire();
1415 }
1416
1417 void BaseTransaction::Unlock() {
1418 dirkernel_->transaction_mutex.Release();
1419 }
1420
1421 void BaseTransaction::OnUnrecoverableError(
1422 const tracked_objects::Location& location,
1423 const std::string& message) {
1424 unrecoverable_error_set_ = true;
1425 unrecoverable_error_location_ = location;
1426 unrecoverable_error_msg_ = message;
1427
1428 // Note: We dont call the Directory's OnUnrecoverableError method right
1429 // away. Instead we wait to unwind the stack and in the destructor of the
1430 // transaction we would call the OnUnrecoverableError method.
1431
1432 directory()->ReportUnrecoverableError();
1433 }
1434
1435 bool BaseTransaction::unrecoverable_error_set() const {
1436 return unrecoverable_error_set_;
1437 }
1438
1439 void BaseTransaction::HandleUnrecoverableErrorIfSet() {
1440 if (unrecoverable_error_set_) {
1441 directory()->OnUnrecoverableError(this,
1442 unrecoverable_error_location_,
1443 unrecoverable_error_msg_);
1444 }
1445 }
1446
1447 BaseTransaction::BaseTransaction(const tracked_objects::Location& from_here,
1448 const char* name,
1449 WriterTag writer,
1450 Directory* directory)
1451 : from_here_(from_here), name_(name), writer_(writer),
1452 directory_(directory), dirkernel_(directory->kernel_),
1453 unrecoverable_error_set_(false) {
1454 // TODO(lipalani): Don't issue a good transaction if the directory has
1455 // unrecoverable error set. And the callers have to check trans.good before
1456 // proceeding.
1457 TRACE_EVENT_BEGIN2("sync", name_,
1458 "src_file", from_here_.file_name(),
1459 "src_func", from_here_.function_name());
1460 }
1461
1462 BaseTransaction::~BaseTransaction() {
1463 TRACE_EVENT_END0("sync", name_);
1464 }
1465
1466 ReadTransaction::ReadTransaction(const tracked_objects::Location& location,
1467 Directory* directory)
1468 : BaseTransaction(location, "ReadTransaction", INVALID, directory) {
1469 Lock();
1470 }
1471
1472 ReadTransaction::~ReadTransaction() {
1473 HandleUnrecoverableErrorIfSet();
1474 Unlock();
1475 }
1476
1477 WriteTransaction::WriteTransaction(const tracked_objects::Location& location,
1478 WriterTag writer, Directory* directory)
1479 : BaseTransaction(location, "WriteTransaction", writer, directory) {
1480 Lock();
1481 }
1482
1483 void WriteTransaction::SaveOriginal(const EntryKernel* entry) {
1484 if (!entry) {
1485 return;
1486 }
1487 // Insert only if it's not already there.
1488 const int64 handle = entry->ref(META_HANDLE);
1489 EntryKernelMutationMap::iterator it = mutations_.lower_bound(handle);
1490 if (it == mutations_.end() || it->first != handle) {
1491 EntryKernelMutation mutation;
1492 mutation.original = *entry;
1493 ignore_result(mutations_.insert(it, std::make_pair(handle, mutation)));
1494 }
1495 }
1496
1497 ImmutableEntryKernelMutationMap WriteTransaction::RecordMutations() {
1498 dirkernel_->transaction_mutex.AssertAcquired();
1499 for (syncable::EntryKernelMutationMap::iterator it = mutations_.begin();
1500 it != mutations_.end();) {
1501 EntryKernel* kernel = directory()->GetEntryByHandle(it->first);
1502 if (!kernel) {
1503 NOTREACHED();
1504 continue;
1505 }
1506 if (kernel->is_dirty()) {
1507 it->second.mutated = *kernel;
1508 ++it;
1509 } else {
1510 DCHECK(!it->second.original.is_dirty());
1511 // Not actually mutated, so erase from |mutations_|.
1512 mutations_.erase(it++);
1513 }
1514 }
1515 return ImmutableEntryKernelMutationMap(&mutations_);
1516 }
1517
1518 void WriteTransaction::UnlockAndNotify(
1519 const ImmutableEntryKernelMutationMap& mutations) {
1520 // Work while transaction mutex is held.
1521 ModelTypeSet models_with_changes;
1522 bool has_mutations = !mutations.Get().empty();
1523 if (has_mutations) {
1524 models_with_changes = NotifyTransactionChangingAndEnding(mutations);
1525 }
1526 Unlock();
1527
1528 // Work after mutex is relased.
1529 if (has_mutations) {
1530 NotifyTransactionComplete(models_with_changes);
1531 }
1532 }
1533
1534 ModelTypeSet WriteTransaction::NotifyTransactionChangingAndEnding(
1535 const ImmutableEntryKernelMutationMap& mutations) {
1536 dirkernel_->transaction_mutex.AssertAcquired();
1537 DCHECK(!mutations.Get().empty());
1538
1539 WriteTransactionInfo write_transaction_info(
1540 dirkernel_->next_write_transaction_id, from_here_, writer_, mutations);
1541 ++dirkernel_->next_write_transaction_id;
1542
1543 ImmutableWriteTransactionInfo immutable_write_transaction_info(
1544 &write_transaction_info);
1545 DirectoryChangeDelegate* const delegate = dirkernel_->delegate;
1546 if (writer_ == syncable::SYNCAPI) {
1547 delegate->HandleCalculateChangesChangeEventFromSyncApi(
1548 immutable_write_transaction_info, this);
1549 } else {
1550 delegate->HandleCalculateChangesChangeEventFromSyncer(
1551 immutable_write_transaction_info, this);
1552 }
1553
1554 ModelTypeSet models_with_changes =
1555 delegate->HandleTransactionEndingChangeEvent(
1556 immutable_write_transaction_info, this);
1557
1558 dirkernel_->transaction_observer.Call(FROM_HERE,
1559 &TransactionObserver::OnTransactionWrite,
1560 immutable_write_transaction_info, models_with_changes);
1561
1562 return models_with_changes;
1563 }
1564
1565 void WriteTransaction::NotifyTransactionComplete(
1566 ModelTypeSet models_with_changes) {
1567 dirkernel_->delegate->HandleTransactionCompleteChangeEvent(
1568 models_with_changes);
1569 }
1570
1571 WriteTransaction::~WriteTransaction() {
1572 const ImmutableEntryKernelMutationMap& mutations = RecordMutations();
1573
1574 if (!unrecoverable_error_set_) {
1575 if (OFF != kInvariantCheckLevel) {
1576 const bool full_scan = (FULL_DB_VERIFICATION == kInvariantCheckLevel);
1577 if (full_scan)
1578 directory()->CheckTreeInvariants(this, full_scan);
1579 else
1580 directory()->CheckTreeInvariants(this, mutations.Get());
1581 }
1582 }
1583
1584 // |CheckTreeInvariants| could have thrown an unrecoverable error.
1585 if (unrecoverable_error_set_) {
1586 HandleUnrecoverableErrorIfSet();
1587 Unlock();
1588 return;
1589 }
1590
1591 UnlockAndNotify(mutations);
1592 }
1593
1594 ///////////////////////////////////////////////////////////////////////////
1595 // Entry
1596
1597 Entry::Entry(BaseTransaction* trans, GetById, const Id& id)
1598 : basetrans_(trans) {
1599 kernel_ = trans->directory()->GetEntryById(id);
1600 }
1601
1602 Entry::Entry(BaseTransaction* trans, GetByClientTag, const string& tag)
1603 : basetrans_(trans) {
1604 kernel_ = trans->directory()->GetEntryByClientTag(tag);
1605 }
1606
1607 Entry::Entry(BaseTransaction* trans, GetByServerTag, const string& tag)
1608 : basetrans_(trans) {
1609 kernel_ = trans->directory()->GetEntryByServerTag(tag);
1610 }
1611
1612 Entry::Entry(BaseTransaction* trans, GetByHandle, int64 metahandle)
1613 : basetrans_(trans) {
1614 kernel_ = trans->directory()->GetEntryByHandle(metahandle);
1615 }
1616
1617 Directory* Entry::dir() const {
1618 return basetrans_->directory();
1619 }
1620
1621 Id Entry::ComputePrevIdFromServerPosition(const Id& parent_id) const {
1622 return dir()->ComputePrevIdFromServerPosition(kernel_, parent_id);
1623 }
1624
1625 DictionaryValue* Entry::ToValue() const {
1626 DictionaryValue* entry_info = new DictionaryValue();
1627 entry_info->SetBoolean("good", good());
1628 if (good()) {
1629 entry_info->Set("kernel", kernel_->ToValue());
1630 entry_info->Set("modelType",
1631 ModelTypeToValue(GetModelType()));
1632 entry_info->SetBoolean("existsOnClientBecauseNameIsNonEmpty",
1633 ExistsOnClientBecauseNameIsNonEmpty());
1634 entry_info->SetBoolean("isRoot", IsRoot());
1635 }
1636 return entry_info;
1637 }
1638
1639 const string& Entry::Get(StringField field) const {
1640 DCHECK(kernel_);
1641 return kernel_->ref(field);
1642 }
1643
1644 syncable::ModelType Entry::GetServerModelType() const {
1645 ModelType specifics_type = kernel_->GetServerModelType();
1646 if (specifics_type != UNSPECIFIED)
1647 return specifics_type;
1648
1649 // Otherwise, we don't have a server type yet. That should only happen
1650 // if the item is an uncommitted locally created item.
1651 // It's possible we'll need to relax these checks in the future; they're
1652 // just here for now as a safety measure.
1653 DCHECK(Get(IS_UNSYNCED));
1654 DCHECK_EQ(Get(SERVER_VERSION), 0);
1655 DCHECK(Get(SERVER_IS_DEL));
1656 // Note: can't enforce !Get(ID).ServerKnows() here because that could
1657 // actually happen if we hit AttemptReuniteLostCommitResponses.
1658 return UNSPECIFIED;
1659 }
1660
1661 syncable::ModelType Entry::GetModelType() const {
1662 ModelType specifics_type = GetModelTypeFromSpecifics(Get(SPECIFICS));
1663 if (specifics_type != UNSPECIFIED)
1664 return specifics_type;
1665 if (IsRoot())
1666 return TOP_LEVEL_FOLDER;
1667 // Loose check for server-created top-level folders that aren't
1668 // bound to a particular model type.
1669 if (!Get(UNIQUE_SERVER_TAG).empty() && Get(IS_DIR))
1670 return TOP_LEVEL_FOLDER;
1671
1672 return UNSPECIFIED;
1673 }
1674
1675 ///////////////////////////////////////////////////////////////////////////
1676 // MutableEntry
1677
1678 MutableEntry::MutableEntry(WriteTransaction* trans, Create,
1679 const Id& parent_id, const string& name)
1680 : Entry(trans),
1681 write_transaction_(trans) {
1682 Init(trans, parent_id, name);
1683 }
1684
1685
1686 void MutableEntry::Init(WriteTransaction* trans, const Id& parent_id,
1687 const string& name) {
1688 scoped_ptr<EntryKernel> kernel(new EntryKernel);
1689 kernel_ = NULL;
1690
1691 kernel->put(ID, trans->directory_->NextId());
1692 kernel->put(META_HANDLE, trans->directory_->NextMetahandle());
1693 kernel->mark_dirty(trans->directory_->kernel_->dirty_metahandles);
1694 kernel->put(PARENT_ID, parent_id);
1695 kernel->put(NON_UNIQUE_NAME, name);
1696 const base::Time& now = base::Time::Now();
1697 kernel->put(CTIME, now);
1698 kernel->put(MTIME, now);
1699 // We match the database defaults here
1700 kernel->put(BASE_VERSION, CHANGES_VERSION);
1701 if (!trans->directory()->InsertEntry(trans, kernel.get())) {
1702 return; // We failed inserting, nothing more to do.
1703 }
1704 // Because this entry is new, it was originally deleted.
1705 kernel->put(IS_DEL, true);
1706 trans->SaveOriginal(kernel.get());
1707 kernel->put(IS_DEL, false);
1708
1709 // Now swap the pointers.
1710 kernel_ = kernel.release();
1711 }
1712
1713 MutableEntry::MutableEntry(WriteTransaction* trans, CreateNewUpdateItem,
1714 const Id& id)
1715 : Entry(trans), write_transaction_(trans) {
1716 Entry same_id(trans, GET_BY_ID, id);
1717 kernel_ = NULL;
1718 if (same_id.good()) {
1719 return; // already have an item with this ID.
1720 }
1721 scoped_ptr<EntryKernel> kernel(new EntryKernel());
1722
1723 kernel->put(ID, id);
1724 kernel->put(META_HANDLE, trans->directory_->NextMetahandle());
1725 kernel->mark_dirty(trans->directory_->kernel_->dirty_metahandles);
1726 kernel->put(IS_DEL, true);
1727 // We match the database defaults here
1728 kernel->put(BASE_VERSION, CHANGES_VERSION);
1729 if (!trans->directory()->InsertEntry(trans, kernel.get())) {
1730 return; // Failed inserting.
1731 }
1732 trans->SaveOriginal(kernel.get());
1733
1734 kernel_ = kernel.release();
1735 }
1736
1737 MutableEntry::MutableEntry(WriteTransaction* trans, GetById, const Id& id)
1738 : Entry(trans, GET_BY_ID, id), write_transaction_(trans) {
1739 trans->SaveOriginal(kernel_);
1740 }
1741
1742 MutableEntry::MutableEntry(WriteTransaction* trans, GetByHandle,
1743 int64 metahandle)
1744 : Entry(trans, GET_BY_HANDLE, metahandle), write_transaction_(trans) {
1745 trans->SaveOriginal(kernel_);
1746 }
1747
1748 MutableEntry::MutableEntry(WriteTransaction* trans, GetByClientTag,
1749 const std::string& tag)
1750 : Entry(trans, GET_BY_CLIENT_TAG, tag), write_transaction_(trans) {
1751 trans->SaveOriginal(kernel_);
1752 }
1753
1754 MutableEntry::MutableEntry(WriteTransaction* trans, GetByServerTag,
1755 const string& tag)
1756 : Entry(trans, GET_BY_SERVER_TAG, tag), write_transaction_(trans) {
1757 trans->SaveOriginal(kernel_);
1758 }
1759
1760 bool MutableEntry::PutIsDel(bool is_del) {
1761 DCHECK(kernel_);
1762 if (is_del == kernel_->ref(IS_DEL)) {
1763 return true;
1764 }
1765 if (is_del) {
1766 if (!UnlinkFromOrder()) {
1767 return false;
1768 }
1769 }
1770
1771 {
1772 ScopedKernelLock lock(dir());
1773 // Some indices don't include deleted items and must be updated
1774 // upon a value change.
1775 ScopedIndexUpdater<ParentIdAndHandleIndexer> updater(lock, kernel_,
1776 dir()->kernel_->parent_id_child_index);
1777
1778 kernel_->put(IS_DEL, is_del);
1779 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
1780 }
1781
1782 if (!is_del)
1783 // Restores position to the 0th index.
1784 if (!PutPredecessor(Id())) {
1785 // TODO(lipalani) : Propagate the error to caller. crbug.com/100444.
1786 NOTREACHED();
1787 }
1788
1789 return true;
1790 }
1791
1792 bool MutableEntry::Put(Int64Field field, const int64& value) {
1793 DCHECK(kernel_);
1794 if (kernel_->ref(field) != value) {
1795 ScopedKernelLock lock(dir());
1796 if (SERVER_POSITION_IN_PARENT == field) {
1797 ScopedIndexUpdater<ParentIdAndHandleIndexer> updater(lock, kernel_,
1798 dir()->kernel_->parent_id_child_index);
1799 kernel_->put(field, value);
1800 } else {
1801 kernel_->put(field, value);
1802 }
1803 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
1804 }
1805 return true;
1806 }
1807
1808 bool MutableEntry::Put(TimeField field, const base::Time& value) {
1809 DCHECK(kernel_);
1810 if (kernel_->ref(field) != value) {
1811 kernel_->put(field, value);
1812 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
1813 }
1814 return true;
1815 }
1816
1817 bool MutableEntry::Put(IdField field, const Id& value) {
1818 DCHECK(kernel_);
1819 if (kernel_->ref(field) != value) {
1820 if (ID == field) {
1821 if (!dir()->ReindexId(write_transaction(), kernel_, value))
1822 return false;
1823 } else if (PARENT_ID == field) {
1824 PutParentIdPropertyOnly(value); // Makes sibling order inconsistent.
1825 // Fixes up the sibling order inconsistency.
1826 if (!PutPredecessor(Id())) {
1827 // TODO(lipalani) : Propagate the error to caller. crbug.com/100444.
1828 NOTREACHED();
1829 }
1830 } else {
1831 kernel_->put(field, value);
1832 }
1833 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
1834 }
1835 return true;
1836 }
1837
1838 void MutableEntry::PutParentIdPropertyOnly(const Id& parent_id) {
1839 dir()->ReindexParentId(write_transaction(), kernel_, parent_id);
1840 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
1841 }
1842
1843 bool MutableEntry::Put(BaseVersion field, int64 value) {
1844 DCHECK(kernel_);
1845 if (kernel_->ref(field) != value) {
1846 kernel_->put(field, value);
1847 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
1848 }
1849 return true;
1850 }
1851
1852 bool MutableEntry::Put(StringField field, const string& value) {
1853 return PutImpl(field, value);
1854 }
1855
1856 bool MutableEntry::Put(ProtoField field,
1857 const sync_pb::EntitySpecifics& value) {
1858 DCHECK(kernel_);
1859 // TODO(ncarter): This is unfortunately heavyweight. Can we do
1860 // better?
1861 if (kernel_->ref(field).SerializeAsString() != value.SerializeAsString()) {
1862 const bool update_unapplied_updates_index =
1863 (field == SERVER_SPECIFICS) && kernel_->ref(IS_UNAPPLIED_UPDATE);
1864 if (update_unapplied_updates_index) {
1865 // Remove ourselves from unapplied_update_metahandles with our
1866 // old server type.
1867 const syncable::ModelType old_server_type =
1868 kernel_->GetServerModelType();
1869 const int64 metahandle = kernel_->ref(META_HANDLE);
1870 size_t erase_count =
1871 dir()->kernel_->unapplied_update_metahandles[old_server_type]
1872 .erase(metahandle);
1873 DCHECK_EQ(erase_count, 1u);
1874 }
1875
1876 kernel_->put(field, value);
1877 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
1878
1879 if (update_unapplied_updates_index) {
1880 // Add ourselves back into unapplied_update_metahandles with our
1881 // new server type.
1882 const syncable::ModelType new_server_type =
1883 kernel_->GetServerModelType();
1884 const int64 metahandle = kernel_->ref(META_HANDLE);
1885 dir()->kernel_->unapplied_update_metahandles[new_server_type]
1886 .insert(metahandle);
1887 }
1888 }
1889 return true;
1890 }
1891
1892 bool MutableEntry::Put(BitField field, bool value) {
1893 DCHECK(kernel_);
1894 if (kernel_->ref(field) != value) {
1895 kernel_->put(field, value);
1896 kernel_->mark_dirty(GetDirtyIndexHelper());
1897 }
1898 return true;
1899 }
1900
1901 MetahandleSet* MutableEntry::GetDirtyIndexHelper() {
1902 return dir()->kernel_->dirty_metahandles;
1903 }
1904
1905 bool MutableEntry::PutUniqueClientTag(const string& new_tag) {
1906 // There is no SERVER_UNIQUE_CLIENT_TAG. This field is similar to ID.
1907 string old_tag = kernel_->ref(UNIQUE_CLIENT_TAG);
1908 if (old_tag == new_tag) {
1909 return true;
1910 }
1911
1912 ScopedKernelLock lock(dir());
1913 if (!new_tag.empty()) {
1914 // Make sure your new value is not in there already.
1915 EntryKernel lookup_kernel_ = *kernel_;
1916 lookup_kernel_.put(UNIQUE_CLIENT_TAG, new_tag);
1917 bool new_tag_conflicts =
1918 (dir()->kernel_->client_tag_index->count(&lookup_kernel_) > 0);
1919 if (new_tag_conflicts) {
1920 return false;
1921 }
1922 }
1923
1924 {
1925 ScopedIndexUpdater<ClientTagIndexer> index_updater(lock, kernel_,
1926 dir()->kernel_->client_tag_index);
1927 kernel_->put(UNIQUE_CLIENT_TAG, new_tag);
1928 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
1929 }
1930 return true;
1931 }
1932
1933 bool MutableEntry::PutImpl(StringField field, const string& value) {
1934 DCHECK(kernel_);
1935 if (field == UNIQUE_CLIENT_TAG) {
1936 return PutUniqueClientTag(value);
1937 }
1938
1939 if (kernel_->ref(field) != value) {
1940 kernel_->put(field, value);
1941 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
1942 }
1943 return true;
1944 }
1945
1946 bool MutableEntry::Put(IndexedBitField field, bool value) {
1947 DCHECK(kernel_);
1948 if (kernel_->ref(field) != value) {
1949 MetahandleSet* index;
1950 if (IS_UNSYNCED == field) {
1951 index = dir()->kernel_->unsynced_metahandles;
1952 } else {
1953 // Use kernel_->GetServerModelType() instead of
1954 // GetServerModelType() as we may trigger some DCHECKs in the
1955 // latter.
1956 index =
1957 &dir()->kernel_->unapplied_update_metahandles[
1958 kernel_->GetServerModelType()];
1959 }
1960
1961 ScopedKernelLock lock(dir());
1962 if (value) {
1963 if (!SyncAssert(index->insert(kernel_->ref(META_HANDLE)).second,
1964 FROM_HERE,
1965 "Could not insert",
1966 write_transaction())) {
1967 return false;
1968 }
1969 } else {
1970 if (!SyncAssert(1U == index->erase(kernel_->ref(META_HANDLE)),
1971 FROM_HERE,
1972 "Entry Not succesfully erased",
1973 write_transaction())) {
1974 return false;
1975 }
1976 }
1977 kernel_->put(field, value);
1978 kernel_->mark_dirty(dir()->kernel_->dirty_metahandles);
1979 }
1980 return true;
1981 }
1982
1983 bool MutableEntry::UnlinkFromOrder() {
1984 ScopedKernelLock lock(dir());
1985 return dir()->UnlinkEntryFromOrder(kernel_, write_transaction(), &lock);
1986 }
1987
1988 bool Directory::UnlinkEntryFromOrder(EntryKernel* entry,
1989 WriteTransaction* trans,
1990 ScopedKernelLock* lock) {
1991 if (!SyncAssert(!trans || this == trans->directory(),
1992 FROM_HERE,
1993 "Transaction not pointing to the right directory",
1994 trans))
1995 return false;
1996 Id old_previous = entry->ref(PREV_ID);
1997 Id old_next = entry->ref(NEXT_ID);
1998
1999 entry->put(NEXT_ID, entry->ref(ID));
2000 entry->put(PREV_ID, entry->ref(ID));
2001 entry->mark_dirty(kernel_->dirty_metahandles);
2002
2003 if (!old_previous.IsRoot()) {
2004 if (old_previous == old_next) {
2005 // Note previous == next doesn't imply previous == next == Get(ID). We
2006 // could have prev==next=="c-XX" and Get(ID)=="sX..." if an item was added
2007 // and deleted before receiving the server ID in the commit response.
2008 if (!SyncAssert(
2009 (old_next == entry->ref(ID)) || !old_next.ServerKnows(),
2010 FROM_HERE,
2011 "Encounteered inconsistent entry while deleting",
2012 trans)) {
2013 return false;
2014 }
2015 return true; // Done if we were already self-looped (hence unlinked).
2016 }
2017 EntryKernel* previous_entry = GetEntryById(old_previous, lock);
2018 ModelType type = GetModelTypeFromSpecifics(entry->ref(SPECIFICS));
2019 // TODO(tim): Multiple asserts here for bug 101039 investigation.
2020 if (type == AUTOFILL) {
2021 if (!SyncAssert(previous_entry != NULL,
2022 FROM_HERE,
2023 "Could not find previous autofill entry",
2024 trans)) {
2025 return false;
2026 }
2027 } else {
2028 if (!SyncAssert(previous_entry != NULL,
2029 FROM_HERE,
2030 "Could not find previous entry",
2031 trans)) {
2032 return false;
2033 }
2034 }
2035 if (trans)
2036 trans->SaveOriginal(previous_entry);
2037 previous_entry->put(NEXT_ID, old_next);
2038 previous_entry->mark_dirty(kernel_->dirty_metahandles);
2039 }
2040
2041 if (!old_next.IsRoot()) {
2042 EntryKernel* next_entry = GetEntryById(old_next, lock);
2043 if (!SyncAssert(next_entry != NULL,
2044 FROM_HERE,
2045 "Could not find next entry",
2046 trans)) {
2047 return false;
2048 }
2049 if (trans)
2050 trans->SaveOriginal(next_entry);
2051 next_entry->put(PREV_ID, old_previous);
2052 next_entry->mark_dirty(kernel_->dirty_metahandles);
2053 }
2054 return true;
2055 }
2056
2057 bool MutableEntry::PutPredecessor(const Id& predecessor_id) {
2058 if (!UnlinkFromOrder())
2059 return false;
2060
2061 if (Get(IS_DEL)) {
2062 DCHECK(predecessor_id.IsNull());
2063 return true;
2064 }
2065
2066 // TODO(ncarter): It should be possible to not maintain position for
2067 // non-bookmark items. However, we'd need to robustly handle all possible
2068 // permutations of setting IS_DEL and the SPECIFICS to identify the
2069 // object type; or else, we'd need to add a ModelType to the
2070 // MutableEntry's Create ctor.
2071 // if (!ShouldMaintainPosition()) {
2072 // return false;
2073 // }
2074
2075 // This is classic insert-into-doubly-linked-list from CS 101 and your last
2076 // job interview. An "IsRoot" Id signifies the head or tail.
2077 Id successor_id;
2078 if (!predecessor_id.IsRoot()) {
2079 MutableEntry predecessor(write_transaction(), GET_BY_ID, predecessor_id);
2080 if (!predecessor.good()) {
2081 LOG(ERROR) << "Predecessor is not good : "
2082 << predecessor_id.GetServerId();
2083 return false;
2084 }
2085 if (predecessor.Get(PARENT_ID) != Get(PARENT_ID))
2086 return false;
2087 successor_id = predecessor.Get(NEXT_ID);
2088 predecessor.Put(NEXT_ID, Get(ID));
2089 } else {
2090 syncable::Directory* dir = trans()->directory();
2091 if (!dir->GetFirstChildId(trans(), Get(PARENT_ID), &successor_id)) {
2092 return false;
2093 }
2094 }
2095 if (!successor_id.IsRoot()) {
2096 MutableEntry successor(write_transaction(), GET_BY_ID, successor_id);
2097 if (!successor.good()) {
2098 LOG(ERROR) << "Successor is not good: "
2099 << successor_id.GetServerId();
2100 return false;
2101 }
2102 if (successor.Get(PARENT_ID) != Get(PARENT_ID))
2103 return false;
2104 successor.Put(PREV_ID, Get(ID));
2105 }
2106 DCHECK(predecessor_id != Get(ID));
2107 DCHECK(successor_id != Get(ID));
2108 Put(PREV_ID, predecessor_id);
2109 Put(NEXT_ID, successor_id);
2110 return true;
2111 }
2112
2113 bool MutableEntry::Put(BitTemp field, bool value) {
2114 DCHECK(kernel_);
2115 kernel_->put(field, value);
2116 return true;
2117 }
2118
2119 ///////////////////////////////////////////////////////////////////////////
2120 // High-level functions
2121
2122 int64 Directory::NextMetahandle() {
2123 ScopedKernelLock lock(this);
2124 int64 metahandle = (kernel_->next_metahandle)++;
2125 return metahandle;
2126 }
2127
2128 // Always returns a client ID that is the string representation of a negative
2129 // number.
2130 Id Directory::NextId() {
2131 int64 result;
2132 {
2133 ScopedKernelLock lock(this);
2134 result = (kernel_->persisted_info.next_id)--;
2135 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY;
2136 }
2137 DCHECK_LT(result, 0);
2138 return Id::CreateFromClientString(base::Int64ToString(result));
2139 }
2140
2141 bool Directory::HasChildren(BaseTransaction* trans, const Id& id) {
2142 ScopedKernelLock lock(this);
2143 return (GetPossibleFirstChild(lock, id) != NULL);
2144 }
2145
2146 bool Directory::GetFirstChildId(BaseTransaction* trans,
2147 const Id& parent_id,
2148 Id* first_child_id) {
2149 ScopedKernelLock lock(this);
2150 EntryKernel* entry = GetPossibleFirstChild(lock, parent_id);
2151 if (!entry) {
2152 *first_child_id = Id();
2153 return true;
2154 }
2155
2156 // Walk to the front of the list; the server position ordering
2157 // is commonly identical to the linked-list ordering, but pending
2158 // unsynced or unapplied items may diverge.
2159 while (!entry->ref(PREV_ID).IsRoot()) {
2160 entry = GetEntryById(entry->ref(PREV_ID), &lock);
2161 if (!entry) {
2162 *first_child_id = Id();
2163 return false;
2164 }
2165 }
2166 *first_child_id = entry->ref(ID);
2167 return true;
2168 }
2169
2170 bool Directory::GetLastChildIdForTest(
2171 BaseTransaction* trans, const Id& parent_id, Id* last_child_id) {
2172 ScopedKernelLock lock(this);
2173 EntryKernel* entry = GetPossibleLastChildForTest(lock, parent_id);
2174 if (!entry) {
2175 *last_child_id = Id();
2176 return true;
2177 }
2178
2179 // Walk to the back of the list; the server position ordering
2180 // is commonly identical to the linked-list ordering, but pending
2181 // unsynced or unapplied items may diverge.
2182 while (!entry->ref(NEXT_ID).IsRoot()) {
2183 entry = GetEntryById(entry->ref(NEXT_ID), &lock);
2184 if (!entry) {
2185 *last_child_id = Id();
2186 return false;
2187 }
2188 }
2189
2190 *last_child_id = entry->ref(ID);
2191 return true;
2192 }
2193
2194 Id Directory::ComputePrevIdFromServerPosition(
2195 const EntryKernel* entry,
2196 const syncable::Id& parent_id) {
2197 ScopedKernelLock lock(this);
2198
2199 // Find the natural insertion point in the parent_id_child_index, and
2200 // work back from there, filtering out ineligible candidates.
2201 ParentIdChildIndex::iterator sibling = LocateInParentChildIndex(lock,
2202 parent_id, entry->ref(SERVER_POSITION_IN_PARENT), entry->ref(ID));
2203 ParentIdChildIndex::iterator first_sibling =
2204 GetParentChildIndexLowerBound(lock, parent_id);
2205
2206 while (sibling != first_sibling) {
2207 --sibling;
2208 EntryKernel* candidate = *sibling;
2209
2210 // The item itself should never be in the range under consideration.
2211 DCHECK_NE(candidate->ref(META_HANDLE), entry->ref(META_HANDLE));
2212
2213 // Ignore unapplied updates -- they might not even be server-siblings.
2214 if (candidate->ref(IS_UNAPPLIED_UPDATE))
2215 continue;
2216
2217 // We can't trust the SERVER_ fields of unsynced items, but they are
2218 // potentially legitimate local predecessors. In the case where
2219 // |update_item| and an unsynced item wind up in the same insertion
2220 // position, we need to choose how to order them. The following check puts
2221 // the unapplied update first; removing it would put the unsynced item(s)
2222 // first.
2223 if (candidate->ref(IS_UNSYNCED))
2224 continue;
2225
2226 // Skip over self-looped items, which are not valid predecessors. This
2227 // shouldn't happen in practice, but is worth defending against.
2228 if (candidate->ref(PREV_ID) == candidate->ref(NEXT_ID) &&
2229 !candidate->ref(PREV_ID).IsRoot()) {
2230 NOTREACHED();
2231 continue;
2232 }
2233 return candidate->ref(ID);
2234 }
2235 // This item will be the first in the sibling order.
2236 return Id();
2237 }
2238
2239 bool IsLegalNewParent(BaseTransaction* trans, const Id& entry_id,
2240 const Id& new_parent_id) {
2241 if (entry_id.IsRoot())
2242 return false;
2243 // we have to ensure that the entry is not an ancestor of the new parent.
2244 Id ancestor_id = new_parent_id;
2245 while (!ancestor_id.IsRoot()) {
2246 if (entry_id == ancestor_id)
2247 return false;
2248 Entry new_parent(trans, GET_BY_ID, ancestor_id);
2249 if (!SyncAssert(new_parent.good(),
2250 FROM_HERE,
2251 "Invalid new parent",
2252 trans))
2253 return false;
2254 ancestor_id = new_parent.Get(PARENT_ID);
2255 }
2256 return true;
2257 }
2258
2259 // This function sets only the flags needed to get this entry to sync.
2260 bool MarkForSyncing(syncable::MutableEntry* e) {
2261 DCHECK_NE(static_cast<MutableEntry*>(NULL), e);
2262 DCHECK(!e->IsRoot()) << "We shouldn't mark a permanent object for syncing.";
2263 if (!(e->Put(IS_UNSYNCED, true)))
2264 return false;
2265 e->Put(SYNCING, false);
2266 return true;
2267 }
2268
2269 std::ostream& operator<<(std::ostream& os, const Entry& entry) {
2270 int i;
2271 EntryKernel* const kernel = entry.kernel_;
2272 for (i = BEGIN_FIELDS; i < INT64_FIELDS_END; ++i) {
2273 os << g_metas_columns[i].name << ": "
2274 << kernel->ref(static_cast<Int64Field>(i)) << ", ";
2275 }
2276 for ( ; i < TIME_FIELDS_END; ++i) {
2277 os << g_metas_columns[i].name << ": "
2278 << browser_sync::GetTimeDebugString(
2279 kernel->ref(static_cast<TimeField>(i))) << ", ";
2280 }
2281 for ( ; i < ID_FIELDS_END; ++i) {
2282 os << g_metas_columns[i].name << ": "
2283 << kernel->ref(static_cast<IdField>(i)) << ", ";
2284 }
2285 os << "Flags: ";
2286 for ( ; i < BIT_FIELDS_END; ++i) {
2287 if (kernel->ref(static_cast<BitField>(i)))
2288 os << g_metas_columns[i].name << ", ";
2289 }
2290 for ( ; i < STRING_FIELDS_END; ++i) {
2291 const string& field = kernel->ref(static_cast<StringField>(i));
2292 os << g_metas_columns[i].name << ": " << field << ", ";
2293 }
2294 for ( ; i < PROTO_FIELDS_END; ++i) {
2295 os << g_metas_columns[i].name << ": "
2296 << net::EscapePath(
2297 kernel->ref(static_cast<ProtoField>(i)).SerializeAsString())
2298 << ", ";
2299 }
2300 os << "TempFlags: ";
2301 for ( ; i < BIT_TEMPS_END; ++i) {
2302 if (kernel->ref(static_cast<BitTemp>(i)))
2303 os << "#" << i - BIT_TEMPS_BEGIN << ", ";
2304 }
2305 return os;
2306 }
2307
2308 std::ostream& operator<<(std::ostream& s, const Blob& blob) {
2309 for (Blob::const_iterator i = blob.begin(); i != blob.end(); ++i)
2310 s << std::hex << std::setw(2)
2311 << std::setfill('0') << static_cast<unsigned int>(*i);
2312 return s << std::dec;
2313 }
2314
2315 Directory::ParentIdChildIndex::iterator Directory::LocateInParentChildIndex(
2316 const ScopedKernelLock& lock,
2317 const Id& parent_id,
2318 int64 position_in_parent,
2319 const Id& item_id_for_tiebreaking) {
2320 kernel_->needle.put(PARENT_ID, parent_id);
2321 kernel_->needle.put(SERVER_POSITION_IN_PARENT, position_in_parent);
2322 kernel_->needle.put(ID, item_id_for_tiebreaking);
2323 return kernel_->parent_id_child_index->lower_bound(&kernel_->needle);
2324 }
2325
2326 Directory::ParentIdChildIndex::iterator
2327 Directory::GetParentChildIndexLowerBound(const ScopedKernelLock& lock,
2328 const Id& parent_id) {
2329 // Peg the parent ID, and use the least values for the remaining
2330 // index variables.
2331 return LocateInParentChildIndex(lock, parent_id,
2332 std::numeric_limits<int64>::min(),
2333 Id::GetLeastIdForLexicographicComparison());
2334 }
2335
2336 Directory::ParentIdChildIndex::iterator
2337 Directory::GetParentChildIndexUpperBound(const ScopedKernelLock& lock,
2338 const Id& parent_id) {
2339 // The upper bound of |parent_id|'s range is the lower
2340 // bound of |++parent_id|'s range.
2341 return GetParentChildIndexLowerBound(lock,
2342 parent_id.GetLexicographicSuccessor());
2343 }
2344
2345 void Directory::AppendChildHandles(const ScopedKernelLock& lock,
2346 const Id& parent_id,
2347 Directory::ChildHandles* result) {
2348 typedef ParentIdChildIndex::iterator iterator;
2349 CHECK(result);
2350 for (iterator i = GetParentChildIndexLowerBound(lock, parent_id),
2351 end = GetParentChildIndexUpperBound(lock, parent_id);
2352 i != end; ++i) {
2353 DCHECK_EQ(parent_id, (*i)->ref(PARENT_ID));
2354 result->push_back((*i)->ref(META_HANDLE));
2355 }
2356 }
2357
2358 EntryKernel* Directory::GetPossibleFirstChild(
2359 const ScopedKernelLock& lock, const Id& parent_id) {
2360 // We can use the server positional ordering as a hint because it's generally
2361 // in sync with the local (linked-list) positional ordering, and we have an
2362 // index on it.
2363 ParentIdChildIndex::iterator candidate =
2364 GetParentChildIndexLowerBound(lock, parent_id);
2365 ParentIdChildIndex::iterator end_range =
2366 GetParentChildIndexUpperBound(lock, parent_id);
2367 for (; candidate != end_range; ++candidate) {
2368 EntryKernel* entry = *candidate;
2369 // Filter out self-looped items, which are temporarily not in the child
2370 // ordering.
2371 if (entry->ref(PREV_ID).IsRoot() ||
2372 entry->ref(PREV_ID) != entry->ref(NEXT_ID)) {
2373 return entry;
2374 }
2375 }
2376 // There were no children in the linked list.
2377 return NULL;
2378 }
2379
2380 EntryKernel* Directory::GetPossibleLastChildForTest(
2381 const ScopedKernelLock& lock, const Id& parent_id) {
2382 // We can use the server positional ordering as a hint because it's generally
2383 // in sync with the local (linked-list) positional ordering, and we have an
2384 // index on it.
2385 ParentIdChildIndex::iterator begin_range =
2386 GetParentChildIndexLowerBound(lock, parent_id);
2387 ParentIdChildIndex::iterator candidate =
2388 GetParentChildIndexUpperBound(lock, parent_id);
2389
2390 while (begin_range != candidate) {
2391 --candidate;
2392 EntryKernel* entry = *candidate;
2393
2394 // Filter out self-looped items, which are temporarily not in the child
2395 // ordering.
2396 if (entry->ref(NEXT_ID).IsRoot() ||
2397 entry->ref(NEXT_ID) != entry->ref(PREV_ID)) {
2398 return entry;
2399 }
2400 }
2401 // There were no children in the linked list.
2402 return NULL;
2403 }
2404
2405 } // namespace syncable
OLDNEW
« no previous file with comments | « chrome/browser/sync/syncable/syncable.h ('k') | chrome/browser/sync/syncable/syncable-inl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698