syncable_write_transaction.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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
13namespace syncer {
14namespace syncable {
15
16const int64 kInvalidTransactionVersion = -1;
17
18WriteTransaction::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
25WriteTransaction::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
35void 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    mutations_[handle].original = *entry;
44  }
45}
46
47ImmutableEntryKernelMutationMap 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
68void 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
84ModelTypeSet 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
118void WriteTransaction::NotifyTransactionComplete(
119    ModelTypeSet models_with_changes) {
120  directory_->kernel_->delegate->HandleTransactionCompleteChangeEvent(
121      models_with_changes);
122}
123
124void 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.Get(SPECIFICS));
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
148WriteTransaction::~WriteTransaction() {
149  const ImmutableEntryKernelMutationMap& mutations = RecordMutations();
150  directory()->CheckInvariantsOnTransactionClose(this, mutations.Get());
151
152  // |CheckTreeInvariants| could have thrown an unrecoverable error.
153  if (unrecoverable_error_set_) {
154    HandleUnrecoverableErrorIfSet();
155    Unlock();
156    return;
157  }
158
159  UnlockAndNotify(mutations);
160}
161
162#define ENUM_CASE(x) case x: return #x; break
163
164std::string WriterTagToString(WriterTag writer_tag) {
165  switch (writer_tag) {
166    ENUM_CASE(INVALID);
167    ENUM_CASE(SYNCER);
168    ENUM_CASE(AUTHWATCHER);
169    ENUM_CASE(UNITTEST);
170    ENUM_CASE(VACUUM_AFTER_SAVE);
171    ENUM_CASE(HANDLE_SAVE_FAILURE);
172    ENUM_CASE(PURGE_ENTRIES);
173    ENUM_CASE(SYNCAPI);
174  };
175  NOTREACHED();
176  return std::string();
177}
178
179#undef ENUM_CASE
180
181}  // namespace syncable
182}  // namespace syncer
183