Do not create an extra default instance of T when constructing a ThreadLocal<T>.
This commit is contained in:
parent
9e38d77f65
commit
831b87f234
@ -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);
|
||||||
};
|
};
|
||||||
|
@ -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");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user