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

Unified Diff: sync/syncable/directory.cc

Issue 11636006: WIP: The Bookmark Position Megapatch (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Various updates, including switch suffix to unique_client_tag style Created 8 years 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « sync/syncable/directory.h ('k') | sync/syncable/directory_backing_store.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: sync/syncable/directory.cc
diff --git a/sync/syncable/directory.cc b/sync/syncable/directory.cc
index 82eb709cf52c4988ba032c60e5b9883fab21e5bb..623260ef685a7815b1ad4a057e2c019c9732da16 100644
--- a/sync/syncable/directory.cc
+++ b/sync/syncable/directory.cc
@@ -4,11 +4,11 @@
#include "sync/syncable/directory.h"
+#include "base/base64.h"
#include "base/debug/trace_event.h"
-#include "base/perftimer.h"
#include "base/stl_util.h"
#include "base/string_number_conversions.h"
-#include "sync/internal_api/public/base/node_ordinal.h"
+#include "sync/internal_api/public/base/unique_position.h"
#include "sync/internal_api/public/util/unrecoverable_error_handler.h"
#include "sync/syncable/base_transaction.h"
#include "sync/syncable/entry.h"
@@ -17,6 +17,7 @@
#include "sync/syncable/on_disk_directory_backing_store.h"
#include "sync/syncable/read_transaction.h"
#include "sync/syncable/scoped_index_updater.h"
+#include "sync/syncable/scoped_parent_child_index_updater.h"
#include "sync/syncable/syncable-inl.h"
#include "sync/syncable/syncable_changes_version.h"
#include "sync/syncable/syncable_util.h"
@@ -36,7 +37,6 @@ void InitializeIndexEntry(EntryKernel* entry,
index->insert(entry);
}
}
-
}
// static
@@ -44,29 +44,6 @@ bool ClientTagIndexer::ShouldInclude(const EntryKernel* a) {
return !a->ref(UNIQUE_CLIENT_TAG).empty();
}
-bool ParentIdAndHandleIndexer::Comparator::operator() (
- const syncable::EntryKernel* a,
- const syncable::EntryKernel* b) const {
- int cmp = a->ref(PARENT_ID).compare(b->ref(PARENT_ID));
- if (cmp != 0)
- return cmp < 0;
-
- const NodeOrdinal& a_position = a->ref(SERVER_ORDINAL_IN_PARENT);
- const NodeOrdinal& b_position = b->ref(SERVER_ORDINAL_IN_PARENT);
- if (!a_position.Equals(b_position))
- return a_position.LessThan(b_position);
-
- cmp = a->ref(ID).compare(b->ref(ID));
- return cmp < 0;
-}
-
-// static
-bool ParentIdAndHandleIndexer::ShouldInclude(const EntryKernel* a) {
- // This index excludes deleted items and the root item. The root
- // item is excluded so that it doesn't show up as a child of itself.
- return !a->ref(IS_DEL) && !a->ref(ID).IsRoot();
-}
-
// static
const FilePath::CharType Directory::kSyncDatabaseFilename[] =
FILE_PATH_LITERAL("SyncData.sqlite3");
@@ -111,7 +88,7 @@ Directory::Kernel::Kernel(
name(name),
metahandles_index(new Directory::MetahandlesIndex),
ids_index(new Directory::IdsIndex),
- parent_id_child_index(new Directory::ParentIdChildIndex),
+ parent_child_index(new ParentChildIndex),
client_tag_index(new Directory::ClientTagIndex),
unsynced_metahandles(new MetahandleSet),
dirty_metahandles(new MetahandleSet),
@@ -126,11 +103,12 @@ Directory::Kernel::Kernel(
DCHECK(transaction_observer.IsInitialized());
}
+
Directory::Kernel::~Kernel() {
delete unsynced_metahandles;
delete dirty_metahandles;
delete metahandles_to_purge;
- delete parent_id_child_index;
+ delete parent_child_index;
delete client_tag_index;
delete ids_index;
STLDeleteElements(metahandles_index);
@@ -176,8 +154,8 @@ void Directory::InitializeIndices() {
MetahandlesIndex::iterator it = kernel_->metahandles_index->begin();
for (; it != kernel_->metahandles_index->end(); ++it) {
EntryKernel* entry = *it;
- InitializeIndexEntry<ParentIdAndHandleIndexer>(entry,
- kernel_->parent_id_child_index);
+ if (ParentChildIndex::ShouldInclude(entry))
+ kernel_->parent_child_index->Insert(entry);
InitializeIndexEntry<IdIndexer>(entry, kernel_->ids_index);
InitializeIndexEntry<ClientTagIndexer>(entry, kernel_->client_tag_index);
const int64 metahandle = entry->ref(META_HANDLE);
@@ -356,8 +334,8 @@ bool Directory::InsertEntry(WriteTransaction* trans,
trans))
return false;
- if (!entry->ref(IS_DEL)) {
- if (!SyncAssert(kernel_->parent_id_child_index->insert(entry).second,
+ if (ParentChildIndex::ShouldInclude(entry)) {
+ if (!SyncAssert(kernel_->parent_child_index->Insert(entry),
FROM_HERE,
error,
trans)) {
@@ -388,8 +366,8 @@ bool Directory::ReindexId(WriteTransaction* trans,
{
// Update the indices that depend on the ID field.
ScopedIndexUpdater<IdIndexer> updater_a(lock, entry, kernel_->ids_index);
- ScopedIndexUpdater<ParentIdAndHandleIndexer> updater_b(lock, entry,
- kernel_->parent_id_child_index);
+ ScopedParentChildIndexUpdater updater_b(lock, entry,
+ kernel_->parent_child_index);
entry->put(ID, new_id);
}
return true;
@@ -402,8 +380,8 @@ bool Directory::ReindexParentId(WriteTransaction* trans,
{
// Update the indices that depend on the PARENT_ID field.
- ScopedIndexUpdater<ParentIdAndHandleIndexer> index_updater(lock, entry,
- kernel_->parent_id_child_index);
+ ScopedParentChildIndexUpdater index_updater(lock, entry,
+ kernel_->parent_child_index);
entry->put(PARENT_ID, new_parent_id);
}
return true;
@@ -535,7 +513,7 @@ bool Directory::VacuumAfterSaveChanges(const SaveChangesSnapshot& snapshot) {
// Might not be in it
num_erased = kernel_->client_tag_index->erase(entry);
DCHECK_EQ(entry->ref(UNIQUE_CLIENT_TAG).empty(), !num_erased);
- if (!SyncAssert(!kernel_->parent_id_child_index->count(entry),
+ if (!SyncAssert(!kernel_->parent_child_index->Contains(entry),
FROM_HERE,
"Deleted entry still present",
(&trans)))
@@ -567,8 +545,6 @@ bool Directory::PurgeEntriesWithTypeIn(ModelTypeSet types) {
// Note the dance around incrementing |it|, since we sometimes erase().
if ((IsRealDataType(local_type) && types.Has(local_type)) ||
(IsRealDataType(server_type) && types.Has(server_type))) {
- if (!UnlinkEntryFromOrder(*it, &trans, &lock, DATA_TYPE_PURGE))
- return false;
int64 handle = (*it)->ref(META_HANDLE);
kernel_->metahandles_to_purge->insert(handle);
@@ -584,8 +560,8 @@ bool Directory::PurgeEntriesWithTypeIn(ModelTypeSet types) {
num_erased =
kernel_->unapplied_update_metahandles[server_type].erase(handle);
DCHECK_EQ(entry->ref(IS_UNAPPLIED_UPDATE), num_erased > 0);
- num_erased = kernel_->parent_id_child_index->erase(entry);
- DCHECK_EQ(entry->ref(IS_DEL), !num_erased);
+ if (ParentChildIndex::ShouldInclude(entry))
+ kernel_->parent_child_index->Remove(entry);
kernel_->metahandles_index->erase(it++);
delete entry;
} else {
@@ -1010,83 +986,13 @@ void Directory::SetInvariantCheckLevel(InvariantCheckLevel check_level) {
invariant_check_level_ = check_level;
}
-bool Directory::UnlinkEntryFromOrder(EntryKernel* entry,
- WriteTransaction* trans,
- ScopedKernelLock* lock,
- UnlinkReason unlink_reason) {
- if (!SyncAssert(!trans || this == trans->directory(),
- FROM_HERE,
- "Transaction not pointing to the right directory",
- trans))
- return false;
- Id old_previous = entry->ref(PREV_ID);
- Id old_next = entry->ref(NEXT_ID);
-
- entry->put(NEXT_ID, entry->ref(ID));
- entry->put(PREV_ID, entry->ref(ID));
- entry->mark_dirty(kernel_->dirty_metahandles);
-
- if (!old_previous.IsRoot()) {
- if (old_previous == old_next) {
- // Note previous == next doesn't imply previous == next == Get(ID). We
- // could have prev==next=="c-XX" and Get(ID)=="sX..." if an item was added
- // and deleted before receiving the server ID in the commit response.
- if (!SyncAssert(
- (old_next == entry->ref(ID)) || !old_next.ServerKnows(),
- FROM_HERE,
- "Encounteered inconsistent entry while deleting",
- trans)) {
- return false;
- }
- return true; // Done if we were already self-looped (hence unlinked).
- }
- EntryKernel* previous_entry = GetEntryById(old_previous, lock);
- ModelType type = GetModelTypeFromSpecifics(entry->ref(SPECIFICS));
- // TODO(tim): Multiple asserts here for bug 101039 investigation.
- if (type == AUTOFILL) {
- if (!SyncAssert(previous_entry != NULL,
- FROM_HERE,
- "Could not find previous autofill entry",
- trans)) {
- return false;
- }
- } else {
- if (!SyncAssert(previous_entry != NULL,
- FROM_HERE,
- "Could not find previous entry",
- trans)) {
- return false;
- }
- }
- if (unlink_reason == NODE_MANIPULATION)
- trans->SaveOriginal(previous_entry);
- previous_entry->put(NEXT_ID, old_next);
- previous_entry->mark_dirty(kernel_->dirty_metahandles);
- }
-
- if (!old_next.IsRoot()) {
- EntryKernel* next_entry = GetEntryById(old_next, lock);
- if (!SyncAssert(next_entry != NULL,
- FROM_HERE,
- "Could not find next entry",
- trans)) {
- return false;
- }
- if (unlink_reason == NODE_MANIPULATION)
- trans->SaveOriginal(next_entry);
- next_entry->put(PREV_ID, old_previous);
- next_entry->mark_dirty(kernel_->dirty_metahandles);
- }
- return true;
-}
-
int64 Directory::NextMetahandle() {
ScopedKernelLock lock(this);
int64 metahandle = (kernel_->next_metahandle)++;
return metahandle;
}
-// Always returns a client ID that is the string representation of a negative
+// Always returns a client ID that is a string representation of a negative
// number.
Id Directory::NextId() {
int64 result;
@@ -1101,171 +1007,160 @@ Id Directory::NextId() {
bool Directory::HasChildren(BaseTransaction* trans, const Id& id) {
ScopedKernelLock lock(this);
- return (GetPossibleFirstChild(lock, id) != NULL);
+ return kernel_->parent_child_index->GetChildren(id) != NULL;
}
-bool Directory::GetFirstChildId(BaseTransaction* trans,
- const Id& parent_id,
- Id* first_child_id) {
+Id Directory::GetFirstChildId(BaseTransaction* trans,
+ const EntryKernel* parent) {
+ DCHECK(parent);
+ DCHECK(parent->ref(IS_DIR));
+
ScopedKernelLock lock(this);
- EntryKernel* entry = GetPossibleFirstChild(lock, parent_id);
- if (!entry) {
- *first_child_id = Id();
- return true;
- }
+ const ChildSet* children =
+ kernel_->parent_child_index->GetChildren(parent->ref(ID));
- // Walk to the front of the list; the server position ordering
- // is commonly identical to the linked-list ordering, but pending
- // unsynced or unapplied items may diverge.
- while (!entry->ref(PREV_ID).IsRoot()) {
- entry = GetEntryById(entry->ref(PREV_ID), &lock);
- if (!entry) {
- *first_child_id = Id();
- return false;
- }
- }
- *first_child_id = entry->ref(ID);
- return true;
+ // We're expected to return root if there are no children.
+ if (!children)
+ return Id();
+
+ return (*children->begin())->ref(ID);
}
-bool Directory::GetLastChildIdForTest(
- BaseTransaction* trans, const Id& parent_id, Id* last_child_id) {
+syncable::Id Directory::GetPredecessorId(EntryKernel* e) {
ScopedKernelLock lock(this);
- EntryKernel* entry = GetPossibleFirstChild(lock, parent_id);
- if (!entry) {
- *last_child_id = Id();
- return true;
- }
- // Walk to the back of the list; the server position ordering
- // is commonly identical to the linked-list ordering, but pending
- // unsynced or unapplied items may diverge.
- while (!entry->ref(NEXT_ID).IsRoot()) {
- entry = GetEntryById(entry->ref(NEXT_ID), &lock);
- if (!entry) {
- *last_child_id = Id();
- return false;
- }
+ DCHECK(ParentChildIndex::ShouldInclude(e));
+ const ChildSet* children =
+ kernel_->parent_child_index->GetChildren(e->ref(PARENT_ID));
+ DCHECK(children && !children->empty());
+ ChildSet::const_iterator i = children->find(e);
+ DCHECK(i != children->end());
+
+ if (i == children->begin()) {
+ return Id();
+ } else {
+ i--;
+ return (*i)->ref(ID);
}
-
- *last_child_id = entry->ref(ID);
- return true;
}
-Id Directory::ComputePrevIdFromServerPosition(
- const EntryKernel* entry,
- const syncable::Id& parent_id) {
+syncable::Id Directory::GetSuccessorId(EntryKernel* e) {
ScopedKernelLock lock(this);
- // Find the natural insertion point in the parent_id_child_index, and
- // work back from there, filtering out ineligible candidates.
- ParentIdChildIndex::iterator sibling = LocateInParentChildIndex(
- lock,
- parent_id,
- NodeOrdinalToInt64(entry->ref(SERVER_ORDINAL_IN_PARENT)),
- entry->ref(ID));
- ParentIdChildIndex::iterator first_sibling =
- GetParentChildIndexLowerBound(lock, parent_id);
-
- while (sibling != first_sibling) {
- --sibling;
- EntryKernel* candidate = *sibling;
-
- // The item itself should never be in the range under consideration.
- DCHECK_NE(candidate->ref(META_HANDLE), entry->ref(META_HANDLE));
-
- // Ignore unapplied updates -- they might not even be server-siblings.
- if (candidate->ref(IS_UNAPPLIED_UPDATE))
- continue;
+ DCHECK(ParentChildIndex::ShouldInclude(e));
+ const ChildSet* children =
+ kernel_->parent_child_index->GetChildren(e->ref(PARENT_ID));
+ DCHECK(children && !children->empty());
+ ChildSet::const_iterator i = children->find(e);
+ DCHECK(i != children->end());
+
+ i++;
+ if (i == children->end()) {
+ return Id();
+ } else {
+ return (*i)->ref(ID);
+ }
+}
- // We can't trust the SERVER_ fields of unsynced items, but they are
- // potentially legitimate local predecessors. In the case where
- // |update_item| and an unsynced item wind up in the same insertion
- // position, we need to choose how to order them. The following check puts
- // the unapplied update first; removing it would put the unsynced item(s)
- // first.
- if (candidate->ref(IS_UNSYNCED))
- continue;
+// TODO(rlarocque): Remove all support for placing ShouldMaintainPosition()
+// items as siblings of items that do not maintain postions. It is required
+// only for tests.
+void Directory::PutPredecessor(EntryKernel* e, EntryKernel* predecessor) {
+ DCHECK(!e->ref(IS_DEL));
+ if (!ShouldMaintainPosition(GetModelTypeFromSpecifics(e->ref(SPECIFICS)))) {
+ DCHECK(!e->ref(UNIQUE_POSITION).IsValid());
+ return;
+ }
+ std::string suffix = e->ref(UNIQUE_BOOKMARK_TAG);
+ DCHECK(!suffix.empty());
- // Skip over self-looped items, which are not valid predecessors. This
- // shouldn't happen in practice, but is worth defending against.
- if (candidate->ref(PREV_ID) == candidate->ref(NEXT_ID) &&
- !candidate->ref(PREV_ID).IsRoot()) {
- NOTREACHED();
- continue;
+ // Remove our item from the list and remember to re-add it later.
+ ScopedKernelLock lock(this);
+ ScopedParentChildIndexUpdater updater(lock, e, kernel_->parent_child_index);
+
+ // Note: The ScopedParentChildIndexUpdater will update this set for us as we
+ // leave this function.
+ const ChildSet* children =
+ kernel_->parent_child_index->GetChildren(e->ref(PARENT_ID));
+
+ if (!children) {
+ // This parent currently has no other children.
+ DCHECK(predecessor->ref(ID).IsRoot());
+ UniquePosition pos = UniquePosition::InitialPosition(suffix);
+ e->put(UNIQUE_POSITION, pos);
+ return;
+ }
+
+ if (predecessor->ref(ID).IsRoot()) {
+ // We have at least one sibling, and we're inserting to the left of them.
+ UniquePosition successor_pos = (*children->begin())->ref(UNIQUE_POSITION);
+
+ if (!successor_pos.IsValid()) {
+ // If all our successors are of non-positionable types, just create an
+ // initial position. We arbitrarily choose to sort invalid positions to
+ // the right of the valid positions.
+ //
+ // We really shoulndn't need to support this. See TODO above.
+ UniquePosition pos = UniquePosition::InitialPosition(suffix);
+ e->put(UNIQUE_POSITION, pos);
+ return;
}
- return candidate->ref(ID);
+
+ DCHECK(!children->empty());
+ UniquePosition pos = UniquePosition::Before(successor_pos, suffix);
+ e->put(UNIQUE_POSITION, pos);
+ return;
}
- // This item will be the first in the sibling order.
- return Id();
-}
-Directory::ParentIdChildIndex::iterator Directory::LocateInParentChildIndex(
- const ScopedKernelLock& lock,
- const Id& parent_id,
- int64 position_in_parent,
- const Id& item_id_for_tiebreaking) {
- kernel_->needle.put(PARENT_ID, parent_id);
- kernel_->needle.put(SERVER_ORDINAL_IN_PARENT,
- Int64ToNodeOrdinal(position_in_parent));
- kernel_->needle.put(ID, item_id_for_tiebreaking);
- return kernel_->parent_id_child_index->lower_bound(&kernel_->needle);
-}
+ // We can't support placing an item after an invalid position. Fortunately,
+ // the tests don't exercise this particular case. We should not support
+ // siblings with invalid positions at all. See TODO above.
+ DCHECK(predecessor->ref(UNIQUE_POSITION).IsValid());
+
+ ChildSet::iterator i = children->find(predecessor);
+ DCHECK(i != children->end());
+
+ ++i;
+ if (i == children->end()) {
+ // Inserting at the end of the list.
+ UniquePosition pos = UniquePosition::After(
+ predecessor->ref(UNIQUE_POSITION),
+ suffix);
+ e->put(UNIQUE_POSITION, pos);
+ return;
+ }
-Directory::ParentIdChildIndex::iterator
-Directory::GetParentChildIndexLowerBound(const ScopedKernelLock& lock,
- const Id& parent_id) {
- // Peg the parent ID, and use the least values for the remaining
- // index variables.
- return LocateInParentChildIndex(lock, parent_id,
- std::numeric_limits<int64>::min(),
- Id::GetLeastIdForLexicographicComparison());
-}
+ EntryKernel* successor = *i;
+
+ // Another mixed valid and invalid position case. This one could be supported
+ // in theory, but we're trying to deprecate support for siblings with and
+ // without valid positions. See TODO above.
+ DCHECK(successor->ref(UNIQUE_POSITION).IsValid());
-Directory::ParentIdChildIndex::iterator
-Directory::GetParentChildIndexUpperBound(const ScopedKernelLock& lock,
- const Id& parent_id) {
- // The upper bound of |parent_id|'s range is the lower
- // bound of |++parent_id|'s range.
- return GetParentChildIndexLowerBound(lock,
- parent_id.GetLexicographicSuccessor());
+ // Finally, the normal case: inserting between two elements.
+ UniquePosition pos = UniquePosition::Between(
+ predecessor->ref(UNIQUE_POSITION),
+ successor->ref(UNIQUE_POSITION),
+ suffix);
+ e->put(UNIQUE_POSITION, pos);
+ return;
}
+// TODO(rlarocque): Avoid this indirection. Just return the set.
void Directory::AppendChildHandles(const ScopedKernelLock& lock,
const Id& parent_id,
Directory::ChildHandles* result) {
- typedef ParentIdChildIndex::iterator iterator;
- CHECK(result);
- for (iterator i = GetParentChildIndexLowerBound(lock, parent_id),
- end = GetParentChildIndexUpperBound(lock, parent_id);
- i != end; ++i) {
+ const ChildSet* children =
+ kernel_->parent_child_index->GetChildren(parent_id);
+ if (!children)
+ return;
+
+ for (ChildSet::iterator i = children->begin(); i != children->end(); ++i) {
DCHECK_EQ(parent_id, (*i)->ref(PARENT_ID));
result->push_back((*i)->ref(META_HANDLE));
}
}
-EntryKernel* Directory::GetPossibleFirstChild(
- const ScopedKernelLock& lock, const Id& parent_id) {
- // We can use the server positional ordering as a hint because it's generally
- // in sync with the local (linked-list) positional ordering, and we have an
- // index on it.
- ParentIdChildIndex::iterator candidate =
- GetParentChildIndexLowerBound(lock, parent_id);
- ParentIdChildIndex::iterator end_range =
- GetParentChildIndexUpperBound(lock, parent_id);
- for (; candidate != end_range; ++candidate) {
- EntryKernel* entry = *candidate;
- // Filter out self-looped items, which are temporarily not in the child
- // ordering.
- if (entry->ref(PREV_ID).IsRoot() ||
- entry->ref(PREV_ID) != entry->ref(NEXT_ID)) {
- return entry;
- }
- }
- // There were no children in the linked list.
- return NULL;
-}
-
ScopedKernelLock::ScopedKernelLock(const Directory* dir)
: scoped_lock_(dir->kernel_->mutex), dir_(const_cast<Directory*>(dir)) {
}
« no previous file with comments | « sync/syncable/directory.h ('k') | sync/syncable/directory_backing_store.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698