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
WriteTransaction(const tracked_objects::Location & location,WriterTag writer,Directory * directory)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
WriteTransaction(const tracked_objects::Location & location,Directory * directory,int64 * transaction_version)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
TrackChangesTo(const EntryKernel * entry)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
RecordMutations()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
UnlockAndNotify(const ImmutableEntryKernelMutationMap & mutations)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
NotifyTransactionChangingAndEnding(const ImmutableEntryKernelMutationMap & mutations)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
NotifyTransactionComplete(ModelTypeSet models_with_changes)118 void WriteTransaction::NotifyTransactionComplete(
119 ModelTypeSet models_with_changes) {
120 directory_->kernel_->delegate->HandleTransactionCompleteChangeEvent(
121 models_with_changes);
122 }
123
UpdateTransactionVersion(const std::vector<int64> & entry_changed)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
~WriteTransaction()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
WriterTagToString(WriterTag writer_tag)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