Home | History | Annotate | Download | only in syncable
      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 
     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 
     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 
     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 
     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 
     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 
     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 
    118 void WriteTransaction::NotifyTransactionComplete(
    119     ModelTypeSet models_with_changes) {
    120   directory_->kernel_->delegate->HandleTransactionCompleteChangeEvent(
    121       models_with_changes);
    122 }
    123 
    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 
    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 
    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