• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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