syncable_write_transaction.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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    EntryKernelMutation mutation;
44    mutation.original = *entry;
45    ignore_result(mutations_.insert(it, std::make_pair(handle, mutation)));
46  }
47}
48
49ImmutableEntryKernelMutationMap 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);
54    if (!kernel) {
55      NOTREACHED();
56      continue;
57    }
58    if (kernel->is_dirty()) {
59      it->second.mutated = *kernel;
60      ++it;
61    } else {
62      DCHECK(!it->second.original.is_dirty());
63      // Not actually mutated, so erase from |mutations_|.
64      mutations_.erase(it++);
65    }
66  }
67  return ImmutableEntryKernelMutationMap(&mutations_);
68}
69
70void 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();
75  if (has_mutations) {
76    models_with_changes = NotifyTransactionChangingAndEnding(mutations);
77  }
78  Unlock();
79
80  // Work after mutex is relased.
81  if (has_mutations) {
82    NotifyTransactionComplete(models_with_changes);
83  }
84}
85
86ModelTypeSet WriteTransaction::NotifyTransactionChangingAndEnding(
87    const ImmutableEntryKernelMutationMap& mutations) {
88  directory_->kernel_->transaction_mutex.AssertAcquired();
89  DCHECK(!mutations.Get().empty());
90
91  WriteTransactionInfo write_transaction_info(
92      directory_->kernel_->next_write_transaction_id,
93      from_here_, writer_, mutations);
94  ++directory_->kernel_->next_write_transaction_id;
95
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);
103  } else {
104    delegate->HandleCalculateChangesChangeEventFromSyncer(
105        immutable_write_transaction_info, this, &entry_changed);
106  }
107  UpdateTransactionVersion(entry_changed);
108
109  ModelTypeSet models_with_changes =
110      delegate->HandleTransactionEndingChangeEvent(
111          immutable_write_transaction_info, this);
112
113  directory_->kernel_->transaction_observer.Call(FROM_HERE,
114      &TransactionObserver::OnTransactionWrite,
115      immutable_write_transaction_info, models_with_changes);
116
117  return models_with_changes;
118}
119
120void WriteTransaction::NotifyTransactionComplete(
121    ModelTypeSet models_with_changes) {
122  directory_->kernel_->delegate->HandleTransactionCompleteChangeEvent(
123      models_with_changes);
124}
125
126void 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]);
131    if (entry.good()) {
132      ModelType type = GetModelTypeFromSpecifics(entry.Get(SPECIFICS));
133      if (type < FIRST_REAL_MODEL_TYPE)
134        continue;
135      if (!type_seen.Has(type)) {
136        directory_->IncrementTransactionVersion(type);
137        type_seen.Put(type);
138      }
139      entry.Put(TRANSACTION_VERSION, directory_->GetTransactionVersion(type));
140    }
141  }
142
143  if (!type_seen.Empty() && transaction_version_) {
144    DCHECK_EQ(1u, type_seen.Size());
145    *transaction_version_ = directory_->GetTransactionVersion(
146        type_seen.First().Get());
147  }
148}
149
150WriteTransaction::~WriteTransaction() {
151  const ImmutableEntryKernelMutationMap& mutations = RecordMutations();
152  directory()->CheckInvariantsOnTransactionClose(this, mutations.Get());
153
154  // |CheckTreeInvariants| could have thrown an unrecoverable error.
155  if (unrecoverable_error_set_) {
156    HandleUnrecoverableErrorIfSet();
157    Unlock();
158    return;
159  }
160
161  UnlockAndNotify(mutations);
162}
163
164#define ENUM_CASE(x) case x: return #x; break
165
166std::string WriterTagToString(WriterTag writer_tag) {
167  switch (writer_tag) {
168    ENUM_CASE(INVALID);
169    ENUM_CASE(SYNCER);
170    ENUM_CASE(AUTHWATCHER);
171    ENUM_CASE(UNITTEST);
172    ENUM_CASE(VACUUM_AFTER_SAVE);
173    ENUM_CASE(HANDLE_SAVE_FAILURE);
174    ENUM_CASE(PURGE_ENTRIES);
175    ENUM_CASE(SYNCAPI);
176  };
177  NOTREACHED();
178  return "";
179}
180
181#undef ENUM_CASE
182
183}  // namespace syncable
184}  // namespace syncer
185