OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "sync/syncable/directory.h" | 5 #include "sync/syncable/directory.h" |
6 | 6 |
7 #include <iterator> | 7 #include <iterator> |
8 | 8 |
9 #include "base/base64.h" | 9 #include "base/base64.h" |
10 #include "base/debug/trace_event.h" | 10 #include "base/debug/trace_event.h" |
11 #include "base/stl_util.h" | 11 #include "base/stl_util.h" |
12 #include "base/strings/string_number_conversions.h" | 12 #include "base/strings/string_number_conversions.h" |
13 #include "sync/internal_api/public/base/attachment_id_proto.h" | |
14 #include "sync/internal_api/public/base/unique_position.h" | 13 #include "sync/internal_api/public/base/unique_position.h" |
15 #include "sync/internal_api/public/util/unrecoverable_error_handler.h" | 14 #include "sync/internal_api/public/util/unrecoverable_error_handler.h" |
16 #include "sync/syncable/entry.h" | 15 #include "sync/syncable/entry.h" |
17 #include "sync/syncable/entry_kernel.h" | 16 #include "sync/syncable/entry_kernel.h" |
18 #include "sync/syncable/in_memory_directory_backing_store.h" | 17 #include "sync/syncable/in_memory_directory_backing_store.h" |
19 #include "sync/syncable/on_disk_directory_backing_store.h" | 18 #include "sync/syncable/on_disk_directory_backing_store.h" |
20 #include "sync/syncable/scoped_kernel_lock.h" | 19 #include "sync/syncable/scoped_kernel_lock.h" |
21 #include "sync/syncable/scoped_parent_child_index_updater.h" | 20 #include "sync/syncable/scoped_parent_child_index_updater.h" |
22 #include "sync/syncable/syncable-inl.h" | 21 #include "sync/syncable/syncable-inl.h" |
23 #include "sync/syncable/syncable_base_transaction.h" | 22 #include "sync/syncable/syncable_base_transaction.h" |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
117 | 116 |
118 const DirOpenResult result = | 117 const DirOpenResult result = |
119 OpenImpl(name, delegate, transaction_observer); | 118 OpenImpl(name, delegate, transaction_observer); |
120 | 119 |
121 if (OPENED != result) | 120 if (OPENED != result) |
122 Close(); | 121 Close(); |
123 return result; | 122 return result; |
124 } | 123 } |
125 | 124 |
126 void Directory::InitializeIndices(MetahandlesMap* handles_map) { | 125 void Directory::InitializeIndices(MetahandlesMap* handles_map) { |
127 ScopedKernelLock lock(this); | |
128 kernel_->metahandles_map.swap(*handles_map); | 126 kernel_->metahandles_map.swap(*handles_map); |
129 for (MetahandlesMap::const_iterator it = kernel_->metahandles_map.begin(); | 127 for (MetahandlesMap::const_iterator it = kernel_->metahandles_map.begin(); |
130 it != kernel_->metahandles_map.end(); ++it) { | 128 it != kernel_->metahandles_map.end(); ++it) { |
131 EntryKernel* entry = it->second; | 129 EntryKernel* entry = it->second; |
132 if (ParentChildIndex::ShouldInclude(entry)) | 130 if (ParentChildIndex::ShouldInclude(entry)) |
133 kernel_->parent_child_index.Insert(entry); | 131 kernel_->parent_child_index.Insert(entry); |
134 const int64 metahandle = entry->ref(META_HANDLE); | 132 const int64 metahandle = entry->ref(META_HANDLE); |
135 if (entry->ref(IS_UNSYNCED)) | 133 if (entry->ref(IS_UNSYNCED)) |
136 kernel_->unsynced_metahandles.insert(metahandle); | 134 kernel_->unsynced_metahandles.insert(metahandle); |
137 if (entry->ref(IS_UNAPPLIED_UPDATE)) { | 135 if (entry->ref(IS_UNAPPLIED_UPDATE)) { |
138 const ModelType type = entry->GetServerModelType(); | 136 const ModelType type = entry->GetServerModelType(); |
139 kernel_->unapplied_update_metahandles[type].insert(metahandle); | 137 kernel_->unapplied_update_metahandles[type].insert(metahandle); |
140 } | 138 } |
141 if (!entry->ref(UNIQUE_SERVER_TAG).empty()) { | 139 if (!entry->ref(UNIQUE_SERVER_TAG).empty()) { |
142 DCHECK(kernel_->server_tags_map.find(entry->ref(UNIQUE_SERVER_TAG)) == | 140 DCHECK(kernel_->server_tags_map.find(entry->ref(UNIQUE_SERVER_TAG)) == |
143 kernel_->server_tags_map.end()) | 141 kernel_->server_tags_map.end()) |
144 << "Unexpected duplicate use of client tag"; | 142 << "Unexpected duplicate use of client tag"; |
145 kernel_->server_tags_map[entry->ref(UNIQUE_SERVER_TAG)] = entry; | 143 kernel_->server_tags_map[entry->ref(UNIQUE_SERVER_TAG)] = entry; |
146 } | 144 } |
147 if (!entry->ref(UNIQUE_CLIENT_TAG).empty()) { | 145 if (!entry->ref(UNIQUE_CLIENT_TAG).empty()) { |
148 DCHECK(kernel_->server_tags_map.find(entry->ref(UNIQUE_SERVER_TAG)) == | 146 DCHECK(kernel_->server_tags_map.find(entry->ref(UNIQUE_SERVER_TAG)) == |
149 kernel_->server_tags_map.end()) | 147 kernel_->server_tags_map.end()) |
150 << "Unexpected duplicate use of server tag"; | 148 << "Unexpected duplicate use of server tag"; |
151 kernel_->client_tags_map[entry->ref(UNIQUE_CLIENT_TAG)] = entry; | 149 kernel_->client_tags_map[entry->ref(UNIQUE_CLIENT_TAG)] = entry; |
152 } | 150 } |
153 DCHECK(kernel_->ids_map.find(entry->ref(ID).value()) == | 151 DCHECK(kernel_->ids_map.find(entry->ref(ID).value()) == |
154 kernel_->ids_map.end()) << "Unexpected duplicate use of ID"; | 152 kernel_->ids_map.end()) << "Unexpected duplicate use of ID"; |
155 kernel_->ids_map[entry->ref(ID).value()] = entry; | 153 kernel_->ids_map[entry->ref(ID).value()] = entry; |
156 DCHECK(!entry->is_dirty()); | 154 DCHECK(!entry->is_dirty()); |
157 AddToAttachmentIndex(metahandle, entry->ref(ATTACHMENT_METADATA), lock); | |
158 } | 155 } |
159 } | 156 } |
160 | 157 |
161 DirOpenResult Directory::OpenImpl( | 158 DirOpenResult Directory::OpenImpl( |
162 const string& name, | 159 const string& name, |
163 DirectoryChangeDelegate* delegate, | 160 DirectoryChangeDelegate* delegate, |
164 const WeakHandle<TransactionObserver>& | 161 const WeakHandle<TransactionObserver>& |
165 transaction_observer) { | 162 transaction_observer) { |
166 KernelLoadInfo info; | 163 KernelLoadInfo info; |
167 // Temporary indices before kernel_ initialized in case Load fails. We 0(1) | 164 // Temporary indices before kernel_ initialized in case Load fails. We 0(1) |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
358 return false; | 355 return false; |
359 } | 356 } |
360 if (ParentChildIndex::ShouldInclude(entry)) { | 357 if (ParentChildIndex::ShouldInclude(entry)) { |
361 if (!SyncAssert(kernel_->parent_child_index.Insert(entry), | 358 if (!SyncAssert(kernel_->parent_child_index.Insert(entry), |
362 FROM_HERE, | 359 FROM_HERE, |
363 error, | 360 error, |
364 trans)) { | 361 trans)) { |
365 return false; | 362 return false; |
366 } | 363 } |
367 } | 364 } |
368 AddToAttachmentIndex( | |
369 entry->ref(META_HANDLE), entry->ref(ATTACHMENT_METADATA), *lock); | |
370 | 365 |
371 // Should NEVER be created with a client tag or server tag. | 366 // Should NEVER be created with a client tag or server tag. |
372 if (!SyncAssert(entry->ref(UNIQUE_SERVER_TAG).empty(), FROM_HERE, | 367 if (!SyncAssert(entry->ref(UNIQUE_SERVER_TAG).empty(), FROM_HERE, |
373 "Server tag should be empty", trans)) { | 368 "Server tag should be empty", trans)) { |
374 return false; | 369 return false; |
375 } | 370 } |
376 if (!SyncAssert(entry->ref(UNIQUE_CLIENT_TAG).empty(), FROM_HERE, | 371 if (!SyncAssert(entry->ref(UNIQUE_CLIENT_TAG).empty(), FROM_HERE, |
377 "Client tag should be empty", trans)) | 372 "Client tag should be empty", trans)) |
378 return false; | 373 return false; |
379 | 374 |
(...skipping 26 matching lines...) Expand all Loading... |
406 | 401 |
407 { | 402 { |
408 // Update the indices that depend on the PARENT_ID field. | 403 // Update the indices that depend on the PARENT_ID field. |
409 ScopedParentChildIndexUpdater index_updater(lock, entry, | 404 ScopedParentChildIndexUpdater index_updater(lock, entry, |
410 &kernel_->parent_child_index); | 405 &kernel_->parent_child_index); |
411 entry->put(PARENT_ID, new_parent_id); | 406 entry->put(PARENT_ID, new_parent_id); |
412 } | 407 } |
413 return true; | 408 return true; |
414 } | 409 } |
415 | 410 |
416 void Directory::RemoveFromAttachmentIndex( | |
417 const int64 metahandle, | |
418 const sync_pb::AttachmentMetadata& attachment_metadata, | |
419 const ScopedKernelLock& lock) { | |
420 for (int i = 0; i < attachment_metadata.record_size(); ++i) { | |
421 AttachmentIdUniqueId unique_id = | |
422 attachment_metadata.record(i).id().unique_id(); | |
423 IndexByAttachmentId::iterator iter = | |
424 kernel_->index_by_attachment_id.find(unique_id); | |
425 if (iter != kernel_->index_by_attachment_id.end()) { | |
426 iter->second.erase(metahandle); | |
427 if (iter->second.empty()) { | |
428 kernel_->index_by_attachment_id.erase(iter); | |
429 } | |
430 } | |
431 } | |
432 } | |
433 | |
434 void Directory::AddToAttachmentIndex( | |
435 const int64 metahandle, | |
436 const sync_pb::AttachmentMetadata& attachment_metadata, | |
437 const ScopedKernelLock& lock) { | |
438 for (int i = 0; i < attachment_metadata.record_size(); ++i) { | |
439 AttachmentIdUniqueId unique_id = | |
440 attachment_metadata.record(i).id().unique_id(); | |
441 IndexByAttachmentId::iterator iter = | |
442 kernel_->index_by_attachment_id.find(unique_id); | |
443 if (iter == kernel_->index_by_attachment_id.end()) { | |
444 iter = kernel_->index_by_attachment_id.insert(std::make_pair( | |
445 unique_id, | |
446 MetahandleSet())).first; | |
447 } | |
448 iter->second.insert(metahandle); | |
449 } | |
450 } | |
451 | |
452 void Directory::UpdateAttachmentIndex( | |
453 const int64 metahandle, | |
454 const sync_pb::AttachmentMetadata& old_metadata, | |
455 const sync_pb::AttachmentMetadata& new_metadata) { | |
456 ScopedKernelLock lock(this); | |
457 RemoveFromAttachmentIndex(metahandle, old_metadata, lock); | |
458 AddToAttachmentIndex(metahandle, new_metadata, lock); | |
459 } | |
460 | |
461 bool Directory::unrecoverable_error_set(const BaseTransaction* trans) const { | 411 bool Directory::unrecoverable_error_set(const BaseTransaction* trans) const { |
462 DCHECK(trans != NULL); | 412 DCHECK(trans != NULL); |
463 return unrecoverable_error_set_; | 413 return unrecoverable_error_set_; |
464 } | 414 } |
465 | 415 |
466 void Directory::ClearDirtyMetahandles() { | 416 void Directory::ClearDirtyMetahandles() { |
467 kernel_->transaction_mutex.AssertAcquired(); | 417 kernel_->transaction_mutex.AssertAcquired(); |
468 kernel_->dirty_metahandles.clear(); | 418 kernel_->dirty_metahandles.clear(); |
469 } | 419 } |
470 | 420 |
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
591 if (!entry->ref(UNIQUE_CLIENT_TAG).empty()) { | 541 if (!entry->ref(UNIQUE_CLIENT_TAG).empty()) { |
592 num_erased = | 542 num_erased = |
593 kernel_->client_tags_map.erase(entry->ref(UNIQUE_CLIENT_TAG)); | 543 kernel_->client_tags_map.erase(entry->ref(UNIQUE_CLIENT_TAG)); |
594 DCHECK_EQ(1u, num_erased); | 544 DCHECK_EQ(1u, num_erased); |
595 } | 545 } |
596 if (!SyncAssert(!kernel_->parent_child_index.Contains(entry), | 546 if (!SyncAssert(!kernel_->parent_child_index.Contains(entry), |
597 FROM_HERE, | 547 FROM_HERE, |
598 "Deleted entry still present", | 548 "Deleted entry still present", |
599 (&trans))) | 549 (&trans))) |
600 return false; | 550 return false; |
601 RemoveFromAttachmentIndex( | |
602 entry->ref(META_HANDLE), entry->ref(ATTACHMENT_METADATA), lock); | |
603 | |
604 delete entry; | 551 delete entry; |
605 } | 552 } |
606 if (trans.unrecoverable_error_set()) | 553 if (trans.unrecoverable_error_set()) |
607 return false; | 554 return false; |
608 } | 555 } |
609 return true; | 556 return true; |
610 } | 557 } |
611 | 558 |
612 void Directory::UnapplyEntry(EntryKernel* entry) { | 559 void Directory::UnapplyEntry(EntryKernel* entry) { |
613 int64 handle = entry->ref(META_HANDLE); | 560 int64 handle = entry->ref(META_HANDLE); |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
653 } | 600 } |
654 | 601 |
655 // At this point locally created items that aren't synced will become locally | 602 // At this point locally created items that aren't synced will become locally |
656 // deleted items, and purged on the next snapshot. All other items will match | 603 // deleted items, and purged on the next snapshot. All other items will match |
657 // the state they would have had if they were just created via a server | 604 // the state they would have had if they were just created via a server |
658 // update. See MutableEntry::MutableEntry(.., CreateNewUpdateItem, ..). | 605 // update. See MutableEntry::MutableEntry(.., CreateNewUpdateItem, ..). |
659 } | 606 } |
660 | 607 |
661 void Directory::DeleteEntry(bool save_to_journal, | 608 void Directory::DeleteEntry(bool save_to_journal, |
662 EntryKernel* entry, | 609 EntryKernel* entry, |
663 EntryKernelSet* entries_to_journal, | 610 EntryKernelSet* entries_to_journal) { |
664 const ScopedKernelLock& lock) { | |
665 int64 handle = entry->ref(META_HANDLE); | 611 int64 handle = entry->ref(META_HANDLE); |
666 ModelType server_type = GetModelTypeFromSpecifics( | 612 ModelType server_type = GetModelTypeFromSpecifics( |
667 entry->ref(SERVER_SPECIFICS)); | 613 entry->ref(SERVER_SPECIFICS)); |
668 | 614 |
669 kernel_->metahandles_to_purge.insert(handle); | 615 kernel_->metahandles_to_purge.insert(handle); |
670 | 616 |
671 size_t num_erased = 0; | 617 size_t num_erased = 0; |
672 num_erased = kernel_->metahandles_map.erase(entry->ref(META_HANDLE)); | 618 num_erased = kernel_->metahandles_map.erase(entry->ref(META_HANDLE)); |
673 DCHECK_EQ(1u, num_erased); | 619 DCHECK_EQ(1u, num_erased); |
674 num_erased = kernel_->ids_map.erase(entry->ref(ID).value()); | 620 num_erased = kernel_->ids_map.erase(entry->ref(ID).value()); |
675 DCHECK_EQ(1u, num_erased); | 621 DCHECK_EQ(1u, num_erased); |
676 num_erased = kernel_->unsynced_metahandles.erase(handle); | 622 num_erased = kernel_->unsynced_metahandles.erase(handle); |
677 DCHECK_EQ(entry->ref(IS_UNSYNCED), num_erased > 0); | 623 DCHECK_EQ(entry->ref(IS_UNSYNCED), num_erased > 0); |
678 num_erased = | 624 num_erased = |
679 kernel_->unapplied_update_metahandles[server_type].erase(handle); | 625 kernel_->unapplied_update_metahandles[server_type].erase(handle); |
680 DCHECK_EQ(entry->ref(IS_UNAPPLIED_UPDATE), num_erased > 0); | 626 DCHECK_EQ(entry->ref(IS_UNAPPLIED_UPDATE), num_erased > 0); |
681 if (kernel_->parent_child_index.Contains(entry)) | 627 if (kernel_->parent_child_index.Contains(entry)) |
682 kernel_->parent_child_index.Remove(entry); | 628 kernel_->parent_child_index.Remove(entry); |
683 | 629 |
684 if (!entry->ref(UNIQUE_CLIENT_TAG).empty()) { | 630 if (!entry->ref(UNIQUE_CLIENT_TAG).empty()) { |
685 num_erased = | 631 num_erased = |
686 kernel_->client_tags_map.erase(entry->ref(UNIQUE_CLIENT_TAG)); | 632 kernel_->client_tags_map.erase(entry->ref(UNIQUE_CLIENT_TAG)); |
687 DCHECK_EQ(1u, num_erased); | 633 DCHECK_EQ(1u, num_erased); |
688 } | 634 } |
689 if (!entry->ref(UNIQUE_SERVER_TAG).empty()) { | 635 if (!entry->ref(UNIQUE_SERVER_TAG).empty()) { |
690 num_erased = | 636 num_erased = |
691 kernel_->server_tags_map.erase(entry->ref(UNIQUE_SERVER_TAG)); | 637 kernel_->server_tags_map.erase(entry->ref(UNIQUE_SERVER_TAG)); |
692 DCHECK_EQ(1u, num_erased); | 638 DCHECK_EQ(1u, num_erased); |
693 } | 639 } |
694 RemoveFromAttachmentIndex(handle, entry->ref(ATTACHMENT_METADATA), lock); | |
695 | 640 |
696 if (save_to_journal) { | 641 if (save_to_journal) { |
697 entries_to_journal->insert(entry); | 642 entries_to_journal->insert(entry); |
698 } else { | 643 } else { |
699 delete entry; | 644 delete entry; |
700 } | 645 } |
701 } | 646 } |
702 | 647 |
703 bool Directory::PurgeEntriesWithTypeIn(ModelTypeSet disabled_types, | 648 bool Directory::PurgeEntriesWithTypeIn(ModelTypeSet disabled_types, |
704 ModelTypeSet types_to_journal, | 649 ModelTypeSet types_to_journal, |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
752 | 697 |
753 if (types_to_unapply.Has(local_type) || | 698 if (types_to_unapply.Has(local_type) || |
754 types_to_unapply.Has(server_type)) { | 699 types_to_unapply.Has(server_type)) { |
755 UnapplyEntry(entry); | 700 UnapplyEntry(entry); |
756 } else { | 701 } else { |
757 bool save_to_journal = | 702 bool save_to_journal = |
758 (types_to_journal.Has(local_type) || | 703 (types_to_journal.Has(local_type) || |
759 types_to_journal.Has(server_type)) && | 704 types_to_journal.Has(server_type)) && |
760 (delete_journal_->IsDeleteJournalEnabled(local_type) || | 705 (delete_journal_->IsDeleteJournalEnabled(local_type) || |
761 delete_journal_->IsDeleteJournalEnabled(server_type)); | 706 delete_journal_->IsDeleteJournalEnabled(server_type)); |
762 DeleteEntry(save_to_journal, entry, &entries_to_journal, lock); | 707 DeleteEntry(save_to_journal, entry, &entries_to_journal); |
763 } | 708 } |
764 } | 709 } |
765 | 710 |
766 delete_journal_->AddJournalBatch(&trans, entries_to_journal); | 711 delete_journal_->AddJournalBatch(&trans, entries_to_journal); |
767 | 712 |
768 // Ensure meta tracking for these data types reflects the purged state. | 713 // Ensure meta tracking for these data types reflects the purged state. |
769 for (ModelTypeSet::Iterator it = disabled_types.First(); | 714 for (ModelTypeSet::Iterator it = disabled_types.First(); |
770 it.Good(); it.Inc()) { | 715 it.Good(); it.Inc()) { |
771 kernel_->persisted_info.transaction_version[it.Get()] = 0; | 716 kernel_->persisted_info.transaction_version[it.Get()] = 0; |
772 | 717 |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
810 | 755 |
811 // Note that we do not unset IS_UNSYNCED or IS_UNAPPLIED_UPDATE in order | 756 // Note that we do not unset IS_UNSYNCED or IS_UNAPPLIED_UPDATE in order |
812 // to ensure no in-transit data is lost. | 757 // to ensure no in-transit data is lost. |
813 | 758 |
814 entry->mark_dirty(&kernel_->dirty_metahandles); | 759 entry->mark_dirty(&kernel_->dirty_metahandles); |
815 } | 760 } |
816 | 761 |
817 return true; | 762 return true; |
818 } | 763 } |
819 | 764 |
820 bool Directory::IsAttachmentLinked( | |
821 const sync_pb::AttachmentIdProto& attachment_id_proto) const { | |
822 ScopedKernelLock lock(this); | |
823 IndexByAttachmentId::const_iterator iter = | |
824 kernel_->index_by_attachment_id.find(attachment_id_proto.unique_id()); | |
825 if (iter != kernel_->index_by_attachment_id.end() && !iter->second.empty()) { | |
826 return true; | |
827 } | |
828 return false; | |
829 } | |
830 | |
831 void Directory::HandleSaveChangesFailure(const SaveChangesSnapshot& snapshot) { | 765 void Directory::HandleSaveChangesFailure(const SaveChangesSnapshot& snapshot) { |
832 WriteTransaction trans(FROM_HERE, HANDLE_SAVE_FAILURE, this); | 766 WriteTransaction trans(FROM_HERE, HANDLE_SAVE_FAILURE, this); |
833 ScopedKernelLock lock(this); | 767 ScopedKernelLock lock(this); |
834 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY; | 768 kernel_->info_status = KERNEL_SHARE_INFO_DIRTY; |
835 | 769 |
836 // Because we optimistically cleared the dirty bit on the real entries when | 770 // Because we optimistically cleared the dirty bit on the real entries when |
837 // taking the snapshot, we must restore it on failure. Not doing this could | 771 // taking the snapshot, we must restore it on failure. Not doing this could |
838 // cause lost data, if no other changes are made to the in-memory entries | 772 // cause lost data, if no other changes are made to the in-memory entries |
839 // that would cause the dirty bit to get set again. Setting the bit ensures | 773 // that would cause the dirty bit to get set again. Setting the bit ensures |
840 // that SaveChanges will at least try again later. | 774 // that SaveChanges will at least try again later. |
(...skipping 581 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1422 | 1356 |
1423 for (OrderedChildSet::const_iterator i = children->begin(); | 1357 for (OrderedChildSet::const_iterator i = children->begin(); |
1424 i != children->end(); ++i) { | 1358 i != children->end(); ++i) { |
1425 DCHECK_EQ(parent_id, (*i)->ref(PARENT_ID)); | 1359 DCHECK_EQ(parent_id, (*i)->ref(PARENT_ID)); |
1426 result->push_back((*i)->ref(META_HANDLE)); | 1360 result->push_back((*i)->ref(META_HANDLE)); |
1427 } | 1361 } |
1428 } | 1362 } |
1429 | 1363 |
1430 } // namespace syncable | 1364 } // namespace syncable |
1431 } // namespace syncer | 1365 } // namespace syncer |
OLD | NEW |