syncable_write_transaction.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1// Copyright 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 "sync/syncable/syncable_write_transaction.h" 6 7#include "sync/syncable/directory.h" 8#include "sync/syncable/directory_change_delegate.h" 9#include "sync/syncable/mutable_entry.h" 10#include "sync/syncable/transaction_observer.h" 11#include "sync/syncable/write_transaction_info.h" 12 13namespace syncer { 14namespace syncable { 15 16const int64 kInvalidTransactionVersion = -1; 17 18WriteTransaction::WriteTransaction(const tracked_objects::Location& location, 19 WriterTag writer, Directory* directory) 20 : BaseTransaction(location, "WriteTransaction", writer, directory), 21 transaction_version_(NULL) { 22 Lock(); 23} 24 25WriteTransaction::WriteTransaction(const tracked_objects::Location& location, 26 Directory* directory, 27 int64* transaction_version) 28 : BaseTransaction(location, "WriteTransaction", SYNCAPI, directory), 29 transaction_version_(transaction_version) { 30 Lock(); 31 if (transaction_version_) 32 *transaction_version_ = kInvalidTransactionVersion; 33} 34 35void WriteTransaction::SaveOriginal(const EntryKernel* entry) { 36 if (!entry) { 37 return; 38 } 39 // Insert only if it's not already there. 40 const int64 handle = entry->ref(META_HANDLE); 41 EntryKernelMutationMap::iterator it = mutations_.lower_bound(handle); 42 if (it == mutations_.end() || it->first != handle) { 43 EntryKernelMutation mutation; 44 mutation.original = *entry; 45 ignore_result(mutations_.insert(it, std::make_pair(handle, mutation))); 46 } 47} 48 49ImmutableEntryKernelMutationMap WriteTransaction::RecordMutations() { 50 directory_->kernel_->transaction_mutex.AssertAcquired(); 51 for (syncable::EntryKernelMutationMap::iterator it = mutations_.begin(); 52 it != mutations_.end();) { 53 EntryKernel* kernel = directory()->GetEntryByHandle(it->first); 54 if (!kernel) { 55 NOTREACHED(); 56 continue; 57 } 58 if (kernel->is_dirty()) { 59 it->second.mutated = *kernel; 60 ++it; 61 } else { 62 DCHECK(!it->second.original.is_dirty()); 63 // Not actually mutated, so erase from |mutations_|. 64 mutations_.erase(it++); 65 } 66 } 67 return ImmutableEntryKernelMutationMap(&mutations_); 68} 69 70void WriteTransaction::UnlockAndNotify( 71 const ImmutableEntryKernelMutationMap& mutations) { 72 // Work while transaction mutex is held. 73 ModelTypeSet models_with_changes; 74 bool has_mutations = !mutations.Get().empty(); 75 if (has_mutations) { 76 models_with_changes = NotifyTransactionChangingAndEnding(mutations); 77 } 78 Unlock(); 79 80 // Work after mutex is relased. 81 if (has_mutations) { 82 NotifyTransactionComplete(models_with_changes); 83 } 84} 85 86ModelTypeSet WriteTransaction::NotifyTransactionChangingAndEnding( 87 const ImmutableEntryKernelMutationMap& mutations) { 88 directory_->kernel_->transaction_mutex.AssertAcquired(); 89 DCHECK(!mutations.Get().empty()); 90 91 WriteTransactionInfo write_transaction_info( 92 directory_->kernel_->next_write_transaction_id, 93 from_here_, writer_, mutations); 94 ++directory_->kernel_->next_write_transaction_id; 95 96 ImmutableWriteTransactionInfo immutable_write_transaction_info( 97 &write_transaction_info); 98 DirectoryChangeDelegate* const delegate = directory_->kernel_->delegate; 99 std::vector<int64> entry_changed; 100 if (writer_ == syncable::SYNCAPI) { 101 delegate->HandleCalculateChangesChangeEventFromSyncApi( 102 immutable_write_transaction_info, this, &entry_changed); 103 } else { 104 delegate->HandleCalculateChangesChangeEventFromSyncer( 105 immutable_write_transaction_info, this, &entry_changed); 106 } 107 UpdateTransactionVersion(entry_changed); 108 109 ModelTypeSet models_with_changes = 110 delegate->HandleTransactionEndingChangeEvent( 111 immutable_write_transaction_info, this); 112 113 directory_->kernel_->transaction_observer.Call(FROM_HERE, 114 &TransactionObserver::OnTransactionWrite, 115 immutable_write_transaction_info, models_with_changes); 116 117 return models_with_changes; 118} 119 120void WriteTransaction::NotifyTransactionComplete( 121 ModelTypeSet models_with_changes) { 122 directory_->kernel_->delegate->HandleTransactionCompleteChangeEvent( 123 models_with_changes); 124} 125 126void WriteTransaction::UpdateTransactionVersion( 127 const std::vector<int64>& entry_changed) { 128 syncer::ModelTypeSet type_seen; 129 for (uint32 i = 0; i < entry_changed.size(); ++i) { 130 MutableEntry entry(this, GET_BY_HANDLE, entry_changed[i]); 131 if (entry.good()) { 132 ModelType type = GetModelTypeFromSpecifics(entry.Get(SPECIFICS)); 133 if (type < FIRST_REAL_MODEL_TYPE) 134 continue; 135 if (!type_seen.Has(type)) { 136 directory_->IncrementTransactionVersion(type); 137 type_seen.Put(type); 138 } 139 entry.Put(TRANSACTION_VERSION, directory_->GetTransactionVersion(type)); 140 } 141 } 142 143 if (!type_seen.Empty() && transaction_version_) { 144 DCHECK_EQ(1u, type_seen.Size()); 145 *transaction_version_ = directory_->GetTransactionVersion( 146 type_seen.First().Get()); 147 } 148} 149 150WriteTransaction::~WriteTransaction() { 151 const ImmutableEntryKernelMutationMap& mutations = RecordMutations(); 152 directory()->CheckInvariantsOnTransactionClose(this, mutations.Get()); 153 154 // |CheckTreeInvariants| could have thrown an unrecoverable error. 155 if (unrecoverable_error_set_) { 156 HandleUnrecoverableErrorIfSet(); 157 Unlock(); 158 return; 159 } 160 161 UnlockAndNotify(mutations); 162} 163 164#define ENUM_CASE(x) case x: return #x; break 165 166std::string WriterTagToString(WriterTag writer_tag) { 167 switch (writer_tag) { 168 ENUM_CASE(INVALID); 169 ENUM_CASE(SYNCER); 170 ENUM_CASE(AUTHWATCHER); 171 ENUM_CASE(UNITTEST); 172 ENUM_CASE(VACUUM_AFTER_SAVE); 173 ENUM_CASE(HANDLE_SAVE_FAILURE); 174 ENUM_CASE(PURGE_ENTRIES); 175 ENUM_CASE(SYNCAPI); 176 }; 177 NOTREACHED(); 178 return ""; 179} 180 181#undef ENUM_CASE 182 183} // namespace syncable 184} // namespace syncer 185