Commit f2daf056 authored by Andrii Grynenko's avatar Andrii Grynenko Committed by facebook-github-bot-9

Fix folly::ThreadLocal to work in a shared library

Reviewed By: bmaurer

Differential Revision: D2667499

fb-gh-sync-id: 463f86752240bd88761910de934ba25d6e62fafe
parent a74ba58b
...@@ -397,5 +397,15 @@ constexpr size_t constexpr_strlen(const char* s) { ...@@ -397,5 +397,15 @@ constexpr size_t constexpr_strlen(const char* s) {
#endif #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 } // namespace folly
#endif // FOLLY_PORTABILITY_H_ #endif // FOLLY_PORTABILITY_H_
...@@ -220,6 +220,7 @@ struct StaticMeta { ...@@ -220,6 +220,7 @@ struct StaticMeta {
// worry about synchronization with exiting threads. // worry about synchronization with exiting threads.
static bool constructed = (inst_ = new StaticMeta<Tag>()); static bool constructed = (inst_ = new StaticMeta<Tag>());
(void)constructed; // suppress unused warning (void)constructed; // suppress unused warning
return *inst_; return *inst_;
} }
...@@ -247,10 +248,27 @@ struct StaticMeta { ...@@ -247,10 +248,27 @@ struct StaticMeta {
#endif #endif
static StaticMeta<Tag>* inst_; static StaticMeta<Tag>* 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) { StaticMeta() : nextId_(1) {
head_.next = head_.prev = &head_; head_.next = head_.prev = &head_;
int ret = pthread_key_create(&pthreadKey_, &onThreadExit); int ret = pthread_key_create(&pthreadKey_, &onThreadExit);
checkPosixError(ret, "pthread_key_create failed"); checkPosixError(ret, "pthread_key_create failed");
(void)pthreadKeyUnregister_; // suppress unused warning
#if FOLLY_HAVE_PTHREAD_ATFORK #if FOLLY_HAVE_PTHREAD_ATFORK
ret = pthread_atfork(/*prepare*/ &StaticMeta::preFork, ret = pthread_atfork(/*prepare*/ &StaticMeta::preFork,
...@@ -530,6 +548,10 @@ FOLLY_TLS ThreadEntry StaticMeta<Tag>::threadEntry_ = {nullptr, 0, ...@@ -530,6 +548,10 @@ FOLLY_TLS ThreadEntry StaticMeta<Tag>::threadEntry_ = {nullptr, 0,
#endif #endif
template <class Tag> StaticMeta<Tag>* StaticMeta<Tag>::inst_ = nullptr; template <class Tag> StaticMeta<Tag>* StaticMeta<Tag>::inst_ = nullptr;
template <class Tag> typename StaticMeta<Tag>::PthreadKeyUnregister
MAX_STATIC_CONSTRUCTOR_PRIORITY
StaticMeta<Tag>::pthreadKeyUnregister_;
} // namespace threadlocal_detail } // namespace threadlocal_detail
} // namespace folly } // namespace folly
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <folly/ThreadLocal.h> #include <folly/ThreadLocal.h>
#include <dlfcn.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <unistd.h> #include <unistd.h>
...@@ -37,6 +38,7 @@ ...@@ -37,6 +38,7 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <folly/Benchmark.h> #include <folly/Benchmark.h>
#include <folly/Baton.h>
using namespace folly; using namespace folly;
...@@ -539,6 +541,47 @@ TEST(ThreadLocal, Fork2) { ...@@ -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. // clang is unable to compile this code unless in c++14 mode.
#if __cplusplus >= 201402L #if __cplusplus >= 201402L
namespace { namespace {
......
/*
* 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 <iostream>
#include <thread>
#include <folly/ThreadLocal.h>
class A {
public:
void use() const {
}
};
folly::ThreadLocal<A> a;
extern "C" {
void useA() {
a->use();
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment