summaryrefslogtreecommitdiffstats
path: root/sync/internal_api/public/util
diff options
context:
space:
mode:
Diffstat (limited to 'sync/internal_api/public/util')
-rw-r--r--sync/internal_api/public/util/experiments.h28
-rw-r--r--sync/internal_api/public/util/immutable.h262
-rw-r--r--sync/internal_api/public/util/immutable_unittest.cc250
-rw-r--r--sync/internal_api/public/util/report_unrecoverable_error_function.h19
-rw-r--r--sync/internal_api/public/util/unrecoverable_error_handler.h30
-rw-r--r--sync/internal_api/public/util/unrecoverable_error_info.cc44
-rw-r--r--sync/internal_api/public/util/unrecoverable_error_info.h41
-rw-r--r--sync/internal_api/public/util/weak_handle.cc36
-rw-r--r--sync/internal_api/public/util/weak_handle.h379
-rw-r--r--sync/internal_api/public/util/weak_handle_unittest.cc326
10 files changed, 1415 insertions, 0 deletions
diff --git a/sync/internal_api/public/util/experiments.h b/sync/internal_api/public/util/experiments.h
new file mode 100644
index 0000000..1439e2b
--- /dev/null
+++ b/sync/internal_api/public/util/experiments.h
@@ -0,0 +1,28 @@
+// Copyright (c) 2012 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 SYNC_UTIL_EXPERIMENTS_
+#define SYNC_UTIL_EXPERIMENTS_
+#pragma once
+
+#include "sync/internal_api/public/syncable/model_type.h"
+
+namespace browser_sync {
+
+// A structure to hold the enable status of experimental sync features.
+struct Experiments {
+ Experiments() : sync_tab_favicons(false) {}
+
+ bool Matches(const Experiments& rhs) {
+ return (sync_tab_favicons == rhs.sync_tab_favicons);
+ }
+
+ // Enable syncing of favicons within tab sync (only has an effect if tab sync
+ // is already enabled). This takes effect on the next restart.
+ bool sync_tab_favicons;
+};
+
+}
+
+#endif // SYNC_UTIL_EXPERIMENTS_
diff --git a/sync/internal_api/public/util/immutable.h b/sync/internal_api/public/util/immutable.h
new file mode 100644
index 0000000..6624b90
--- /dev/null
+++ b/sync/internal_api/public/util/immutable.h
@@ -0,0 +1,262 @@
+// Copyright (c) 2012 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 SYNC_UTIL_IMMUTABLE_H_
+#define 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 // SYNC_UTIL_IMMUTABLE_H_
diff --git a/sync/internal_api/public/util/immutable_unittest.cc b/sync/internal_api/public/util/immutable_unittest.cc
new file mode 100644
index 0000000..ea0b29b
--- /dev/null
+++ b/sync/internal_api/public/util/immutable_unittest.cc
@@ -0,0 +1,250 @@
+// Copyright (c) 2012 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 "sync/internal_api/public/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");
+}
+
+// http://crbug.com/129128
+#if defined(OS_WIN)
+#define MAYBE_Deque DISABLED_Deque
+#else
+#define MAYBE_Deque Deque
+#endif
+TEST_F(ImmutableTest, MAYBE_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/sync/internal_api/public/util/report_unrecoverable_error_function.h b/sync/internal_api/public/util/report_unrecoverable_error_function.h
new file mode 100644
index 0000000..ead73f0
--- /dev/null
+++ b/sync/internal_api/public/util/report_unrecoverable_error_function.h
@@ -0,0 +1,19 @@
+// Copyright (c) 2012 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 SYNC_UTIL_REPORT_UNRECOVERABLE_ERROR_FUNCTION_H_
+#define SYNC_UTIL_REPORT_UNRECOVERABLE_ERROR_FUNCTION_H_
+#pragma once
+
+namespace browser_sync {
+
+// A ReportUnrecoverableErrorFunction is a function that is called
+// immediately when an unrecoverable error is encountered. Unlike
+// UnrecoverableErrorHandler, it should just log the error and any
+// context surrounding it.
+typedef void (*ReportUnrecoverableErrorFunction)(void);
+
+} // namespace browser_sync
+
+#endif // SYNC_UTIL_REPORT_UNRECOVERABLE_ERROR_FUNCTION_H_
diff --git a/sync/internal_api/public/util/unrecoverable_error_handler.h b/sync/internal_api/public/util/unrecoverable_error_handler.h
new file mode 100644
index 0000000..aaca1e9
--- /dev/null
+++ b/sync/internal_api/public/util/unrecoverable_error_handler.h
@@ -0,0 +1,30 @@
+// Copyright (c) 2012 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 SYNC_UTIL_UNRECOVERABLE_ERROR_HANDLER_H_
+#define SYNC_UTIL_UNRECOVERABLE_ERROR_HANDLER_H_
+#pragma once
+
+#include <string>
+
+#include "base/location.h"
+
+namespace browser_sync {
+
+class UnrecoverableErrorHandler {
+ public:
+ // Call this when normal operation detects that the chrome model and the
+ // syncer model are inconsistent, or similar. The ProfileSyncService will
+ // try to avoid doing any work to avoid crashing or corrupting things
+ // further, and will report an error status if queried.
+ virtual void OnUnrecoverableError(const tracked_objects::Location& from_here,
+ const std::string& message) = 0;
+ protected:
+ virtual ~UnrecoverableErrorHandler() { }
+};
+
+}
+
+#endif // SYNC_UTIL_UNRECOVERABLE_ERROR_HANDLER_H_
+
diff --git a/sync/internal_api/public/util/unrecoverable_error_info.cc b/sync/internal_api/public/util/unrecoverable_error_info.cc
new file mode 100644
index 0000000..369fa3e
--- /dev/null
+++ b/sync/internal_api/public/util/unrecoverable_error_info.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2012 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 "sync/internal_api/public/util/unrecoverable_error_info.h"
+
+namespace browser_sync {
+
+UnrecoverableErrorInfo::UnrecoverableErrorInfo()
+ : is_set_(false) {
+}
+
+UnrecoverableErrorInfo::UnrecoverableErrorInfo(
+ const tracked_objects::Location& location,
+ const std::string& message)
+ : location_(location),
+ message_(message),
+ is_set_(true) {
+}
+
+UnrecoverableErrorInfo::~UnrecoverableErrorInfo() {
+}
+
+void UnrecoverableErrorInfo::Reset(
+ const tracked_objects::Location& location,
+ const std::string& message) {
+ location_ = location;
+ message_ = message;
+ is_set_ = true;
+}
+
+bool UnrecoverableErrorInfo::IsSet() const {
+ return is_set_;
+}
+
+const tracked_objects::Location& UnrecoverableErrorInfo::location() const {
+ return location_;
+}
+
+const std::string& UnrecoverableErrorInfo::message() const {
+ return message_;
+}
+
+} // namespace browser_sync
diff --git a/sync/internal_api/public/util/unrecoverable_error_info.h b/sync/internal_api/public/util/unrecoverable_error_info.h
new file mode 100644
index 0000000..64b780a
--- /dev/null
+++ b/sync/internal_api/public/util/unrecoverable_error_info.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2012 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 SYNC_UTIL_UNRECOVERABLE_ERROR_INFO_H_
+#define SYNC_UTIL_UNRECOVERABLE_ERROR_INFO_H_
+// TODO(lipalani): Figure out the right location for this class so it is
+// accessible outside of sync engine as well.
+#pragma once
+
+#include <string>
+
+#include "base/location.h"
+
+namespace browser_sync {
+
+class UnrecoverableErrorInfo {
+ public:
+ UnrecoverableErrorInfo();
+ UnrecoverableErrorInfo(
+ const tracked_objects::Location& location,
+ const std::string& message);
+ ~UnrecoverableErrorInfo();
+
+ void Reset(const tracked_objects::Location& location,
+ const std::string& message);
+
+ bool IsSet() const;
+
+ const tracked_objects::Location& location() const;
+ const std::string& message() const;
+
+ private:
+ tracked_objects::Location location_;
+ std::string message_;
+ bool is_set_;
+};
+
+} // namespace browser_sync
+
+#endif // SYNC_UTIL_UNRECOVERABLE_ERROR_INFO_H_
diff --git a/sync/internal_api/public/util/weak_handle.cc b/sync/internal_api/public/util/weak_handle.cc
new file mode 100644
index 0000000..136fc58
--- /dev/null
+++ b/sync/internal_api/public/util/weak_handle.cc
@@ -0,0 +1,36 @@
+// Copyright (c) 2012 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 "sync/internal_api/public/util/weak_handle.h"
+
+#include <sstream>
+
+#include "base/callback.h"
+#include "base/location.h"
+#include "base/message_loop_proxy.h"
+
+namespace browser_sync {
+
+namespace internal {
+
+WeakHandleCoreBase::WeakHandleCoreBase()
+ : owner_loop_proxy_(base::MessageLoopProxy::current()) {}
+
+bool WeakHandleCoreBase::IsOnOwnerThread() const {
+ return owner_loop_proxy_->BelongsToCurrentThread();
+}
+
+WeakHandleCoreBase::~WeakHandleCoreBase() {}
+
+void WeakHandleCoreBase::PostToOwnerThread(
+ const tracked_objects::Location& from_here,
+ const base::Closure& fn) const {
+ if (!owner_loop_proxy_->PostTask(from_here, fn)) {
+ DVLOG(1) << "Could not post task from " << from_here.ToString();
+ }
+}
+
+} // namespace internal
+
+} // namespace base
diff --git a/sync/internal_api/public/util/weak_handle.h b/sync/internal_api/public/util/weak_handle.h
new file mode 100644
index 0000000..653da60
--- /dev/null
+++ b/sync/internal_api/public/util/weak_handle.h
@@ -0,0 +1,379 @@
+// Copyright (c) 2012 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.
+
+// Weak handles provides a way to refer to weak pointers from another
+// thread. This is useful because it is not safe to reference a weak
+// pointer from a thread other than the thread on which it was
+// created.
+//
+// Weak handles can be passed across threads, so for example, you can
+// use them to do the "real" work on one thread and get notified on
+// another thread:
+//
+// class FooIOWorker {
+// public:
+// FooIOWorker(const WeakHandle<Foo>& foo) : foo_(foo) {}
+//
+// void OnIOStart() {
+// foo_.Call(FROM_HERE, &Foo::OnIOStart);
+// }
+//
+// void OnIOEvent(IOEvent e) {
+// foo_.Call(FROM_HERE, &Foo::OnIOEvent, e);
+// }
+//
+// void OnIOError(IOError err) {
+// foo_.Call(FROM_HERE, &Foo::OnIOError, err);
+// }
+//
+// private:
+// const WeakHandle<Foo> foo_;
+// };
+//
+// class Foo : public SupportsWeakPtr<Foo>, public NonThreadSafe {
+// public:
+// Foo() {
+// SpawnFooIOWorkerOnIOThread(base::MakeWeakHandle(AsWeakPtr()));
+// }
+//
+// /* Will always be called on the correct thread, and only if this
+// object hasn't been destroyed. */
+// void OnIOStart() { DCHECK(CalledOnValidThread(); ... }
+// void OnIOEvent(IOEvent e) { DCHECK(CalledOnValidThread(); ... }
+// void OnIOError(IOError err) { DCHECK(CalledOnValidThread(); ... }
+// };
+
+#ifndef SYNC_UTIL_WEAK_HANDLE_H_
+#define SYNC_UTIL_WEAK_HANDLE_H_
+#pragma once
+
+#include <cstddef>
+
+#include "base/basictypes.h"
+#include "base/bind.h"
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+
+namespace base {
+class MessageLoopProxy;
+} // namespace base
+
+namespace tracked_objects {
+class Location;
+} // namespace tracked_objects
+
+namespace browser_sync {
+
+template <typename T> class WeakHandle;
+
+namespace internal {
+// These classes are part of the WeakHandle implementation. DO NOT
+// USE THESE CLASSES DIRECTLY YOURSELF.
+
+// Adapted from base/callback_internal.h.
+
+template <typename T>
+struct ParamTraits {
+ typedef const T& ForwardType;
+};
+
+template <typename T>
+struct ParamTraits<T&> {
+ typedef T& ForwardType;
+};
+
+template <typename T, size_t n>
+struct ParamTraits<T[n]> {
+ typedef const T* ForwardType;
+};
+
+template <typename T>
+struct ParamTraits<T[]> {
+ typedef const T* ForwardType;
+};
+
+// Base class for WeakHandleCore<T> to avoid template bloat. Handles
+// the interaction with the owner thread and its message loop.
+class WeakHandleCoreBase {
+ public:
+ // Assumes the current thread is the owner thread.
+ WeakHandleCoreBase();
+
+ // May be called on any thread.
+ bool IsOnOwnerThread() const;
+
+ protected:
+ // May be destroyed on any thread.
+ ~WeakHandleCoreBase();
+
+ // May be called on any thread.
+ void PostToOwnerThread(const tracked_objects::Location& from_here,
+ const base::Closure& fn) const;
+
+ private:
+ // May be used on any thread.
+ const scoped_refptr<base::MessageLoopProxy> owner_loop_proxy_;
+
+ DISALLOW_COPY_AND_ASSIGN(WeakHandleCoreBase);
+};
+
+// WeakHandleCore<T> contains all the logic for WeakHandle<T>.
+template <typename T>
+class WeakHandleCore
+ : public WeakHandleCoreBase,
+ public base::RefCountedThreadSafe<WeakHandleCore<T> > {
+ public:
+ // Must be called on |ptr|'s owner thread, which is assumed to be
+ // the current thread.
+ explicit WeakHandleCore(const base::WeakPtr<T>& ptr) : ptr_(ptr) {}
+
+ // Must be called on |ptr_|'s owner thread.
+ base::WeakPtr<T> Get() const {
+ CHECK(IsOnOwnerThread());
+ return ptr_;
+ }
+
+ // Call(...) may be called on any thread, but all its arguments
+ // should be safe to be bound and copied across threads.
+
+ template <typename U>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(void)) const {
+ PostToOwnerThread(
+ from_here,
+ Bind(&WeakHandleCore::template DoCall0<U>, this, fn));
+ }
+
+ template <typename U, typename A1>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(A1),
+ typename ParamTraits<A1>::ForwardType a1) const {
+ PostToOwnerThread(
+ from_here,
+ Bind(&WeakHandleCore::template DoCall1<U, A1>,
+ this, fn, a1));
+ }
+
+ template <typename U, typename A1, typename A2>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(A1, A2),
+ typename ParamTraits<A1>::ForwardType a1,
+ typename ParamTraits<A2>::ForwardType a2) const {
+ PostToOwnerThread(
+ from_here,
+ Bind(&WeakHandleCore::template DoCall2<U, A1, A2>,
+ this, fn, a1, a2));
+ }
+
+ template <typename U, typename A1, typename A2, typename A3>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(A1, A2, A3),
+ typename ParamTraits<A1>::ForwardType a1,
+ typename ParamTraits<A2>::ForwardType a2,
+ typename ParamTraits<A3>::ForwardType a3) const {
+ PostToOwnerThread(
+ from_here,
+ Bind(&WeakHandleCore::template DoCall3<U, A1, A2, A3>,
+ this, fn, a1, a2, a3));
+ }
+
+ template <typename U, typename A1, typename A2, typename A3, typename A4>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(A1, A2, A3, A4),
+ typename ParamTraits<A1>::ForwardType a1,
+ typename ParamTraits<A2>::ForwardType a2,
+ typename ParamTraits<A3>::ForwardType a3,
+ typename ParamTraits<A4>::ForwardType a4) const {
+ PostToOwnerThread(
+ from_here,
+ Bind(&WeakHandleCore::template DoCall4<U, A1, A2, A3, A4>,
+ this, fn, a1, a2, a3, a4));
+ }
+
+ private:
+ friend class base::RefCountedThreadSafe<WeakHandleCore<T> >;
+
+ // May be destroyed on any thread.
+ ~WeakHandleCore() {}
+
+ // GCC 4.2.1 on OS X gets confused if all the DoCall functions are
+ // named the same, so we distinguish them.
+
+ template <typename U>
+ void DoCall0(void (U::*fn)(void)) const {
+ CHECK(IsOnOwnerThread());
+ if (!Get()) {
+ return;
+ }
+ (Get()->*fn)();
+ }
+
+ template <typename U, typename A1>
+ void DoCall1(void (U::*fn)(A1),
+ typename ParamTraits<A1>::ForwardType a1) const {
+ CHECK(IsOnOwnerThread());
+ if (!Get()) {
+ return;
+ }
+ (Get()->*fn)(a1);
+ }
+
+ template <typename U, typename A1, typename A2>
+ void DoCall2(void (U::*fn)(A1, A2),
+ typename ParamTraits<A1>::ForwardType a1,
+ typename ParamTraits<A2>::ForwardType a2) const {
+ CHECK(IsOnOwnerThread());
+ if (!Get()) {
+ return;
+ }
+ (Get()->*fn)(a1, a2);
+ }
+
+ template <typename U, typename A1, typename A2, typename A3>
+ void DoCall3(void (U::*fn)(A1, A2, A3),
+ typename ParamTraits<A1>::ForwardType a1,
+ typename ParamTraits<A2>::ForwardType a2,
+ typename ParamTraits<A3>::ForwardType a3) const {
+ CHECK(IsOnOwnerThread());
+ if (!Get()) {
+ return;
+ }
+ (Get()->*fn)(a1, a2, a3);
+ }
+
+ template <typename U, typename A1, typename A2, typename A3, typename A4>
+ void DoCall4(void (U::*fn)(A1, A2, A3, A4),
+ typename ParamTraits<A1>::ForwardType a1,
+ typename ParamTraits<A2>::ForwardType a2,
+ typename ParamTraits<A3>::ForwardType a3,
+ typename ParamTraits<A4>::ForwardType a4) const {
+ CHECK(IsOnOwnerThread());
+ if (!Get()) {
+ return;
+ }
+ (Get()->*fn)(a1, a2, a3, a4);
+ }
+
+ // Must be dereferenced only on the owner thread. May be destroyed
+ // from any thread.
+ base::WeakPtr<T> ptr_;
+
+ DISALLOW_COPY_AND_ASSIGN(WeakHandleCore);
+};
+
+} // namespace internal
+
+// May be destroyed on any thread.
+// Copying and assignment are welcome.
+template <typename T>
+class WeakHandle {
+ public:
+ // Creates an uninitialized WeakHandle.
+ WeakHandle() {}
+
+ // Creates an initialized WeakHandle from |ptr|.
+ explicit WeakHandle(const base::WeakPtr<T>& ptr)
+ : core_(new internal::WeakHandleCore<T>(ptr)) {}
+
+ // Allow conversion from WeakHandle<U> to WeakHandle<T> if U is
+ // convertible to T, but we *must* be on |other|'s owner thread.
+ // Note that this doesn't override the regular copy constructor, so
+ // that one can be called on any thread.
+ template <typename U>
+ WeakHandle(const browser_sync::WeakHandle<U>& other) // NOLINT
+ : core_(
+ other.IsInitialized() ?
+ new internal::WeakHandleCore<T>(other.Get()) :
+ NULL) {}
+
+ // Returns true iff this WeakHandle is initialized. Note that being
+ // initialized isn't a guarantee that the underlying object is still
+ // alive.
+ bool IsInitialized() const {
+ return core_.get() != NULL;
+ }
+
+ // Resets to an uninitialized WeakHandle.
+ void Reset() {
+ core_ = NULL;
+ }
+
+ // Must be called only on the underlying object's owner thread.
+ base::WeakPtr<T> Get() const {
+ CHECK(IsInitialized());
+ CHECK(core_->IsOnOwnerThread());
+ return core_->Get();
+ }
+
+ // Call(...) may be called on any thread, but all its arguments
+ // should be safe to be bound and copied across threads.
+
+ template <typename U>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(void)) const {
+ CHECK(IsInitialized());
+ core_->Call(from_here, fn);
+ }
+
+ template <typename U, typename A1>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(A1),
+ typename internal::ParamTraits<A1>::ForwardType a1) const {
+ CHECK(IsInitialized());
+ core_->Call(from_here, fn, a1);
+ }
+
+ template <typename U, typename A1, typename A2>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(A1, A2),
+ typename internal::ParamTraits<A1>::ForwardType a1,
+ typename internal::ParamTraits<A2>::ForwardType a2) const {
+ CHECK(IsInitialized());
+ core_->Call(from_here, fn, a1, a2);
+ }
+
+ template <typename U, typename A1, typename A2, typename A3>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(A1, A2, A3),
+ typename internal::ParamTraits<A1>::ForwardType a1,
+ typename internal::ParamTraits<A2>::ForwardType a2,
+ typename internal::ParamTraits<A3>::ForwardType a3) const {
+ CHECK(IsInitialized());
+ core_->Call(from_here, fn, a1, a2, a3);
+ }
+
+ template <typename U, typename A1, typename A2, typename A3, typename A4>
+ void Call(const tracked_objects::Location& from_here,
+ void (U::*fn)(A1, A2, A3, A4),
+ typename internal::ParamTraits<A1>::ForwardType a1,
+ typename internal::ParamTraits<A2>::ForwardType a2,
+ typename internal::ParamTraits<A3>::ForwardType a3,
+ typename internal::ParamTraits<A4>::ForwardType a4) const {
+ CHECK(IsInitialized());
+ core_->Call(from_here, fn, a1, a2, a3, a4);
+ }
+
+ private:
+ FRIEND_TEST_ALL_PREFIXES(WeakHandleTest,
+ TypeConversionConstructor);
+ FRIEND_TEST_ALL_PREFIXES(WeakHandleTest,
+ TypeConversionConstructorAssignment);
+
+ scoped_refptr<internal::WeakHandleCore<T> > core_;
+};
+
+// Makes a WeakHandle from a WeakPtr.
+template <typename T>
+WeakHandle<T> MakeWeakHandle(const base::WeakPtr<T>& ptr) {
+ return WeakHandle<T>(ptr);
+}
+
+} // namespace browser_sync
+
+#endif // SYNC_UTIL_WEAK_HANDLE_H_
diff --git a/sync/internal_api/public/util/weak_handle_unittest.cc b/sync/internal_api/public/util/weak_handle_unittest.cc
new file mode 100644
index 0000000..af919ad
--- /dev/null
+++ b/sync/internal_api/public/util/weak_handle_unittest.cc
@@ -0,0 +1,326 @@
+// Copyright (c) 2012 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 "sync/internal_api/public/util/weak_handle.h"
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/location.h"
+#include "base/memory/weak_ptr.h"
+#include "base/message_loop.h"
+#include "base/threading/thread.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace browser_sync {
+
+using ::testing::_;
+using ::testing::SaveArg;
+using ::testing::StrictMock;
+
+class Base {
+ public:
+ Base() : weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {}
+
+ WeakHandle<Base> AsWeakHandle() {
+ return MakeWeakHandle(weak_ptr_factory_.GetWeakPtr());
+ }
+
+ void Kill() {
+ weak_ptr_factory_.InvalidateWeakPtrs();
+ }
+
+ MOCK_METHOD0(Test, void());
+ MOCK_METHOD1(Test1, void(const int&));
+ MOCK_METHOD2(Test2, void(const int&, Base*));
+ MOCK_METHOD3(Test3, void(const int&, Base*, float));
+ MOCK_METHOD4(Test4, void(const int&, Base*, float, const char*));
+
+ MOCK_METHOD1(TestWithSelf, void(const WeakHandle<Base>&));
+
+ private:
+ base::WeakPtrFactory<Base> weak_ptr_factory_;
+};
+
+class Derived : public Base, public base::SupportsWeakPtr<Derived> {};
+
+class WeakHandleTest : public ::testing::Test {
+ protected:
+ virtual void TearDown() {
+ // Process any last-minute posted tasks.
+ PumpLoop();
+ }
+
+ void PumpLoop() {
+ message_loop_.RunAllPending();
+ }
+
+ static void CallTestFromOtherThread(tracked_objects::Location from_here,
+ const WeakHandle<Base>& h) {
+ base::Thread t("Test thread");
+ ASSERT_TRUE(t.Start());
+ t.message_loop()->PostTask(
+ from_here, base::Bind(&WeakHandleTest::CallTest, from_here, h));
+ }
+
+ private:
+ static void CallTest(tracked_objects::Location from_here,
+ const WeakHandle<Base>& h) {
+ h.Call(from_here, &Base::Test);
+ }
+
+ MessageLoop message_loop_;
+};
+
+TEST_F(WeakHandleTest, Uninitialized) {
+ // Default.
+ WeakHandle<int> h;
+ EXPECT_FALSE(h.IsInitialized());
+ // Copy.
+ {
+ WeakHandle<int> h2(h);
+ EXPECT_FALSE(h2.IsInitialized());
+ }
+ // Assign.
+ {
+ WeakHandle<int> h2;
+ h2 = h;
+ EXPECT_FALSE(h.IsInitialized());
+ }
+}
+
+TEST_F(WeakHandleTest, InitializedAfterDestroy) {
+ WeakHandle<Base> h;
+ {
+ StrictMock<Base> b;
+ h = b.AsWeakHandle();
+ }
+ EXPECT_TRUE(h.IsInitialized());
+ EXPECT_FALSE(h.Get());
+}
+
+TEST_F(WeakHandleTest, InitializedAfterInvalidate) {
+ StrictMock<Base> b;
+ WeakHandle<Base> h = b.AsWeakHandle();
+ b.Kill();
+ EXPECT_TRUE(h.IsInitialized());
+ EXPECT_FALSE(h.Get());
+}
+
+TEST_F(WeakHandleTest, Call) {
+ StrictMock<Base> b;
+ const char test_str[] = "test";
+ EXPECT_CALL(b, Test());
+ EXPECT_CALL(b, Test1(5));
+ EXPECT_CALL(b, Test2(5, &b));
+ EXPECT_CALL(b, Test3(5, &b, 5));
+ EXPECT_CALL(b, Test4(5, &b, 5, test_str));
+
+ WeakHandle<Base> h = b.AsWeakHandle();
+ EXPECT_TRUE(h.IsInitialized());
+
+ // Should run.
+ h.Call(FROM_HERE, &Base::Test);
+ h.Call(FROM_HERE, &Base::Test1, 5);
+ h.Call(FROM_HERE, &Base::Test2, 5, &b);
+ h.Call(FROM_HERE, &Base::Test3, 5, &b, 5);
+ h.Call(FROM_HERE, &Base::Test4, 5, &b, 5, test_str);
+ PumpLoop();
+}
+
+TEST_F(WeakHandleTest, CallAfterDestroy) {
+ {
+ StrictMock<Base> b;
+ EXPECT_CALL(b, Test()).Times(0);
+
+ WeakHandle<Base> h = b.AsWeakHandle();
+ EXPECT_TRUE(h.IsInitialized());
+
+ // Should not run.
+ h.Call(FROM_HERE, &Base::Test);
+ }
+ PumpLoop();
+}
+
+TEST_F(WeakHandleTest, CallAfterInvalidate) {
+ StrictMock<Base> b;
+ EXPECT_CALL(b, Test()).Times(0);
+
+ WeakHandle<Base> h = b.AsWeakHandle();
+ EXPECT_TRUE(h.IsInitialized());
+
+ // Should not run.
+ h.Call(FROM_HERE, &Base::Test);
+
+ b.Kill();
+ PumpLoop();
+}
+
+TEST_F(WeakHandleTest, CallThreaded) {
+ StrictMock<Base> b;
+ EXPECT_CALL(b, Test());
+
+ WeakHandle<Base> h = b.AsWeakHandle();
+ // Should run.
+ CallTestFromOtherThread(FROM_HERE, h);
+ PumpLoop();
+}
+
+TEST_F(WeakHandleTest, CallAfterDestroyThreaded) {
+ WeakHandle<Base> h;
+ {
+ StrictMock<Base> b;
+ EXPECT_CALL(b, Test()).Times(0);
+ h = b.AsWeakHandle();
+ }
+
+ // Should not run.
+ CallTestFromOtherThread(FROM_HERE, h);
+ PumpLoop();
+}
+
+TEST_F(WeakHandleTest, CallAfterInvalidateThreaded) {
+ StrictMock<Base> b;
+ EXPECT_CALL(b, Test()).Times(0);
+
+ WeakHandle<Base> h = b.AsWeakHandle();
+ b.Kill();
+ // Should not run.
+ CallTestFromOtherThread(FROM_HERE, h);
+ PumpLoop();
+}
+
+TEST_F(WeakHandleTest, DeleteOnOtherThread) {
+ StrictMock<Base> b;
+ EXPECT_CALL(b, Test()).Times(0);
+
+ WeakHandle<Base>* h = new WeakHandle<Base>(b.AsWeakHandle());
+
+ {
+ base::Thread t("Test thread");
+ ASSERT_TRUE(t.Start());
+ t.message_loop()->DeleteSoon(FROM_HERE, h);
+ }
+
+ PumpLoop();
+}
+
+void CallTestWithSelf(const WeakHandle<Base>& b1) {
+ StrictMock<Base> b2;
+ b1.Call(FROM_HERE, &Base::TestWithSelf, b2.AsWeakHandle());
+}
+
+TEST_F(WeakHandleTest, WithDestroyedThread) {
+ StrictMock<Base> b1;
+ WeakHandle<Base> b2;
+ EXPECT_CALL(b1, TestWithSelf(_)).WillOnce(SaveArg<0>(&b2));
+
+ {
+ base::Thread t("Test thread");
+ ASSERT_TRUE(t.Start());
+ t.message_loop()->PostTask(FROM_HERE,
+ base::Bind(&CallTestWithSelf,
+ b1.AsWeakHandle()));
+ }
+
+ // Calls b1.TestWithSelf().
+ PumpLoop();
+
+ // Shouldn't do anything, since the thread is gone.
+ b2.Call(FROM_HERE, &Base::Test);
+
+ // |b2| shouldn't leak when it's destroyed, even if the original
+ // thread is gone.
+}
+
+TEST_F(WeakHandleTest, InitializedAcrossCopyAssign) {
+ StrictMock<Base> b;
+ EXPECT_CALL(b, Test()).Times(3);
+
+ EXPECT_TRUE(b.AsWeakHandle().IsInitialized());
+ b.AsWeakHandle().Call(FROM_HERE, &Base::Test);
+
+ {
+ WeakHandle<Base> h(b.AsWeakHandle());
+ EXPECT_TRUE(h.IsInitialized());
+ h.Call(FROM_HERE, &Base::Test);
+ h.Reset();
+ EXPECT_FALSE(h.IsInitialized());
+ }
+
+ {
+ WeakHandle<Base> h;
+ h = b.AsWeakHandle();
+ EXPECT_TRUE(h.IsInitialized());
+ h.Call(FROM_HERE, &Base::Test);
+ h.Reset();
+ EXPECT_FALSE(h.IsInitialized());
+ }
+
+ PumpLoop();
+}
+
+TEST_F(WeakHandleTest, TypeConversionConstructor) {
+ StrictMock<Derived> d;
+ EXPECT_CALL(d, Test()).Times(2);
+
+ const WeakHandle<Derived> weak_handle = MakeWeakHandle(d.AsWeakPtr());
+
+ // Should trigger type conversion constructor.
+ const WeakHandle<Base> base_weak_handle(weak_handle);
+ // Should trigger regular copy constructor.
+ const WeakHandle<Derived> derived_weak_handle(weak_handle);
+
+ EXPECT_TRUE(base_weak_handle.IsInitialized());
+ base_weak_handle.Call(FROM_HERE, &Base::Test);
+
+ EXPECT_TRUE(derived_weak_handle.IsInitialized());
+ // Copy constructor shouldn't construct a new |core_|.
+ EXPECT_EQ(weak_handle.core_.get(), derived_weak_handle.core_.get());
+ derived_weak_handle.Call(FROM_HERE, &Base::Test);
+
+ PumpLoop();
+}
+
+TEST_F(WeakHandleTest, TypeConversionConstructorMakeWeakHandle) {
+ const base::WeakPtr<Derived> weak_ptr;
+
+ // Should trigger type conversion constructor after MakeWeakHandle.
+ WeakHandle<Base> base_weak_handle(MakeWeakHandle(weak_ptr));
+ // Should trigger regular copy constructor after MakeWeakHandle.
+ const WeakHandle<Derived> derived_weak_handle(MakeWeakHandle(weak_ptr));
+
+ EXPECT_TRUE(base_weak_handle.IsInitialized());
+ EXPECT_TRUE(derived_weak_handle.IsInitialized());
+}
+
+TEST_F(WeakHandleTest, TypeConversionConstructorAssignment) {
+ const WeakHandle<Derived> weak_handle =
+ MakeWeakHandle(Derived().AsWeakPtr());
+
+ // Should trigger type conversion constructor before the assignment.
+ WeakHandle<Base> base_weak_handle;
+ base_weak_handle = weak_handle;
+ // Should trigger regular copy constructor before the assignment.
+ WeakHandle<Derived> derived_weak_handle;
+ derived_weak_handle = weak_handle;
+
+ EXPECT_TRUE(base_weak_handle.IsInitialized());
+ EXPECT_TRUE(derived_weak_handle.IsInitialized());
+ // Copy constructor shouldn't construct a new |core_|.
+ EXPECT_EQ(weak_handle.core_.get(), derived_weak_handle.core_.get());
+}
+
+TEST_F(WeakHandleTest, TypeConversionConstructorUninitialized) {
+ const WeakHandle<Base> base_weak_handle = WeakHandle<Derived>();
+ EXPECT_FALSE(base_weak_handle.IsInitialized());
+}
+
+TEST_F(WeakHandleTest, TypeConversionConstructorUninitializedAssignment) {
+ WeakHandle<Base> base_weak_handle;
+ base_weak_handle = WeakHandle<Derived>();
+ EXPECT_FALSE(base_weak_handle.IsInitialized());
+}
+
+} // namespace browser_sync