fix(util/thread): LEAN_AUTO_THREAD_FINALIZATION on OSX
This commit is contained in:
parent
1dc1574ad4
commit
3c50a9cff8
7 changed files with 163 additions and 124 deletions
|
@ -53,7 +53,6 @@ void test_import() {
|
||||||
check(lean_options_mk_empty(&o, &ex));
|
check(lean_options_mk_empty(&o, &ex));
|
||||||
check(lean_ios_mk_std(o, &ios, &ex));
|
check(lean_ios_mk_std(o, &ios, &ex));
|
||||||
check(lean_env_import(env, ios, ms, &new_env, &ex));
|
check(lean_env_import(env, ios, ms, &new_env, &ex));
|
||||||
std::cout << "standard library has been imported\n";
|
|
||||||
lean_env_del(env);
|
lean_env_del(env);
|
||||||
lean_env_del(new_env);
|
lean_env_del(new_env);
|
||||||
lean_name_del(std);
|
lean_name_del(std);
|
||||||
|
|
|
@ -17,14 +17,13 @@ using namespace lean;
|
||||||
|
|
||||||
#if defined(LEAN_MULTI_THREAD) && !defined(__APPLE__)
|
#if defined(LEAN_MULTI_THREAD) && !defined(__APPLE__)
|
||||||
LEAN_THREAD_PTR(std::vector<int>, g_v);
|
LEAN_THREAD_PTR(std::vector<int>, g_v);
|
||||||
void finalize_vector() {
|
void finalize_vector(void * p) {
|
||||||
delete g_v;
|
delete reinterpret_cast<std::vector<int>*>(p);
|
||||||
g_v = nullptr;
|
|
||||||
}
|
}
|
||||||
void foo() {
|
void foo() {
|
||||||
if (!g_v) {
|
if (!g_v) {
|
||||||
g_v = new std::vector<int>(1024);
|
g_v = new std::vector<int>(1024);
|
||||||
register_thread_finalizer(finalize_vector);
|
register_thread_finalizer(finalize_vector, g_v);
|
||||||
}
|
}
|
||||||
if (g_v->size() != 1024) {
|
if (g_v->size() != 1024) {
|
||||||
std::cerr << "Error\n";
|
std::cerr << "Error\n";
|
||||||
|
|
|
@ -30,21 +30,21 @@ void * memory_pool::allocate() {
|
||||||
typedef std::vector<memory_pool*> memory_pools;
|
typedef std::vector<memory_pool*> memory_pools;
|
||||||
LEAN_THREAD_PTR(memory_pools, g_thread_pools);
|
LEAN_THREAD_PTR(memory_pools, g_thread_pools);
|
||||||
|
|
||||||
static void thread_finalize_memory_pool() {
|
static void thread_finalize_memory_pool(void * p) {
|
||||||
if (g_thread_pools) {
|
std::vector<memory_pool*> * thread_pools = reinterpret_cast<std::vector<memory_pool*>*>(p);
|
||||||
unsigned i = g_thread_pools->size();
|
unsigned i = thread_pools->size();
|
||||||
while (i > 0) {
|
while (i > 0) {
|
||||||
--i;
|
--i;
|
||||||
delete (*g_thread_pools)[i];
|
delete (*thread_pools)[i];
|
||||||
}
|
|
||||||
delete g_thread_pools;
|
|
||||||
}
|
}
|
||||||
|
delete thread_pools;
|
||||||
|
g_thread_pools = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
memory_pool * allocate_thread_memory_pool(unsigned sz) {
|
memory_pool * allocate_thread_memory_pool(unsigned sz) {
|
||||||
if (!g_thread_pools) {
|
if (!g_thread_pools) {
|
||||||
register_post_thread_finalizer(thread_finalize_memory_pool);
|
|
||||||
g_thread_pools = new std::vector<memory_pool*>();
|
g_thread_pools = new std::vector<memory_pool*>();
|
||||||
|
register_post_thread_finalizer(thread_finalize_memory_pool, g_thread_pools);
|
||||||
}
|
}
|
||||||
memory_pool * r = new memory_pool(sz);
|
memory_pool * r = new memory_pool(sz);
|
||||||
g_thread_pools->push_back(r);
|
g_thread_pools->push_back(r);
|
||||||
|
|
|
@ -30,83 +30,131 @@ void initialize_thread() {}
|
||||||
void finalize_thread() {}
|
void finalize_thread() {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef std::vector<thread_finalizer> thread_finalizers;
|
typedef std::vector<std::pair<thread_finalizer, void*>> thread_finalizers;
|
||||||
|
|
||||||
|
void run_thread_finalizers_core(thread_finalizers & fns) {
|
||||||
|
unsigned i = fns.size();
|
||||||
|
while (i > 0) {
|
||||||
|
--i;
|
||||||
|
auto fn = fns[i].first;
|
||||||
|
fn(fns[i].second);
|
||||||
|
}
|
||||||
|
fns.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have two different implementations of the following procedures.
|
||||||
|
//
|
||||||
|
// void register_thread_finalizer(thread_finalizer fn, void * p);
|
||||||
|
// void register_post_thread_finalizer(thread_finalizer fn, void * p);
|
||||||
|
// void run_thread_finalizers();
|
||||||
|
//
|
||||||
|
// The implementation is selected by using the LEAN_AUTO_THREAD_FINALIZATION compilation directive.
|
||||||
|
// We can remove the implementation based on pthreads after the new thread_local C++11 keyword is properly
|
||||||
|
// implemented in all platforms.
|
||||||
|
// In the meantime, when LEAN_AUTO_THREAD_FINALIZATION is defined/set, we use a thread finalization
|
||||||
|
// procedure based on the pthread API.
|
||||||
|
// Remark: we only need this feature when Lean is being used as a library.
|
||||||
|
// Example: the C API is being used from Haskell, and the execution threads
|
||||||
|
// are being created by Haskell.
|
||||||
|
// Remark: for the threads created by Lean, we explicitly create the thread finalizers.
|
||||||
|
// The idea is to avoid memory leaks even when LEAN_AUTO_THREAD_FINALIZATION is not used.
|
||||||
|
|
||||||
|
#if defined(LEAN_AUTO_THREAD_FINALIZATION)
|
||||||
|
// pthread based implementation
|
||||||
|
|
||||||
|
typedef std::pair<thread_finalizers, thread_finalizers> thread_finalizers_pair;
|
||||||
|
|
||||||
|
class thread_finalizers_manager {
|
||||||
|
pthread_key_t g_key;
|
||||||
|
public:
|
||||||
|
thread_finalizers_manager() {
|
||||||
|
pthread_key_create(&g_key, finalize_thread);
|
||||||
|
init_thread(); // initialize main thread
|
||||||
|
}
|
||||||
|
|
||||||
|
~thread_finalizers_manager() {
|
||||||
|
finalize_thread(get_pair()); // finalize main thread
|
||||||
|
pthread_key_delete(g_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_finalizers_pair * get_pair() {
|
||||||
|
return reinterpret_cast<thread_finalizers_pair*>(pthread_getspecific(g_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
void init_thread() {
|
||||||
|
if (get_pair() == nullptr) {
|
||||||
|
thread_finalizers_pair * p = new thread_finalizers_pair();
|
||||||
|
pthread_setspecific(g_key, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_finalizers & get_thread_finalizers() {
|
||||||
|
init_thread();
|
||||||
|
return get_pair()->first;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_finalizers & get_post_thread_finalizers() {
|
||||||
|
init_thread();
|
||||||
|
return get_pair()->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void finalize_thread(void * d) {
|
||||||
|
if (d) {
|
||||||
|
thread_finalizers_pair * p = reinterpret_cast<thread_finalizers_pair*>(d);
|
||||||
|
run_thread_finalizers_core(p->first);
|
||||||
|
run_thread_finalizers_core(p->second);
|
||||||
|
delete p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static thread_finalizers_manager g_aux;
|
||||||
|
|
||||||
|
void register_thread_finalizer(thread_finalizer fn, void * p) {
|
||||||
|
g_aux.get_thread_finalizers().emplace_back(fn, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void register_post_thread_finalizer(thread_finalizer fn, void * p) {
|
||||||
|
g_aux.get_post_thread_finalizers().emplace_back(fn, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_thread_finalizers() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void run_post_thread_finalizers() {
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// reference implementation
|
||||||
LEAN_THREAD_PTR(thread_finalizers, g_finalizers);
|
LEAN_THREAD_PTR(thread_finalizers, g_finalizers);
|
||||||
LEAN_THREAD_PTR(thread_finalizers, g_post_finalizers);
|
LEAN_THREAD_PTR(thread_finalizers, g_post_finalizers);
|
||||||
|
|
||||||
|
void register_thread_finalizer(thread_finalizer fn, void * p) {
|
||||||
|
if (!g_finalizers)
|
||||||
|
g_finalizers = new thread_finalizers();
|
||||||
|
g_finalizers->emplace_back(fn, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
void register_post_thread_finalizer(thread_finalizer fn, void * p) {
|
||||||
|
if (!g_post_finalizers)
|
||||||
|
g_post_finalizers = new thread_finalizers();
|
||||||
|
g_post_finalizers->emplace_back(fn, p);
|
||||||
|
}
|
||||||
|
|
||||||
void run_thread_finalizers(thread_finalizers * fns) {
|
void run_thread_finalizers(thread_finalizers * fns) {
|
||||||
if (fns) {
|
if (fns) {
|
||||||
unsigned i = fns->size();
|
run_thread_finalizers_core(*fns);
|
||||||
while (i > 0) {
|
|
||||||
--i;
|
|
||||||
auto fn = (*fns)[i];
|
|
||||||
fn();
|
|
||||||
}
|
|
||||||
delete fns;
|
delete fns;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void run_thread_finalizers() {
|
void run_thread_finalizers() {
|
||||||
run_thread_finalizers(g_finalizers);
|
run_thread_finalizers(g_finalizers);
|
||||||
g_finalizers = nullptr;
|
g_finalizers = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void run_post_thread_finalizers() {
|
void run_post_thread_finalizers() {
|
||||||
run_thread_finalizers(g_post_finalizers);
|
run_thread_finalizers(g_post_finalizers);
|
||||||
g_post_finalizers = nullptr;
|
g_post_finalizers = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(LEAN_AUTO_THREAD_FINALIZATION)
|
|
||||||
// This is an auxiliary class used to finalize thread local storage.
|
|
||||||
// It can be removed after the new thread_local C++11 keyword is properly
|
|
||||||
// implemented in all platforms.
|
|
||||||
// In the meantime, we create a "fake" key that is only used to trigger
|
|
||||||
// the Lean thread finalization procedures.
|
|
||||||
// We only need this feature when Lean is being used as a library.
|
|
||||||
// Example: the C API is being used from Haskell, and the execution threads
|
|
||||||
// are being created by Haskell.
|
|
||||||
// Remark: for the threads created by Lean, we explicitly create the thread finalizers.
|
|
||||||
// The idea is to avoid memory leaks even when LEAN_AUTO_THREAD_FINALIZATION is not used.
|
|
||||||
class thread_key_init {
|
|
||||||
pthread_key_t g_key;
|
|
||||||
public:
|
|
||||||
static void finalize_thread(void *) { // NOLINT
|
|
||||||
run_thread_finalizers();
|
|
||||||
run_post_thread_finalizers();
|
|
||||||
}
|
|
||||||
|
|
||||||
thread_key_init() {
|
|
||||||
pthread_key_create(&g_key, finalize_thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
~thread_key_init() {
|
|
||||||
pthread_key_delete(g_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
void init_thread() {
|
|
||||||
void * p;
|
|
||||||
if ((p = pthread_getspecific(g_key)) == nullptr) {
|
|
||||||
pthread_setspecific(g_key, reinterpret_cast<void*>(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static thread_key_init g_aux;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void register_thread_finalizer(thread_finalizer fn) {
|
|
||||||
if (!g_finalizers) {
|
|
||||||
g_finalizers = new thread_finalizers();
|
|
||||||
#if defined(LEAN_AUTO_THREAD_FINALIZATION)
|
|
||||||
g_aux.init_thread();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
g_finalizers->push_back(fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
void register_post_thread_finalizer(thread_finalizer fn) {
|
|
||||||
if (!g_post_finalizers)
|
|
||||||
g_post_finalizers = new thread_finalizers();
|
|
||||||
g_post_finalizers->push_back(fn);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
||||||
Author: Leonardo de Moura
|
Author: Leonardo de Moura
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <iostream>
|
||||||
#if defined(LEAN_MULTI_THREAD)
|
#if defined(LEAN_MULTI_THREAD)
|
||||||
#if !defined(LEAN_USE_BOOST)
|
#if !defined(LEAN_USE_BOOST)
|
||||||
// MULTI THREADING SUPPORT BASED ON THE STANDARD LIBRARY
|
// MULTI THREADING SUPPORT BASED ON THE STANDARD LIBRARY
|
||||||
|
@ -169,42 +170,41 @@ public:
|
||||||
#define LEAN_THREAD_VALUE(T, V, VAL) static __thread T V = VAL
|
#define LEAN_THREAD_VALUE(T, V, VAL) static __thread T V = VAL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define MK_THREAD_LOCAL_GET(T, GETTER_NAME, DEF_VALUE) \
|
#define MK_THREAD_LOCAL_GET(T, GETTER_NAME, DEF_VALUE) \
|
||||||
LEAN_THREAD_PTR(T, GETTER_NAME ## _tlocal); \
|
LEAN_THREAD_PTR(T, GETTER_NAME ## _tlocal); \
|
||||||
static void finalize_ ## GETTER_NAME() { \
|
static void finalize_ ## GETTER_NAME(void * p) { \
|
||||||
delete GETTER_NAME ## _tlocal; \
|
delete reinterpret_cast<T*>(p); \
|
||||||
GETTER_NAME ## _tlocal = nullptr; \
|
GETTER_NAME ## _tlocal = nullptr; \
|
||||||
} \
|
} \
|
||||||
static T & GETTER_NAME() { \
|
static T & GETTER_NAME() { \
|
||||||
if (!GETTER_NAME ## _tlocal) { \
|
if (!GETTER_NAME ## _tlocal) { \
|
||||||
GETTER_NAME ## _tlocal = new T(DEF_VALUE); \
|
GETTER_NAME ## _tlocal = new T(DEF_VALUE); \
|
||||||
register_thread_finalizer(finalize_ ## GETTER_NAME); \
|
register_thread_finalizer(finalize_ ## GETTER_NAME, GETTER_NAME ## _tlocal); \
|
||||||
} \
|
} \
|
||||||
return *(GETTER_NAME ## _tlocal); \
|
return *(GETTER_NAME ## _tlocal); \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MK_THREAD_LOCAL_GET_DEF(T, GETTER_NAME) \
|
#define MK_THREAD_LOCAL_GET_DEF(T, GETTER_NAME) \
|
||||||
LEAN_THREAD_PTR(T, GETTER_NAME ## _tlocal); \
|
LEAN_THREAD_PTR(T, GETTER_NAME ## _tlocal); \
|
||||||
static void finalize_ ## GETTER_NAME() { \
|
static void finalize_ ## GETTER_NAME(void * p) { \
|
||||||
delete GETTER_NAME ## _tlocal; \
|
delete reinterpret_cast<T*>(p); \
|
||||||
GETTER_NAME ## _tlocal = nullptr; \
|
GETTER_NAME ## _tlocal = nullptr; \
|
||||||
} \
|
} \
|
||||||
static T & GETTER_NAME() { \
|
static T & GETTER_NAME() { \
|
||||||
if (!GETTER_NAME ## _tlocal) { \
|
if (!GETTER_NAME ## _tlocal) { \
|
||||||
GETTER_NAME ## _tlocal = new T(); \
|
GETTER_NAME ## _tlocal = new T(); \
|
||||||
register_thread_finalizer(finalize_ ## GETTER_NAME); \
|
register_thread_finalizer(finalize_ ## GETTER_NAME, GETTER_NAME ## _tlocal); \
|
||||||
} \
|
} \
|
||||||
return *(GETTER_NAME ## _tlocal); \
|
return *(GETTER_NAME ## _tlocal); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
namespace lean {
|
namespace lean {
|
||||||
void initialize_thread();
|
void initialize_thread();
|
||||||
void finalize_thread();
|
void finalize_thread();
|
||||||
|
|
||||||
typedef void (*thread_finalizer)();
|
typedef void (*thread_finalizer)(void *);
|
||||||
void register_post_thread_finalizer(thread_finalizer fn);
|
void register_post_thread_finalizer(thread_finalizer fn, void * p);
|
||||||
void register_thread_finalizer(thread_finalizer fn);
|
void register_thread_finalizer(thread_finalizer fn, void * p);
|
||||||
void run_post_thread_finalizers();
|
|
||||||
void run_thread_finalizers();
|
void run_thread_finalizers();
|
||||||
|
void run_post_thread_finalizers();
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,26 +123,25 @@ struct script_state_ref {
|
||||||
LEAN_THREAD_PTR(bool, g_registered);
|
LEAN_THREAD_PTR(bool, g_registered);
|
||||||
LEAN_THREAD_PTR(script_state_ref, g_thread_state_ref);
|
LEAN_THREAD_PTR(script_state_ref, g_thread_state_ref);
|
||||||
|
|
||||||
static void finalize_thread_state_ref() {
|
static void finalize_registered(void * p) {
|
||||||
delete g_thread_state_ref;
|
delete reinterpret_cast<bool*>(p);
|
||||||
g_thread_state_ref = nullptr;
|
|
||||||
delete g_registered;
|
|
||||||
g_registered = nullptr;
|
g_registered = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void finalize_thread_state_ref(void * p) {
|
||||||
|
delete reinterpret_cast<script_state_ref*>(p);
|
||||||
|
g_thread_state_ref = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
script_state get_thread_script_state() {
|
script_state get_thread_script_state() {
|
||||||
if (!g_thread_state_ref) {
|
if (!g_thread_state_ref) {
|
||||||
g_thread_state_ref = new script_state_ref();
|
g_thread_state_ref = new script_state_ref();
|
||||||
if (!g_registered) {
|
register_thread_finalizer(finalize_thread_state_ref, g_thread_state_ref);
|
||||||
g_registered = new bool(true);
|
}
|
||||||
register_thread_finalizer(finalize_thread_state_ref);
|
if (!g_registered) {
|
||||||
}
|
g_registered = new bool(true);
|
||||||
|
register_thread_finalizer(finalize_registered, g_registered);
|
||||||
}
|
}
|
||||||
return g_thread_state_ref->m_state;
|
return g_thread_state_ref->m_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
void release_thread_script_state() {
|
|
||||||
delete g_thread_state_ref;
|
|
||||||
g_thread_state_ref = nullptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,19 +25,13 @@ void system_dostring(char const * code);
|
||||||
void system_import(char const * fname);
|
void system_import(char const * fname);
|
||||||
/**
|
/**
|
||||||
\brief Retrieve a script_state object for the current thread.
|
\brief Retrieve a script_state object for the current thread.
|
||||||
The thread has exclusive access until the thread is destroyed,
|
The thread has exclusive access until the thread is destroyed.
|
||||||
or the method \c release_thread_script_state is invoked.
|
|
||||||
|
|
||||||
\remark For performance reasons global script_state objects
|
\remark For performance reasons global script_state objects
|
||||||
are recycled. So, users should not delete/redefine functions
|
are recycled. So, users should not delete/redefine functions
|
||||||
defined using #system_dostring. So, side-effects are discouraged.
|
defined using #system_dostring. So, side-effects are discouraged.
|
||||||
*/
|
*/
|
||||||
script_state get_thread_script_state();
|
script_state get_thread_script_state();
|
||||||
/**
|
|
||||||
\brief Put the thread script_state back on the state pool,
|
|
||||||
and available for other threads.
|
|
||||||
*/
|
|
||||||
void release_thread_script_state();
|
|
||||||
|
|
||||||
void enable_script_state_recycling(bool flag);
|
void enable_script_state_recycling(bool flag);
|
||||||
void initialize_thread_script_state();
|
void initialize_thread_script_state();
|
||||||
|
|
Loading…
Reference in a new issue