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