syncable_write_transaction.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
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 mutations_[handle].original = *entry; 44 } 45} 46 47ImmutableEntryKernelMutationMap 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 68void 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 84ModelTypeSet 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 118void WriteTransaction::NotifyTransactionComplete( 119 ModelTypeSet models_with_changes) { 120 directory_->kernel_->delegate->HandleTransactionCompleteChangeEvent( 121 models_with_changes); 122} 123 124void 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 148WriteTransaction::~WriteTransaction() { 149 const ImmutableEntryKernelMutationMap& mutations = RecordMutations(); 150 directory()->CheckInvariantsOnTransactionClose(this, mutations.Get()); 151 152 // |CheckTreeInvariants| could have thrown an unrecoverable error. 153 if (unrecoverable_error_set_) { 154 HandleUnrecoverableErrorIfSet(); 155 Unlock(); 156 return; 157 } 158 159 UnlockAndNotify(mutations); 160} 161 162#define ENUM_CASE(x) case x: return #x; break 163 164std::string WriterTagToString(WriterTag writer_tag) { 165 switch (writer_tag) { 166 ENUM_CASE(INVALID); 167 ENUM_CASE(SYNCER); 168 ENUM_CASE(AUTHWATCHER); 169 ENUM_CASE(UNITTEST); 170 ENUM_CASE(VACUUM_AFTER_SAVE); 171 ENUM_CASE(HANDLE_SAVE_FAILURE); 172 ENUM_CASE(PURGE_ENTRIES); 173 ENUM_CASE(SYNCAPI); 174 }; 175 NOTREACHED(); 176 return std::string(); 177} 178 179#undef ENUM_CASE 180 181} // namespace syncable 182} // namespace syncer 183