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 : BaseTransaction(location
, "WriteTransaction", writer
, directory
),
21 transaction_version_(NULL
) {
25 WriteTransaction::WriteTransaction(const tracked_objects::Location
& location
,
27 int64
* transaction_version
)
28 : BaseTransaction(location
, "WriteTransaction", SYNCAPI
, directory
),
29 transaction_version_(transaction_version
) {
31 if (transaction_version_
)
32 *transaction_version_
= kInvalidTransactionVersion
;
35 void WriteTransaction::SaveOriginal(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 EntryKernelMutation mutation
;
44 mutation
.original
= *entry
;
45 ignore_result(mutations_
.insert(it
, std::make_pair(handle
, mutation
)));
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
);
58 if (kernel
->is_dirty()) {
59 it
->second
.mutated
= *kernel
;
62 DCHECK(!it
->second
.original
.is_dirty());
63 // Not actually mutated, so erase from |mutations_|.
64 mutations_
.erase(it
++);
67 return ImmutableEntryKernelMutationMap(&mutations_
);
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();
76 models_with_changes
= NotifyTransactionChangingAndEnding(mutations
);
80 // Work after mutex is relased.
82 NotifyTransactionComplete(models_with_changes
);
86 ModelTypeSet
WriteTransaction::NotifyTransactionChangingAndEnding(
87 const ImmutableEntryKernelMutationMap
& mutations
) {
88 directory_
->kernel_
->transaction_mutex
.AssertAcquired();
89 DCHECK(!mutations
.Get().empty());
91 WriteTransactionInfo
write_transaction_info(
92 directory_
->kernel_
->next_write_transaction_id
,
93 from_here_
, writer_
, mutations
);
94 ++directory_
->kernel_
->next_write_transaction_id
;
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
);
104 delegate
->HandleCalculateChangesChangeEventFromSyncer(
105 immutable_write_transaction_info
, this, &entry_changed
);
107 UpdateTransactionVersion(entry_changed
);
109 ModelTypeSet models_with_changes
=
110 delegate
->HandleTransactionEndingChangeEvent(
111 immutable_write_transaction_info
, this);
113 directory_
->kernel_
->transaction_observer
.Call(FROM_HERE
,
114 &TransactionObserver::OnTransactionWrite
,
115 immutable_write_transaction_info
, models_with_changes
);
117 return models_with_changes
;
120 void WriteTransaction::NotifyTransactionComplete(
121 ModelTypeSet models_with_changes
) {
122 directory_
->kernel_
->delegate
->HandleTransactionCompleteChangeEvent(
123 models_with_changes
);
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
]);
132 ModelType type
= GetModelTypeFromSpecifics(entry
.Get(SPECIFICS
));
133 if (type
< FIRST_REAL_MODEL_TYPE
)
135 if (!type_seen
.Has(type
)) {
136 directory_
->IncrementTransactionVersion(type
);
139 entry
.Put(TRANSACTION_VERSION
, directory_
->GetTransactionVersion(type
));
143 if (!type_seen
.Empty() && transaction_version_
) {
144 DCHECK_EQ(1u, type_seen
.Size());
145 *transaction_version_
= directory_
->GetTransactionVersion(
146 type_seen
.First().Get());
150 WriteTransaction::~WriteTransaction() {
151 const ImmutableEntryKernelMutationMap
& mutations
= RecordMutations();
152 directory()->CheckInvariantsOnTransactionClose(this, mutations
.Get());
154 // |CheckTreeInvariants| could have thrown an unrecoverable error.
155 if (unrecoverable_error_set_
) {
156 HandleUnrecoverableErrorIfSet();
161 UnlockAndNotify(mutations
);
164 #define ENUM_CASE(x) case x: return #x; break
166 std::string
WriterTagToString(WriterTag writer_tag
) {
167 switch (writer_tag
) {
170 ENUM_CASE(AUTHWATCHER
);
172 ENUM_CASE(VACUUM_AFTER_SAVE
);
173 ENUM_CASE(HANDLE_SAVE_FAILURE
);
174 ENUM_CASE(PURGE_ENTRIES
);
183 } // namespace syncable
184 } // namespace syncer