Do not create an extra default instance of T when constructing a ThreadLocal<T>.

This commit is contained in:
kosak 2015-07-19 22:33:19 +00:00
parent 9e38d77f65
commit 831b87f234
2 changed files with 87 additions and 42 deletions

View File

@ -1838,8 +1838,9 @@ class ThreadWithParam : public ThreadWithParamBase {
template <typename T> template <typename T>
class ThreadLocal : public ThreadLocalBase { class ThreadLocal : public ThreadLocalBase {
public: public:
ThreadLocal() : default_() {} ThreadLocal() : default_factory_(new DefaultValueHolderFactory()) {}
explicit ThreadLocal(const T& value) : default_(value) {} explicit ThreadLocal(const T& value)
: default_factory_(new InstanceValueHolderFactory(value)) {}
~ThreadLocal() { ThreadLocalRegistry::OnThreadLocalDestroyed(this); } ~ThreadLocal() { ThreadLocalRegistry::OnThreadLocalDestroyed(this); }
@ -1853,6 +1854,7 @@ class ThreadLocal : public ThreadLocalBase {
// knowing the type of T. // knowing the type of T.
class ValueHolder : public ThreadLocalValueHolderBase { class ValueHolder : public ThreadLocalValueHolderBase {
public: public:
ValueHolder() : value_() {}
explicit ValueHolder(const T& value) : value_(value) {} explicit ValueHolder(const T& value) : value_(value) {}
T* pointer() { return &value_; } T* pointer() { return &value_; }
@ -1869,10 +1871,42 @@ class ThreadLocal : public ThreadLocalBase {
} }
virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const { virtual ThreadLocalValueHolderBase* NewValueForCurrentThread() const {
return new ValueHolder(default_); return default_factory_->MakeNewHolder();
} }
const T default_; // The default value for each thread. class ValueHolderFactory {
public:
ValueHolderFactory() {}
virtual ~ValueHolderFactory() {}
virtual ValueHolder* MakeNewHolder() const = 0;
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolderFactory);
};
class DefaultValueHolderFactory : public ValueHolderFactory {
public:
DefaultValueHolderFactory() {}
virtual ValueHolder* MakeNewHolder() const { return new ValueHolder(); }
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultValueHolderFactory);
};
class InstanceValueHolderFactory : public ValueHolderFactory {
public:
explicit InstanceValueHolderFactory(const T& value) : value_(value) {}
virtual ValueHolder* MakeNewHolder() const {
return new ValueHolder(value_);
}
private:
const T value_; // The value for each thread.
GTEST_DISALLOW_COPY_AND_ASSIGN_(InstanceValueHolderFactory);
};
scoped_ptr<ValueHolderFactory> default_factory_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal);
}; };
@ -1993,10 +2027,11 @@ extern "C" inline void DeleteThreadLocalValue(void* value_holder) {
template <typename T> template <typename T>
class ThreadLocal { class ThreadLocal {
public: public:
ThreadLocal() : key_(CreateKey()), ThreadLocal()
default_() {} : key_(CreateKey()), default_factory_(new DefaultValueHolderFactory()) {}
explicit ThreadLocal(const T& value) : key_(CreateKey()), explicit ThreadLocal(const T& value)
default_(value) {} : key_(CreateKey()),
default_factory_(new InstanceValueHolderFactory(value)) {}
~ThreadLocal() { ~ThreadLocal() {
// Destroys the managed object for the current thread, if any. // Destroys the managed object for the current thread, if any.
@ -2016,6 +2051,7 @@ class ThreadLocal {
// Holds a value of type T. // Holds a value of type T.
class ValueHolder : public ThreadLocalValueHolderBase { class ValueHolder : public ThreadLocalValueHolderBase {
public: public:
ValueHolder() : value_() {}
explicit ValueHolder(const T& value) : value_(value) {} explicit ValueHolder(const T& value) : value_(value) {}
T* pointer() { return &value_; } T* pointer() { return &value_; }
@ -2041,15 +2077,47 @@ class ThreadLocal {
return CheckedDowncastToActualType<ValueHolder>(holder)->pointer(); return CheckedDowncastToActualType<ValueHolder>(holder)->pointer();
} }
ValueHolder* const new_holder = new ValueHolder(default_); ValueHolder* const new_holder = default_factory_->MakeNewHolder();
ThreadLocalValueHolderBase* const holder_base = new_holder; ThreadLocalValueHolderBase* const holder_base = new_holder;
GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base)); GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base));
return new_holder->pointer(); return new_holder->pointer();
} }
class ValueHolderFactory {
public:
ValueHolderFactory() {}
virtual ~ValueHolderFactory() {}
virtual ValueHolder* MakeNewHolder() const = 0;
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolderFactory);
};
class DefaultValueHolderFactory : public ValueHolderFactory {
public:
DefaultValueHolderFactory() {}
virtual ValueHolder* MakeNewHolder() const { return new ValueHolder(); }
private:
GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultValueHolderFactory);
};
class InstanceValueHolderFactory : public ValueHolderFactory {
public:
explicit InstanceValueHolderFactory(const T& value) : value_(value) {}
virtual ValueHolder* MakeNewHolder() const {
return new ValueHolder(value_);
}
private:
const T value_; // The value for each thread.
GTEST_DISALLOW_COPY_AND_ASSIGN_(InstanceValueHolderFactory);
};
// A key pthreads uses for looking up per-thread values. // A key pthreads uses for looking up per-thread values.
const pthread_key_t key_; const pthread_key_t key_;
const T default_; // The default value for each thread. scoped_ptr<ValueHolderFactory> default_factory_;
GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal);
}; };

View File

@ -1149,13 +1149,6 @@ TEST(ThreadLocalTest, ParameterizedConstructorSetsDefault) {
EXPECT_STREQ("foo", result.c_str()); EXPECT_STREQ("foo", result.c_str());
} }
# if !GTEST_HAS_MUTEX_AND_THREAD_LOCAL_
// Tests in this section depend on that Google Test's own ThreadLocal
// implementation stores a copy of the default value shared by all
// threads. We don't want to test this for an external implementation received
// through GTEST_HAS_MUTEX_AND_THREAD_LOCAL_.
// Keeps track of whether of destructors being called on instances of // Keeps track of whether of destructors being called on instances of
// DestructorTracker. On Windows, waits for the destructor call reports. // DestructorTracker. On Windows, waits for the destructor call reports.
class DestructorCall { class DestructorCall {
@ -1240,25 +1233,18 @@ TEST(ThreadLocalTest, DestroysManagedObjectForOwnThreadWhenDying) {
DestructorCall::ResetList(); DestructorCall::ResetList();
{ {
// The next line default constructs a DestructorTracker object as
// the default value of objects managed by thread_local_tracker.
ThreadLocal<DestructorTracker> thread_local_tracker; ThreadLocal<DestructorTracker> thread_local_tracker;
ASSERT_EQ(1U, DestructorCall::List().size()); ASSERT_EQ(0U, DestructorCall::List().size());
ASSERT_FALSE(DestructorCall::List()[0]->CheckDestroyed());
// This creates another DestructorTracker object for the main thread. // This creates another DestructorTracker object for the main thread.
thread_local_tracker.get(); thread_local_tracker.get();
ASSERT_EQ(2U, DestructorCall::List().size()); ASSERT_EQ(1U, DestructorCall::List().size());
ASSERT_FALSE(DestructorCall::List()[0]->CheckDestroyed()); ASSERT_FALSE(DestructorCall::List()[0]->CheckDestroyed());
ASSERT_FALSE(DestructorCall::List()[1]->CheckDestroyed());
} }
// Now thread_local_tracker has died. It should have destroyed both the // Now thread_local_tracker has died.
// default value shared by all threads and the value for the main ASSERT_EQ(1U, DestructorCall::List().size());
// thread.
ASSERT_EQ(2U, DestructorCall::List().size());
EXPECT_TRUE(DestructorCall::List()[0]->CheckDestroyed()); EXPECT_TRUE(DestructorCall::List()[0]->CheckDestroyed());
EXPECT_TRUE(DestructorCall::List()[1]->CheckDestroyed());
DestructorCall::ResetList(); DestructorCall::ResetList();
} }
@ -1269,35 +1255,26 @@ TEST(ThreadLocalTest, DestroysManagedObjectAtThreadExit) {
DestructorCall::ResetList(); DestructorCall::ResetList();
{ {
// The next line default constructs a DestructorTracker object as
// the default value of objects managed by thread_local_tracker.
ThreadLocal<DestructorTracker> thread_local_tracker; ThreadLocal<DestructorTracker> thread_local_tracker;
ASSERT_EQ(1U, DestructorCall::List().size()); ASSERT_EQ(0U, DestructorCall::List().size());
ASSERT_FALSE(DestructorCall::List()[0]->CheckDestroyed());
// This creates another DestructorTracker object in the new thread. // This creates another DestructorTracker object in the new thread.
ThreadWithParam<ThreadParam> thread( ThreadWithParam<ThreadParam> thread(
&CallThreadLocalGet, &thread_local_tracker, NULL); &CallThreadLocalGet, &thread_local_tracker, NULL);
thread.Join(); thread.Join();
// The thread has exited, and we should have another DestroyedTracker // The thread has exited, and we should have a DestroyedTracker
// instance created for it. But it may not have been destroyed yet. // instance created for it. But it may not have been destroyed yet.
// The instance for the main thread should still persist. ASSERT_EQ(1U, DestructorCall::List().size());
ASSERT_EQ(2U, DestructorCall::List().size());
ASSERT_FALSE(DestructorCall::List()[0]->CheckDestroyed());
} }
// The thread has exited and thread_local_tracker has died. The default // The thread has exited and thread_local_tracker has died.
// value should have been destroyed too. ASSERT_EQ(1U, DestructorCall::List().size());
ASSERT_EQ(2U, DestructorCall::List().size());
EXPECT_TRUE(DestructorCall::List()[0]->CheckDestroyed()); EXPECT_TRUE(DestructorCall::List()[0]->CheckDestroyed());
EXPECT_TRUE(DestructorCall::List()[1]->CheckDestroyed());
DestructorCall::ResetList(); DestructorCall::ResetList();
} }
# endif // !GTEST_HAS_MUTEX_AND_THREAD_LOCAL_
TEST(ThreadLocalTest, ThreadLocalMutationsAffectOnlyCurrentThread) { TEST(ThreadLocalTest, ThreadLocalMutationsAffectOnlyCurrentThread) {
ThreadLocal<std::string> thread_local_string; ThreadLocal<std::string> thread_local_string;
thread_local_string.set("Foo"); thread_local_string.set("Foo");