* This must be POD, as we memset() it to 0 and memcpy() it around.
*/
struct ElementWrapper {
- void dispose(TLPDestructionMode mode) {
- if (ptr != nullptr) {
- DCHECK(deleter != nullptr);
- deleter->dispose(ptr, mode);
-
- cleanup();
+ bool dispose(TLPDestructionMode mode) {
+ if (ptr == nullptr) {
+ return false;
}
+
+ DCHECK(deleter != nullptr);
+ deleter->dispose(ptr, mode);
+ cleanup();
+ return true;
}
void* release() {
}
static void onThreadExit(void* ptr) {
- auto & meta = instance();
+ auto& meta = instance();
#if !__APPLE__
ThreadEntry* threadEntry = getThreadEntry();
// No need to hold the lock any longer; the ThreadEntry is private to this
// thread now that it's been removed from meta.
}
- FOR_EACH_RANGE(i, 0, threadEntry->elementsCapacity) {
- threadEntry->elements[i].dispose(TLPDestructionMode::THIS_THREAD);
+ // NOTE: User-provided deleter / object dtor itself may be using ThreadLocal
+ // with the same Tag, so dispose() calls below may (re)create some of the
+ // elements or even increase elementsCapacity, thus multiple cleanup rounds
+ // may be required.
+ for (bool shouldRun = true; shouldRun; ) {
+ shouldRun = false;
+ FOR_EACH_RANGE(i, 0, threadEntry->elementsCapacity) {
+ if (threadEntry->elements[i].dispose(TLPDestructionMode::THIS_THREAD)) {
+ shouldRun = true;
+ }
+ }
}
free(threadEntry->elements);
threadEntry->elements = nullptr;
EXPECT_EQ(10, Widget::totalVal_);
}
+TEST(ThreadLocalPtr, CreateOnThreadExit) {
+ Widget::totalVal_ = 0;
+ ThreadLocal<Widget> w;
+ ThreadLocalPtr<int> tl;
+
+ std::thread([&] {
+ tl.reset(new int(1), [&] (int* ptr, TLPDestructionMode mode) {
+ delete ptr;
+ // This test ensures Widgets allocated here are not leaked.
+ ++w.get()->val_;
+ ThreadLocal<Widget> wl;
+ ++wl.get()->val_;
+ });
+ }).join();
+ EXPECT_EQ(2, Widget::totalVal_);
+}
+
// Test deleting the ThreadLocalPtr object
TEST(ThreadLocalPtr, CustomDeleter2) {
Widget::totalVal_ = 0;