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