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.
5 #include "sync/syncable/syncable_write_transaction.h"
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"
16 const int64 kInvalidTransactionVersion
= -1;
18 WriteTransaction::WriteTransaction(const tracked_objects::Location
& location
,
19 WriterTag writer
, Directory
* directory
)
20 : BaseWriteTransaction(location
, "WriteTransaction", writer
, directory
),
21 transaction_version_(NULL
) {
25 WriteTransaction::WriteTransaction(const tracked_objects::Location
& location
,
27 int64
* transaction_version
)
28 : BaseWriteTransaction(location
, "WriteTransaction", SYNCAPI
, directory
),
29 transaction_version_(transaction_version
) {
31 if (transaction_version_
)
32 *transaction_version_
= kInvalidTransactionVersion
;
35 void WriteTransaction::TrackChangesTo(const EntryKernel
* entry
) {
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
;
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
);
56 if (kernel
->is_dirty()) {
57 it
->second
.mutated
= *kernel
;
60 DCHECK(!it
->second
.original
.is_dirty());
61 // Not actually mutated, so erase from |mutations_|.
62 mutations_
.erase(it
++);
65 return 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();
74 models_with_changes
= NotifyTransactionChangingAndEnding(mutations
);
78 // Work after mutex is relased.
80 NotifyTransactionComplete(models_with_changes
);
84 ModelTypeSet
WriteTransaction::NotifyTransactionChangingAndEnding(
85 const ImmutableEntryKernelMutationMap
& mutations
) {
86 directory_
->kernel()->transaction_mutex
.AssertAcquired();
87 DCHECK(!mutations
.Get().empty());
89 WriteTransactionInfo
write_transaction_info(
90 directory_
->kernel()->next_write_transaction_id
,
91 from_here_
, writer_
, mutations
);
92 ++directory_
->kernel()->next_write_transaction_id
;
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
);
102 delegate
->HandleCalculateChangesChangeEventFromSyncer(
103 immutable_write_transaction_info
, this, &entry_changed
);
105 UpdateTransactionVersion(entry_changed
);
107 ModelTypeSet models_with_changes
=
108 delegate
->HandleTransactionEndingChangeEvent(
109 immutable_write_transaction_info
, this);
111 directory_
->kernel()->transaction_observer
.Call(FROM_HERE
,
112 &TransactionObserver::OnTransactionWrite
,
113 immutable_write_transaction_info
, models_with_changes
);
115 return models_with_changes
;
118 void WriteTransaction::NotifyTransactionComplete(
119 ModelTypeSet models_with_changes
) {
120 directory_
->kernel()->delegate
->HandleTransactionCompleteChangeEvent(
121 models_with_changes
);
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
]);
130 ModelType type
= GetModelTypeFromSpecifics(entry
.GetSpecifics());
131 if (type
< FIRST_REAL_MODEL_TYPE
)
133 if (!type_seen
.Has(type
)) {
134 directory_
->IncrementTransactionVersion(type
);
137 entry
.UpdateTransactionVersion(directory_
->GetTransactionVersion(type
));
141 if (!type_seen
.Empty() && transaction_version_
) {
142 DCHECK_EQ(1u, type_seen
.Size());
143 *transaction_version_
= directory_
->GetTransactionVersion(
144 type_seen
.First().Get());
148 WriteTransaction::~WriteTransaction() {
149 const ImmutableEntryKernelMutationMap
& mutations
= RecordMutations();
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
);
156 directory()->CheckInvariantsOnTransactionClose(this, modified_handles
);
158 // |CheckTreeInvariants| could have thrown an unrecoverable error.
159 if (unrecoverable_error_set_
) {
160 HandleUnrecoverableErrorIfSet();
165 UnlockAndNotify(mutations
);
168 #define ENUM_CASE(x) case x: return #x; break
170 std::string
WriterTagToString(WriterTag writer_tag
) {
171 switch (writer_tag
) {
174 ENUM_CASE(AUTHWATCHER
);
176 ENUM_CASE(VACUUM_AFTER_SAVE
);
177 ENUM_CASE(HANDLE_SAVE_FAILURE
);
178 ENUM_CASE(PURGE_ENTRIES
);
182 return std::string();
187 } // namespace syncable
188 } // namespace syncer