summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-16 04:55:39 +0000
committerakalin@chromium.org <akalin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-16 04:55:39 +0000
commitfb16d7f9b150ad6893e1db386707252b0dbdeab2 (patch)
tree3e4eabb5c0cc934ba0086614e6a18e4d19f77d05
parenteb21c1d6ec3dd475a7f9e0b1541c117d9041878a (diff)
downloadchromium_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/DEPS1
-rw-r--r--chrome/browser/sync/api/sync_change_unittest.cc3
-rw-r--r--chrome/browser/sync/api/sync_data.cc59
-rw-r--r--chrome/browser/sync/api/sync_data.h42
-rw-r--r--chrome/browser/sync/js/js_arg_list.cc7
-rw-r--r--chrome/browser/sync/js/js_arg_list.h13
-rw-r--r--chrome/browser/sync/js/js_event_details.cc8
-rw-r--r--chrome/browser/sync/js/js_event_details.h13
-rw-r--r--chrome/browser/sync/js/js_transaction_observer.cc2
-rw-r--r--chrome/browser/sync/js/js_transaction_observer.h2
-rw-r--r--chrome/browser/sync/syncable/syncable.cc28
-rw-r--r--chrome/browser/sync/syncable/syncable.h33
-rw-r--r--chrome/browser/sync/syncable/transaction_observer.h2
-rw-r--r--chrome/browser/sync/util/immutable.h262
-rw-r--r--chrome/browser/sync/util/immutable_unittest.cc244
-rw-r--r--chrome/browser/sync/util/shared_value.h44
-rw-r--r--chrome/chrome.gyp2
-rw-r--r--chrome/chrome_tests.gypi1
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',
],