diff options
author | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-16 04:55:39 +0000 |
---|---|---|
committer | akalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-09-16 04:55:39 +0000 |
commit | fb16d7f9b150ad6893e1db386707252b0dbdeab2 (patch) | |
tree | 3e4eabb5c0cc934ba0086614e6a18e4d19f77d05 | |
parent | eb21c1d6ec3dd475a7f9e0b1541c117d9041878a (diff) | |
download | chromium_src-fb16d7f9b150ad6893e1db386707252b0dbdeab2.zip chromium_src-fb16d7f9b150ad6893e1db386707252b0dbdeab2.tar.gz chromium_src-fb16d7f9b150ad6893e1db386707252b0dbdeab2.tar.bz2 |
[Sync] Rework SharedValue<T> into Immutable<T>
Rework SharedValue<T> into Immutable<T>, which behaves similarly but is
easier to use and is better documented.
Make everything that used SharedValue<T> use Immutable<T> instead.
Make SyncData use Immutable<T>.
BUG=
TEST=
Review URL: http://codereview.chromium.org/7904021
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@101455 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/sync/api/DEPS | 1 | ||||
-rw-r--r-- | chrome/browser/sync/api/sync_change_unittest.cc | 3 | ||||
-rw-r--r-- | chrome/browser/sync/api/sync_data.cc | 59 | ||||
-rw-r--r-- | chrome/browser/sync/api/sync_data.h | 42 | ||||
-rw-r--r-- | chrome/browser/sync/js/js_arg_list.cc | 7 | ||||
-rw-r--r-- | chrome/browser/sync/js/js_arg_list.h | 13 | ||||
-rw-r--r-- | chrome/browser/sync/js/js_event_details.cc | 8 | ||||
-rw-r--r-- | chrome/browser/sync/js/js_event_details.h | 13 | ||||
-rw-r--r-- | chrome/browser/sync/js/js_transaction_observer.cc | 2 | ||||
-rw-r--r-- | chrome/browser/sync/js/js_transaction_observer.h | 2 | ||||
-rw-r--r-- | chrome/browser/sync/syncable/syncable.cc | 28 | ||||
-rw-r--r-- | chrome/browser/sync/syncable/syncable.h | 33 | ||||
-rw-r--r-- | chrome/browser/sync/syncable/transaction_observer.h | 2 | ||||
-rw-r--r-- | chrome/browser/sync/util/immutable.h | 262 | ||||
-rw-r--r-- | chrome/browser/sync/util/immutable_unittest.cc | 244 | ||||
-rw-r--r-- | chrome/browser/sync/util/shared_value.h | 44 | ||||
-rw-r--r-- | chrome/chrome.gyp | 2 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 |
18 files changed, 598 insertions, 168 deletions
diff --git a/chrome/browser/sync/api/DEPS b/chrome/browser/sync/api/DEPS index 66c3ac8..8e229626 100644 --- a/chrome/browser/sync/api/DEPS +++ b/chrome/browser/sync/api/DEPS @@ -4,5 +4,6 @@ include_rules = [ "+chrome/browser/sync/api", "+chrome/browser/sync/protocol", "+chrome/browser/sync/syncable/model_type.h", + "+chrome/browser/sync/util/immutable.h", ] diff --git a/chrome/browser/sync/api/sync_change_unittest.cc b/chrome/browser/sync/api/sync_change_unittest.cc index e8cdd21e..c28a221 100644 --- a/chrome/browser/sync/api/sync_change_unittest.cc +++ b/chrome/browser/sync/api/sync_change_unittest.cc @@ -6,10 +6,11 @@ #include <string> +#include "base/memory/scoped_ptr.h" #include "base/values.h" -#include "testing/gtest/include/gtest/gtest.h" #include "chrome/browser/sync/protocol/preference_specifics.pb.h" #include "chrome/browser/sync/protocol/proto_value_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" using browser_sync::EntitySpecificsToValue; diff --git a/chrome/browser/sync/api/sync_data.cc b/chrome/browser/sync/api/sync_data.cc index d1cc86c..ad56553 100644 --- a/chrome/browser/sync/api/sync_data.cc +++ b/chrome/browser/sync/api/sync_data.cc @@ -6,34 +6,43 @@ #include "chrome/browser/sync/protocol/sync.pb.h" -SyncData::SharedSyncEntity::SharedSyncEntity( - sync_pb::SyncEntity* sync_entity) - : sync_entity_(new sync_pb::SyncEntity()){ - sync_entity_->Swap(sync_entity); +void SyncData::ImmutableSyncEntityTraits::InitializeWrapper( + Wrapper* wrapper) { + *wrapper = new sync_pb::SyncEntity(); } -const sync_pb::SyncEntity& SyncData::SharedSyncEntity::sync_entity() const { - return *sync_entity_; +void SyncData::ImmutableSyncEntityTraits::DestroyWrapper( + Wrapper* wrapper) { + delete *wrapper; } -SyncData::SharedSyncEntity::~SharedSyncEntity() {} - +const sync_pb::SyncEntity& SyncData::ImmutableSyncEntityTraits::Unwrap( + const Wrapper& wrapper) { + return *wrapper; +} -SyncData::SyncData() - : is_local_(true) { +sync_pb::SyncEntity* SyncData::ImmutableSyncEntityTraits::UnwrapMutable( + Wrapper* wrapper) { + return *wrapper; } -SyncData::~SyncData() { +void SyncData::ImmutableSyncEntityTraits::Swap(sync_pb::SyncEntity* t1, + sync_pb::SyncEntity* t2) { + t1->Swap(t2); } +SyncData::SyncData() : is_valid_(false), is_local_(true) {} + +SyncData::SyncData(sync_pb::SyncEntity* entity, bool is_local) + : is_valid_(true), is_local_(is_local), immutable_entity_(entity) {} + +SyncData::~SyncData() {} + // Static. SyncData SyncData::CreateLocalData(const std::string& sync_tag) { sync_pb::SyncEntity entity; entity.set_client_defined_unique_tag(sync_tag); - SyncData a; - a.shared_entity_ = new SharedSyncEntity(&entity); - a.is_local_ = true; - return a; + return SyncData(&entity, true); } // Static. @@ -45,10 +54,7 @@ SyncData SyncData::CreateLocalData( entity.set_client_defined_unique_tag(sync_tag); entity.set_non_unique_name(non_unique_title); entity.mutable_specifics()->CopyFrom(specifics); - SyncData a; - a.shared_entity_ = new SharedSyncEntity(&entity); - a.is_local_ = true; - return a; + return SyncData(&entity, true); } // Static. @@ -64,18 +70,15 @@ SyncData SyncData::CreateRemoteData( const sync_pb::EntitySpecifics& specifics) { sync_pb::SyncEntity entity; entity.mutable_specifics()->CopyFrom(specifics); - SyncData a; - a.shared_entity_ = new SharedSyncEntity(&entity); - a.is_local_ = false; - return a; + return SyncData(&entity, false); } bool SyncData::IsValid() const { - return (shared_entity_.get() != NULL); + return is_valid_; } const sync_pb::EntitySpecifics& SyncData::GetSpecifics() const { - return shared_entity_->sync_entity().specifics(); + return immutable_entity_.Get().specifics(); } syncable::ModelType SyncData::GetDataType() const { @@ -84,13 +87,13 @@ syncable::ModelType SyncData::GetDataType() const { const std::string& SyncData::GetTag() const { DCHECK(is_local_); - return shared_entity_->sync_entity().client_defined_unique_tag(); + return immutable_entity_.Get().client_defined_unique_tag(); } const std::string& SyncData::GetTitle() const { // TODO(zea): set this for data coming from the syncer too. - DCHECK(shared_entity_->sync_entity().has_non_unique_name()); - return shared_entity_->sync_entity().non_unique_name(); + DCHECK(immutable_entity_.Get().has_non_unique_name()); + return immutable_entity_.Get().non_unique_name(); } bool SyncData::IsLocal() const { diff --git a/chrome/browser/sync/api/sync_data.h b/chrome/browser/sync/api/sync_data.h index 7656de8..d73fdfa 100644 --- a/chrome/browser/sync/api/sync_data.h +++ b/chrome/browser/sync/api/sync_data.h @@ -9,14 +9,13 @@ #include <string> #include <vector> -#include "base/memory/ref_counted.h" -#include "base/memory/scoped_ptr.h" #include "chrome/browser/sync/syncable/model_type.h" +#include "chrome/browser/sync/util/immutable.h" namespace sync_pb { class EntitySpecifics; class SyncEntity; -} +} // namespace sync_pb typedef syncable::ModelType SyncDataType; @@ -77,30 +76,37 @@ class SyncData { // TODO(zea): Query methods for other sync properties: parent, successor, etc. private: - // A reference counted immutable SyncEntity. - class SharedSyncEntity : public - base::RefCountedThreadSafe<SharedSyncEntity> { - public: - // Takes ownership of |sync_entity|'s contents. - explicit SharedSyncEntity(sync_pb::SyncEntity* sync_entity); + // Necessary since we forward-declare sync_pb::SyncEntity; see + // comments in immutable.h. + struct ImmutableSyncEntityTraits { + typedef sync_pb::SyncEntity* Wrapper; - // Returns immutable reference to local sync entity. - const sync_pb::SyncEntity& sync_entity() const; + static void InitializeWrapper(Wrapper* wrapper); - private: - friend class base::RefCountedThreadSafe<SharedSyncEntity>; + static void DestroyWrapper(Wrapper* wrapper); - // Private due to ref counting. - ~SharedSyncEntity(); + static const sync_pb::SyncEntity& Unwrap(const Wrapper& wrapper); - scoped_ptr<sync_pb::SyncEntity> sync_entity_; + static sync_pb::SyncEntity* UnwrapMutable(Wrapper* wrapper); + + static void Swap(sync_pb::SyncEntity* t1, sync_pb::SyncEntity* t2); }; - // The actual shared sync entity being held. - scoped_refptr<SharedSyncEntity> shared_entity_; + typedef browser_sync::Immutable< + sync_pb::SyncEntity, ImmutableSyncEntityTraits> + ImmutableSyncEntity; + + // Clears |entity|. + SyncData(sync_pb::SyncEntity* entity, bool is_local); + + // Whether this SyncData holds valid data. + bool is_valid_; // Whether this data originated locally or from the syncer (remote data). bool is_local_; + + // The actual shared sync entity being held. + ImmutableSyncEntity immutable_entity_; }; #endif // CHROME_BROWSER_SYNC_API_SYNC_DATA_H_ diff --git a/chrome/browser/sync/js/js_arg_list.cc b/chrome/browser/sync/js/js_arg_list.cc index d4eaf48..097c703 100644 --- a/chrome/browser/sync/js/js_arg_list.cc +++ b/chrome/browser/sync/js/js_arg_list.cc @@ -8,15 +8,14 @@ namespace browser_sync { -JsArgList::JsArgList() : args_(new SharedListValue()) {} +JsArgList::JsArgList() {} -JsArgList::JsArgList(ListValue* args) - : args_(new SharedListValue(args)) {} +JsArgList::JsArgList(ListValue* args) : args_(args) {} JsArgList::~JsArgList() {} const ListValue& JsArgList::Get() const { - return args_->Get(); + return args_.Get(); } std::string JsArgList::ToString() const { diff --git a/chrome/browser/sync/js/js_arg_list.h b/chrome/browser/sync/js/js_arg_list.h index 2a0cda7..585aa52 100644 --- a/chrome/browser/sync/js/js_arg_list.h +++ b/chrome/browser/sync/js/js_arg_list.h @@ -10,14 +10,13 @@ #include <string> -#include "base/memory/ref_counted.h" #include "base/values.h" -#include "chrome/browser/sync/util/shared_value.h" +#include "chrome/browser/sync/util/immutable.h" namespace browser_sync { -// A thread-safe wrapper around an immutable ListValue. Used for -// passing around argument lists to different threads. +// A thin wrapper around Immutable<ListValue>. Used for passing +// around argument lists to different threads. class JsArgList { public: // Uses an empty argument list. @@ -35,9 +34,9 @@ class JsArgList { // Copy constructor and assignment operator welcome. private: - typedef SharedValue<ListValue, HasSwapMemFnTraits<ListValue> > - SharedListValue; - scoped_refptr<const SharedListValue> args_; + typedef Immutable<ListValue, HasSwapMemFnByPtr<ListValue> > + ImmutableListValue; + ImmutableListValue args_; }; } // namespace browser_sync diff --git a/chrome/browser/sync/js/js_event_details.cc b/chrome/browser/sync/js/js_event_details.cc index ae23d17..9182d24 100644 --- a/chrome/browser/sync/js/js_event_details.cc +++ b/chrome/browser/sync/js/js_event_details.cc @@ -8,16 +8,14 @@ namespace browser_sync { -JsEventDetails::JsEventDetails() - : details_(new SharedDictionaryValue()) {} +JsEventDetails::JsEventDetails() {} -JsEventDetails::JsEventDetails(DictionaryValue* details) - : details_(new SharedDictionaryValue(details)) {} +JsEventDetails::JsEventDetails(DictionaryValue* details) : details_(details) {} JsEventDetails::~JsEventDetails() {} const DictionaryValue& JsEventDetails::Get() const { - return details_->Get(); + return details_.Get(); } std::string JsEventDetails::ToString() const { diff --git a/chrome/browser/sync/js/js_event_details.h b/chrome/browser/sync/js/js_event_details.h index 293d0ee..51fba04 100644 --- a/chrome/browser/sync/js/js_event_details.h +++ b/chrome/browser/sync/js/js_event_details.h @@ -10,14 +10,13 @@ #include <string> -#include "base/memory/ref_counted.h" #include "base/values.h" -#include "chrome/browser/sync/util/shared_value.h" +#include "chrome/browser/sync/util/immutable.h" namespace browser_sync { -// A thread-safe wrapper around an immutable DictionaryValue. Used -// for passing around event details to different threads. +// A thin wrapper around Immutable<DictionaryValue>. Used for passing +// around event details to different threads. class JsEventDetails { public: // Uses an empty dictionary. @@ -35,10 +34,10 @@ class JsEventDetails { // Copy constructor and assignment operator welcome. private: - typedef SharedValue<DictionaryValue, HasSwapMemFnTraits<DictionaryValue> > - SharedDictionaryValue; + typedef Immutable<DictionaryValue, HasSwapMemFnByPtr<DictionaryValue> > + ImmutableDictionaryValue; - scoped_refptr<const SharedDictionaryValue> details_; + ImmutableDictionaryValue details_; }; } // namespace browser_sync diff --git a/chrome/browser/sync/js/js_transaction_observer.cc b/chrome/browser/sync/js/js_transaction_observer.cc index 6dfb9df..9a6d0aa 100644 --- a/chrome/browser/sync/js/js_transaction_observer.cc +++ b/chrome/browser/sync/js/js_transaction_observer.cc @@ -60,7 +60,7 @@ const size_t kMutationLimit = 300; void JsTransactionObserver::OnTransactionMutate( const tracked_objects::Location& location, const syncable::WriterTag& writer, - const syncable::SharedEntryKernelMutationMap& mutations, + const syncable::ImmutableEntryKernelMutationMap& mutations, const syncable::ModelTypeBitSet& models_with_changes) { DCHECK(non_thread_safe_.CalledOnValidThread()); if (!event_handler_.IsInitialized()) { diff --git a/chrome/browser/sync/js/js_transaction_observer.h b/chrome/browser/sync/js/js_transaction_observer.h index 6562bd5..7e8650c 100644 --- a/chrome/browser/sync/js/js_transaction_observer.h +++ b/chrome/browser/sync/js/js_transaction_observer.h @@ -39,7 +39,7 @@ class JsTransactionObserver : public syncable::TransactionObserver { virtual void OnTransactionMutate( const tracked_objects::Location& location, const syncable::WriterTag& writer, - const syncable::SharedEntryKernelMutationMap& mutations, + const syncable::ImmutableEntryKernelMutationMap& mutations, const syncable::ModelTypeBitSet& models_with_changes) OVERRIDE; virtual void OnTransactionEnd( const tracked_objects::Location& location, diff --git a/chrome/browser/sync/syncable/syncable.cc b/chrome/browser/sync/syncable/syncable.cc index 2fad69c..700ff57 100644 --- a/chrome/browser/sync/syncable/syncable.cc +++ b/chrome/browser/sync/syncable/syncable.cc @@ -90,24 +90,6 @@ std::string WriterTagToString(WriterTag writer_tag) { #undef ENUM_CASE -SharedEntryKernelMutationMap::SharedEntryKernelMutationMap() - : mutations_(new SharedMutationMap()) {} - -SharedEntryKernelMutationMap::SharedEntryKernelMutationMap( - EntryKernelMutationMap* mutations) - : mutations_(new SharedMutationMap(mutations)) {} - -SharedEntryKernelMutationMap::~SharedEntryKernelMutationMap() {} - -const EntryKernelMutationMap& SharedEntryKernelMutationMap::Get() const { - return mutations_->Get(); -} - -void SharedEntryKernelMutationMap::MutationMapTraits::Swap( - EntryKernelMutationMap* mutations1, EntryKernelMutationMap* mutations2) { - mutations1->swap(*mutations2); -} - namespace { DictionaryValue* EntryKernelMutationToValue( @@ -1228,7 +1210,7 @@ void WriteTransaction::SaveOriginal(const EntryKernel* entry) { } } -SharedEntryKernelMutationMap WriteTransaction::RecordMutations() { +ImmutableEntryKernelMutationMap WriteTransaction::RecordMutations() { dirkernel_->transaction_mutex.AssertAcquired(); for (syncable::EntryKernelMutationMap::iterator it = mutations_.begin(); it != mutations_.end(); ++it) { @@ -1239,11 +1221,11 @@ SharedEntryKernelMutationMap WriteTransaction::RecordMutations() { } it->second.mutated = *kernel; } - return SharedEntryKernelMutationMap(&mutations_); + return ImmutableEntryKernelMutationMap(&mutations_); } void WriteTransaction::UnlockAndNotify( - const SharedEntryKernelMutationMap& mutations) { + const ImmutableEntryKernelMutationMap& mutations) { // Work while transaction mutex is held. ModelTypeBitSet models_with_changes; bool has_mutations = !mutations.Get().empty(); @@ -1259,7 +1241,7 @@ void WriteTransaction::UnlockAndNotify( } ModelTypeBitSet WriteTransaction::NotifyTransactionChangingAndEnding( - const SharedEntryKernelMutationMap& mutations) { + const ImmutableEntryKernelMutationMap& mutations) { dirkernel_->transaction_mutex.AssertAcquired(); DCHECK(!mutations.Get().empty()); @@ -1289,7 +1271,7 @@ void WriteTransaction::NotifyTransactionComplete( } WriteTransaction::~WriteTransaction() { - const SharedEntryKernelMutationMap& mutations = RecordMutations(); + const ImmutableEntryKernelMutationMap& mutations = RecordMutations(); if (OFF != kInvariantCheckLevel) { const bool full_scan = (FULL_DB_VERIFICATION == kInvariantCheckLevel); diff --git a/chrome/browser/sync/syncable/syncable.h b/chrome/browser/sync/syncable/syncable.h index 1f706eb..e0fbe1d 100644 --- a/chrome/browser/sync/syncable/syncable.h +++ b/chrome/browser/sync/syncable/syncable.h @@ -32,7 +32,7 @@ #include "chrome/browser/sync/syncable/syncable_id.h" #include "chrome/browser/sync/syncable/model_type.h" #include "chrome/browser/sync/util/dbgq.h" -#include "chrome/browser/sync/util/shared_value.h" +#include "chrome/browser/sync/util/immutable.h" struct PurgeInfo; @@ -563,29 +563,8 @@ struct EntryKernelMutation { }; typedef std::map<int64, EntryKernelMutation> EntryKernelMutationMap; -// A thread-safe wrapper around an immutable mutation map. Used for -// passing around mutation maps without incurring lots of copies. -class SharedEntryKernelMutationMap { - public: - SharedEntryKernelMutationMap(); - // Takes over the data in |mutations|, leaving |mutations| empty. - explicit SharedEntryKernelMutationMap(EntryKernelMutationMap* mutations); - - ~SharedEntryKernelMutationMap(); - - const EntryKernelMutationMap& Get() const; - - private: - struct MutationMapTraits { - static void Swap(EntryKernelMutationMap* mutations1, - EntryKernelMutationMap* mutations2); - }; - - typedef browser_sync::SharedValue<EntryKernelMutationMap, MutationMapTraits> - SharedMutationMap; - - scoped_refptr<const SharedMutationMap> mutations_; -}; +typedef browser_sync::Immutable<EntryKernelMutationMap> + ImmutableEntryKernelMutationMap; // Caller owns the return value. base::ListValue* EntryKernelMutationMapToValue( @@ -1188,12 +1167,12 @@ class WriteTransaction : public BaseTransaction { private: // Clears |mutations_|. - SharedEntryKernelMutationMap RecordMutations(); + ImmutableEntryKernelMutationMap RecordMutations(); - void UnlockAndNotify(const SharedEntryKernelMutationMap& mutations); + void UnlockAndNotify(const ImmutableEntryKernelMutationMap& mutations); ModelTypeBitSet NotifyTransactionChangingAndEnding( - const SharedEntryKernelMutationMap& mutations); + const ImmutableEntryKernelMutationMap& mutations); // Only the original fields are filled in until |RecordMutations()|. // We use a mutation map instead of a kernel set to avoid copying. diff --git a/chrome/browser/sync/syncable/transaction_observer.h b/chrome/browser/sync/syncable/transaction_observer.h index 8205507..9af3445 100644 --- a/chrome/browser/sync/syncable/transaction_observer.h +++ b/chrome/browser/sync/syncable/transaction_observer.h @@ -23,7 +23,7 @@ class TransactionObserver { virtual void OnTransactionMutate( const tracked_objects::Location& location, const WriterTag& writer, - const SharedEntryKernelMutationMap& mutations, + const ImmutableEntryKernelMutationMap& mutations, const ModelTypeBitSet& models_with_changes) = 0; virtual void OnTransactionEnd( const tracked_objects::Location& location, diff --git a/chrome/browser/sync/util/immutable.h b/chrome/browser/sync/util/immutable.h new file mode 100644 index 0000000..8bb03c7 --- /dev/null +++ b/chrome/browser/sync/util/immutable.h @@ -0,0 +1,262 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Immutable<T> provides an easy, cheap, and thread-safe way to pass +// large immutable data around. +// +// For example, consider the following code: +// +// typedef std::vector<LargeObject> LargeObjectList; +// +// void ProcessStuff(const LargeObjectList& stuff) { +// for (LargeObjectList::const_iterator it = stuff.begin(); +// it != stuff.end(); ++it) { +// ... process it ... +// } +// } +// +// ... +// +// LargeObjectList my_stuff; +// ... fill my_stuff with lots of LargeObjects ... +// some_loop->PostTask(FROM_HERE, base::Bind(&ProcessStuff, my_stuff)); +// +// The last line incurs the cost of copying my_stuff, which is +// undesirable. Here's the above code re-written using Immutable<T>: +// +// void ProcessStuff( +// const browser_sync::Immutable<LargeObjectList>& stuff) { +// for (LargeObjectList::const_iterator it = stuff.Get().begin(); +// it != stuff.Get().end(); ++it) { +// ... process it ... +// } +// } +// +// ... +// +// LargeObjectList my_stuff; +// ... fill my_stuff with lots of LargeObjects ... +// some_loop->PostTask( +// FROM_HERE, base::Bind(&ProcessStuff, MakeImmutable(&my_stuff))); +// +// The last line, which resets my_stuff to a default-initialized +// state, incurs only the cost of a swap of LargeObjectLists, which is +// O(1) for most STL container implementations. The data in my_stuff +// is ref-counted (thread-safely), so it is freed as soon as +// ProcessStuff is finished. +// +// NOTE: By default, Immutable<T> relies on ADL +// (http://en.wikipedia.org/wiki/Argument-dependent_name_lookup) to +// find a swap() function for T, falling back to std::swap() when +// necessary. If you overload swap() for your type in its namespace, +// or if you specialize std::swap() for your type, (see +// http://stackoverflow.com/questions/11562/how-to-overload-stdswap +// for discussion) Immutable<T> should be able to find it. +// +// Alternatively, you could explicitly control which swap function is +// used by providing your own traits class or using one of the +// pre-defined ones below. See comments on traits below for details. +// +// NOTE: Some complexity is necessary in order to use Immutable<T> +// with forward-declared types. See comments on traits below for +// details. + +#ifndef CHROME_BROWSER_SYNC_UTIL_IMMUTABLE_H_ +#define CHROME_BROWSER_SYNC_UTIL_IMMUTABLE_H_ +#pragma once + +// For std::swap(). +#include <algorithm> + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" + +namespace browser_sync { + +namespace internal { +// This class is part of the Immutable implementation. DO NOT USE +// THIS CLASS DIRECTLY YOURSELF. + +template <typename T, typename Traits> +class ImmutableCore + : public base::RefCountedThreadSafe<ImmutableCore<T, Traits> > { + public: + // wrapper_ is always explicitly default-initialized to handle + // primitive types and the case where Traits::Wrapper == T. + + ImmutableCore() : wrapper_() { + Traits::InitializeWrapper(&wrapper_); + } + + explicit ImmutableCore(T* t) : wrapper_() { + Traits::InitializeWrapper(&wrapper_); + Traits::Swap(Traits::UnwrapMutable(&wrapper_), t); + } + + const T& Get() const { + return Traits::Unwrap(wrapper_); + } + + private: + ~ImmutableCore() { + Traits::DestroyWrapper(&wrapper_); + } + friend class base::RefCountedThreadSafe<ImmutableCore<T, Traits> >; + + // This is semantically const, but we can't mark it a such as we + // modify it in the constructor. + typename Traits::Wrapper wrapper_; + + DISALLOW_COPY_AND_ASSIGN(ImmutableCore); +}; + +} // namespace internal + +// Traits usage notes +// ------------------ +// The most common reason to use your own traits class is to provide +// your own swap method. First, consider the pre-defined traits +// classes HasSwapMemFn{ByRef,ByPtr} below. If neither of those work, +// then define your own traits class inheriting from +// DefaultImmutableTraits<YourType> (to pick up the defaults for +// everything else) and provide your own Swap() method. +// +// Another reason to use your own traits class is to be able to use +// Immutable<T> with a forward-declared type (important for protobuf +// classes, when you want to avoid headers pulling in generated +// headers). (This is why the Traits::Wrapper type exists; normally, +// Traits::Wrapper is just T itself, but that needs to be changed for +// forward-declared types.) +// +// For example, if you want to do this: +// +// my_class.h +// ---------- +// #include ".../immutable.h" +// +// // Forward declaration. +// class SomeOtherType; +// +// class MyClass { +// ... +// private: +// // Doesn't work, as defaults traits class needs SomeOtherType's +// // definition to be visible. +// Immutable<SomeOtherType> foo_; +// }; +// +// You'll have to do this: +// +// my_class.h +// ---------- +// #include ".../immutable.h" +// +// // Forward declaration. +// class SomeOtherType; +// +// class MyClass { +// ... +// private: +// struct ImmutableSomeOtherTypeTraits { +// // scoped_ptr<SomeOtherType> won't work here, either. +// typedef SomeOtherType* Wrapper; +// +// static void InitializeWrapper(Wrapper* wrapper); +// +// static void DestroyWrapper(Wrapper* wrapper); +// ... +// }; +// +// typedef Immutable<SomeOtherType, ImmutableSomeOtherTypeTraits> +// ImmutableSomeOtherType; +// +// ImmutableSomeOtherType foo_; +// }; +// +// my_class.cc +// ----------- +// #include ".../some_other_type.h" +// +// void MyClass::ImmutableSomeOtherTypeTraits::InitializeWrapper( +// Wrapper* wrapper) { +// *wrapper = new SomeOtherType(); +// } +// +// void MyClass::ImmutableSomeOtherTypeTraits::DestroyWrapper( +// Wrapper* wrapper) { +// delete *wrapper; +// } +// +// ... +// +// Also note that this incurs an additional memory allocation when you +// create an Immutable<SomeOtherType>. + +template <typename T> +struct DefaultImmutableTraits { + typedef T Wrapper; + + static void InitializeWrapper(Wrapper* wrapper) {} + + static void DestroyWrapper(Wrapper* wrapper) {} + + static const T& Unwrap(const Wrapper& wrapper) { return wrapper; } + + static T* UnwrapMutable(Wrapper* wrapper) { return wrapper; } + + static void Swap(T* t1, T* t2) { + // Uses ADL (see + // http://en.wikipedia.org/wiki/Argument-dependent_name_lookup). + using std::swap; + swap(*t1, *t2); + } +}; + +// Most STL containers have by-reference swap() member functions, +// although they usually already overload std::swap() to use those. +template <typename T> +struct HasSwapMemFnByRef : public DefaultImmutableTraits<T> { + static void Swap(T* t1, T* t2) { + t1->swap(*t2); + } +}; + +// Most Google-style objects have by-pointer Swap() member functions +// (for example, generated protocol buffer classes). +template <typename T> +struct HasSwapMemFnByPtr : public DefaultImmutableTraits<T> { + static void Swap(T* t1, T* t2) { + t1->Swap(t2); + } +}; + +template <typename T, typename Traits = DefaultImmutableTraits<T> > +class Immutable { + public: + // Puts the underlying object in a default-initialized state. + Immutable() : core_(new internal::ImmutableCore<T, Traits>()) {} + + // Copy constructor and assignment welcome. + + // Resets |t| to a default-initialized state. + explicit Immutable(T* t) + : core_(new internal::ImmutableCore<T, Traits>(t)) {} + + const T& Get() const { + return core_->Get(); + } + + private: + scoped_refptr<const internal::ImmutableCore<T, Traits> > core_; +}; + +// Helper function to avoid having to write out template arguments. +template <typename T> +Immutable<T> MakeImmutable(T* t) { + return Immutable<T>(t); +} + +} // namespace browser_sync + +#endif // CHROME_BROWSER_SYNC_UTIL_IMMUTABLE_H_ diff --git a/chrome/browser/sync/util/immutable_unittest.cc b/chrome/browser/sync/util/immutable_unittest.cc new file mode 100644 index 0000000..dc6553b --- /dev/null +++ b/chrome/browser/sync/util/immutable_unittest.cc @@ -0,0 +1,244 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/sync/util/immutable.h" + +#include <algorithm> +#include <cstddef> +#include <deque> +#include <list> +#include <set> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace browser_sync { + +// Helper class that keeps track of the token passed in at +// construction and how many times that token is copied. +class TokenCore : public base::RefCounted<TokenCore> { + public: + explicit TokenCore(const char* token) : token_(token), copy_count_(0) {} + + const char* GetToken() const { return token_; } + + void RecordCopy() { ++copy_count_; } + + int GetCopyCount() const { return copy_count_; } + + private: + friend class base::RefCounted<TokenCore>; + + ~TokenCore() {} + + const char* const token_; + int copy_count_; +}; + +enum SwapBehavior { + USE_DEFAULT_SWAP, + USE_FAST_SWAP_VIA_ADL, + USE_FAST_SWAP_VIA_SPECIALIZATION +}; + +const char kEmptyToken[] = "<empty token>"; + +// Base class for various token classes, differing in swap behavior. +template <SwapBehavior> +class TokenBase { + public: + TokenBase() : core_(new TokenCore(kEmptyToken)) {} + + explicit TokenBase(const char* token) : core_(new TokenCore(token)) {} + + TokenBase(const TokenBase& other) : core_(other.core_) { + core_->RecordCopy(); + } + + TokenBase& operator=(const TokenBase& other) { + core_ = other.core_; + core_->RecordCopy(); + return *this; + } + + const char* GetToken() const { + return core_->GetToken(); + } + + int GetCopyCount() const { + return core_->GetCopyCount(); + } + + // For associative containers. + bool operator<(const TokenBase& other) const { + return std::string(GetToken()) < std::string(other.GetToken()); + } + + // STL-style swap. + void swap(TokenBase& other) { + using std::swap; + swap(other.core_, core_); + } + + // Google-style swap. + void Swap(TokenBase* other) { + using std::swap; + swap(other->core_, core_); + } + + private: + scoped_refptr<TokenCore> core_; +}; + +typedef TokenBase<USE_DEFAULT_SWAP> Token; +typedef TokenBase<USE_FAST_SWAP_VIA_ADL> ADLToken; +typedef TokenBase<USE_FAST_SWAP_VIA_SPECIALIZATION> SpecializationToken; + +void swap(ADLToken& t1, ADLToken& t2) { + t1.Swap(&t2); +} + +} // namespace browser_sync + +// Allowed by the standard (17.4.3.1/1). +namespace std { + +template <> +void swap(browser_sync::SpecializationToken& t1, + browser_sync::SpecializationToken& t2) { + t1.Swap(&t2); +} + +} // namespace + +namespace browser_sync { +namespace { + +class ImmutableTest : public ::testing::Test {}; + +TEST_F(ImmutableTest, Int) { + int x = 5; + Immutable<int> ix(&x); + EXPECT_EQ(5, ix.Get()); + EXPECT_EQ(0, x); +} + +TEST_F(ImmutableTest, IntCopy) { + int x = 5; + Immutable<int> ix = Immutable<int>(&x); + EXPECT_EQ(5, ix.Get()); + EXPECT_EQ(0, x); +} + +TEST_F(ImmutableTest, IntAssign) { + int x = 5; + Immutable<int> ix; + EXPECT_EQ(0, ix.Get()); + ix = Immutable<int>(&x); + EXPECT_EQ(5, ix.Get()); + EXPECT_EQ(0, x); +} + +TEST_F(ImmutableTest, IntMakeImmutable) { + int x = 5; + Immutable<int> ix = MakeImmutable(&x); + EXPECT_EQ(5, ix.Get()); + EXPECT_EQ(0, x); +} + +template <typename T, typename ImmutableT> +void RunTokenTest(const char* token, bool expect_copies) { + SCOPED_TRACE(token); + T t(token); + EXPECT_EQ(token, t.GetToken()); + EXPECT_EQ(0, t.GetCopyCount()); + + ImmutableT immutable_t(&t); + EXPECT_EQ(token, immutable_t.Get().GetToken()); + EXPECT_EQ(kEmptyToken, t.GetToken()); + EXPECT_EQ(expect_copies, immutable_t.Get().GetCopyCount() > 0); + EXPECT_EQ(expect_copies, t.GetCopyCount() > 0); +} + +TEST_F(ImmutableTest, Token) { + RunTokenTest<Token, Immutable<Token> >("Token", true /* expect_copies */); +} + +TEST_F(ImmutableTest, TokenSwapMemFnByRef) { + RunTokenTest<Token, Immutable<Token, HasSwapMemFnByRef<Token> > >( + "TokenSwapMemFnByRef", false /* expect_copies */); +} + +TEST_F(ImmutableTest, TokenSwapMemFnByPtr) { + RunTokenTest<Token, Immutable<Token, HasSwapMemFnByPtr<Token> > >( + "TokenSwapMemFnByPtr", false /* expect_copies */); +} + +TEST_F(ImmutableTest, ADLToken) { + RunTokenTest<ADLToken, Immutable<ADLToken> >( + "ADLToken", false /* expect_copies */); +} + +TEST_F(ImmutableTest, SpecializationToken) { + RunTokenTest<SpecializationToken, Immutable<SpecializationToken> >( + "SpecializationToken", false /* expect_copies */); +} + +template <typename C, typename ImmutableC> +void RunTokenContainerTest(const char* token) { + SCOPED_TRACE(token); + const Token tokens[] = { Token(), Token(token) }; + const size_t token_count = arraysize(tokens); + C c(tokens, tokens + token_count); + const int copy_count = c.begin()->GetCopyCount(); + EXPECT_GT(copy_count, 0); + for (typename C::const_iterator it = c.begin(); it != c.end(); ++it) { + EXPECT_EQ(copy_count, it->GetCopyCount()); + } + + // Make sure that making the container immutable doesn't incur any + // copies of the tokens. + ImmutableC immutable_c(&c); + EXPECT_TRUE(c.empty()); + ASSERT_EQ(token_count, immutable_c.Get().size()); + int i = 0; + for (typename C::const_iterator it = c.begin(); it != c.end(); ++it) { + EXPECT_EQ(tokens[i].GetToken(), it->GetToken()); + EXPECT_EQ(copy_count, it->GetCopyCount()); + ++i; + } +} + +TEST_F(ImmutableTest, Vector) { + RunTokenContainerTest<std::vector<Token>, Immutable<std::vector<Token> > >( + "Vector"); +} + +TEST_F(ImmutableTest, VectorSwapMemFnByRef) { + RunTokenContainerTest< + std::vector<Token>, + Immutable<std::vector<Token>, HasSwapMemFnByRef<std::vector<Token> > > >( + "VectorSwapMemFnByRef"); +} + +TEST_F(ImmutableTest, Deque) { + RunTokenContainerTest<std::deque<Token>, Immutable<std::deque<Token> > >( + "Deque"); +} + +TEST_F(ImmutableTest, List) { + RunTokenContainerTest<std::list<Token>, Immutable<std::list<Token> > >( + "List"); +} + +TEST_F(ImmutableTest, Set) { + RunTokenContainerTest<std::set<Token>, Immutable<std::set<Token> > >( + "Set"); +} + +} // namespace +} // namespace browser_sync diff --git a/chrome/browser/sync/util/shared_value.h b/chrome/browser/sync/util/shared_value.h deleted file mode 100644 index bd12754..0000000 --- a/chrome/browser/sync/util/shared_value.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef CHROME_BROWSER_SYNC_UTIL_SHARED_VALUE_H_ -#define CHROME_BROWSER_SYNC_UTIL_SHARED_VALUE_H_ -#pragma once - -#include "base/memory/ref_counted.h" - -namespace browser_sync { - -template <class ValueType> -struct HasSwapMemFnTraits { - static void Swap(ValueType* t1, ValueType* t2) { - t1->Swap(t2); - } -}; - -// A ref-counted thread-safe wrapper around an immutable value. -template <class ValueType, class Traits> -class SharedValue - : public base::RefCountedThreadSafe<SharedValue<ValueType, Traits> > { - public: - SharedValue() {} - // Takes over the data in |value|, leaving |value| empty. - explicit SharedValue(ValueType* value) { - Traits::Swap(&value_, value); - } - - const ValueType& Get() const { - return value_; - } - - private: - ~SharedValue() {} - friend class base::RefCountedThreadSafe<SharedValue<ValueType, Traits> >; - - ValueType value_; -}; - -} // namespace browser_sync - -#endif // CHROME_BROWSER_SYNC_UTIL_SHARED_VALUE_H_ diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index 5ee8fd1..696daf4 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -705,13 +705,13 @@ 'browser/sync/util/dbgq.h', 'browser/sync/util/extensions_activity_monitor.cc', 'browser/sync/util/extensions_activity_monitor.h', + 'browser/sync/util/immutable.h', 'browser/sync/util/logging.cc', 'browser/sync/util/logging.h', 'browser/sync/util/nigori.cc', 'browser/sync/util/nigori.h', 'browser/sync/util/oauth.cc', 'browser/sync/util/oauth.h', - 'browser/sync/util/shared_value.h', 'browser/sync/util/sqlite_utils.cc', 'browser/sync/util/sqlite_utils.h', 'browser/sync/util/user_settings.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 8f70e08..b60b2fa 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -2949,6 +2949,7 @@ 'browser/sync/util/data_encryption_unittest.cc', 'browser/sync/util/extensions_activity_monitor_unittest.cc', 'browser/sync/util/protobuf_unittest.cc', + 'browser/sync/util/immutable_unittest.cc', 'browser/sync/util/user_settings_unittest.cc', 'browser/sync/util/weak_handle_unittest.cc', ], |