From: Andrii Grynenko Date: Thu, 19 Nov 2015 19:30:20 +0000 (-0800) Subject: Fix folly::ThreadLocal to work in a shared library X-Git-Tag: deprecate-dynamic-initializer~250 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=f2daf056924961a0f6739bfe5b5ae7b22955dbfd;p=folly.git Fix folly::ThreadLocal to work in a shared library Reviewed By: bmaurer Differential Revision: D2667499 fb-gh-sync-id: 463f86752240bd88761910de934ba25d6e62fafe --- diff --git a/folly/Portability.h b/folly/Portability.h index bdac6cc3..486f25a8 100644 --- a/folly/Portability.h +++ b/folly/Portability.h @@ -397,5 +397,15 @@ constexpr size_t constexpr_strlen(const char* s) { #endif } +#if defined(__APPLE__) || defined(_MSC_VER) +#define MAX_STATIC_CONSTRUCTOR_PRIORITY +#else +// 101 is the highest priority allowed by the init_priority attribute. +// This priority is already used by JEMalloc and other memory allocators so +// we will take the next one. +#define MAX_STATIC_CONSTRUCTOR_PRIORITY __attribute__ ((__init_priority__(102))) +#endif + + } // namespace folly #endif // FOLLY_PORTABILITY_H_ diff --git a/folly/detail/ThreadLocalDetail.h b/folly/detail/ThreadLocalDetail.h index da3ee073..adab2192 100644 --- a/folly/detail/ThreadLocalDetail.h +++ b/folly/detail/ThreadLocalDetail.h @@ -220,6 +220,7 @@ struct StaticMeta { // worry about synchronization with exiting threads. static bool constructed = (inst_ = new StaticMeta()); (void)constructed; // suppress unused warning + return *inst_; } @@ -247,10 +248,27 @@ struct StaticMeta { #endif static StaticMeta* inst_; + /** + * We want to disable onThreadExit call at the end of shutdown, we don't care + * about leaking memory at that point. + * + * Otherwise if ThreadLocal is used in a shared library, onThreadExit may be + * called after dlclose(). + */ + struct PthreadKeyUnregister { + ~PthreadKeyUnregister() { + if (inst_) { + pthread_key_delete(inst_->pthreadKey_); + } + } + }; + static PthreadKeyUnregister pthreadKeyUnregister_; + StaticMeta() : nextId_(1) { head_.next = head_.prev = &head_; int ret = pthread_key_create(&pthreadKey_, &onThreadExit); checkPosixError(ret, "pthread_key_create failed"); + (void)pthreadKeyUnregister_; // suppress unused warning #if FOLLY_HAVE_PTHREAD_ATFORK ret = pthread_atfork(/*prepare*/ &StaticMeta::preFork, @@ -530,6 +548,10 @@ FOLLY_TLS ThreadEntry StaticMeta::threadEntry_ = {nullptr, 0, #endif template StaticMeta* StaticMeta::inst_ = nullptr; +template typename StaticMeta::PthreadKeyUnregister +MAX_STATIC_CONSTRUCTOR_PRIORITY +StaticMeta::pthreadKeyUnregister_; + } // namespace threadlocal_detail } // namespace folly diff --git a/folly/test/ThreadLocalTest.cpp b/folly/test/ThreadLocalTest.cpp index 82e6eb4f..823c6cb5 100644 --- a/folly/test/ThreadLocalTest.cpp +++ b/folly/test/ThreadLocalTest.cpp @@ -16,6 +16,7 @@ #include +#include #include #include #include @@ -37,6 +38,7 @@ #include #include +#include using namespace folly; @@ -539,6 +541,47 @@ TEST(ThreadLocal, Fork2) { } } +TEST(ThreadLocal, SharedLibrary) +{ + auto handle = dlopen("./_bin/folly/test/lib_thread_local_test.so", + RTLD_LAZY); + EXPECT_NE(nullptr, handle); + + typedef void (*useA_t)(); + dlerror(); + useA_t useA = (useA_t) dlsym(handle, "useA"); + + const char *dlsym_error = dlerror(); + EXPECT_EQ(nullptr, dlsym_error); + + useA(); + + folly::Baton<> b11, b12, b21, b22; + + std::thread t1([&]() { + useA(); + b11.post(); + b12.wait(); + }); + + std::thread t2([&]() { + useA(); + b21.post(); + b22.wait(); + }); + + b11.wait(); + b21.wait(); + + dlclose(handle); + + b12.post(); + b22.post(); + + t1.join(); + t2.join(); +} + // clang is unable to compile this code unless in c++14 mode. #if __cplusplus >= 201402L namespace { diff --git a/folly/test/ThreadLocalTestLib.cpp b/folly/test/ThreadLocalTestLib.cpp new file mode 100644 index 00000000..1a81528b --- /dev/null +++ b/folly/test/ThreadLocalTestLib.cpp @@ -0,0 +1,35 @@ +/* + * Copyright 2015 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include + +class A { + public: + void use() const { + } +}; + +folly::ThreadLocal a; + +extern "C" { + +void useA() { + a->use(); +} + +}