OLD | NEW |
| (Empty) |
1 // Copyright (c) 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/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 : BaseTransaction(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 : BaseTransaction(location, "WriteTransaction", SYNCAPI, directory), | |
29 transaction_version_(transaction_version) { | |
30 Lock(); | |
31 if (transaction_version_) | |
32 *transaction_version_ = kInvalidTransactionVersion; | |
33 } | |
34 | |
35 void 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 | |
49 ImmutableEntryKernelMutationMap 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 | |
70 void 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 | |
86 ModelTypeSet 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 | |
120 void WriteTransaction::NotifyTransactionComplete( | |
121 ModelTypeSet models_with_changes) { | |
122 directory_->kernel_->delegate->HandleTransactionCompleteChangeEvent( | |
123 models_with_changes); | |
124 } | |
125 | |
126 void 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 | |
150 WriteTransaction::~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 | |
166 std::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(PURGE_ENTRIES); | |
174 ENUM_CASE(SYNCAPI); | |
175 }; | |
176 NOTREACHED(); | |
177 return ""; | |
178 } | |
179 | |
180 #undef ENUM_CASE | |
181 | |
182 } // namespace syncable | |
183 } // namespace syncer | |
OLD | NEW |