fix(util/thread): LEAN_AUTO_THREAD_FINALIZATION on OSX

This commit is contained in:
Leonardo de Moura 2015-09-03 14:18:31 -07:00
parent 1dc1574ad4
commit 3c50a9cff8
7 changed files with 163 additions and 124 deletions

View file

@ -53,7 +53,6 @@ void test_import() {
check(lean_options_mk_empty(&o, &ex));
check(lean_ios_mk_std(o, &ios, &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(new_env);
lean_name_del(std);

View file

@ -17,14 +17,13 @@ using namespace lean;
#if defined(LEAN_MULTI_THREAD) && !defined(__APPLE__)
LEAN_THREAD_PTR(std::vector<int>, g_v);
void finalize_vector() {
delete g_v;
g_v = nullptr;
void finalize_vector(void * p) {
delete reinterpret_cast<std::vector<int>*>(p);
}
void foo() {
if (!g_v) {
g_v = new std::vector<int>(1024);
register_thread_finalizer(finalize_vector);
register_thread_finalizer(finalize_vector, g_v);
}
if (g_v->size() != 1024) {
std::cerr << "Error\n";

View file

@ -30,21 +30,21 @@ void * memory_pool::allocate() {
typedef std::vector<memory_pool*> memory_pools;
LEAN_THREAD_PTR(memory_pools, g_thread_pools);
static void thread_finalize_memory_pool() {
if (g_thread_pools) {
unsigned i = g_thread_pools->size();
while (i > 0) {
--i;
delete (*g_thread_pools)[i];
}
delete g_thread_pools;
static void thread_finalize_memory_pool(void * p) {
std::vector<memory_pool*> * thread_pools = reinterpret_cast<std::vector<memory_pool*>*>(p);
unsigned i = thread_pools->size();
while (i > 0) {
--i;
delete (*thread_pools)[i];
}
delete thread_pools;
g_thread_pools = nullptr;
}
memory_pool * allocate_thread_memory_pool(unsigned sz) {
if (!g_thread_pools) {
register_post_thread_finalizer(thread_finalize_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);
g_thread_pools->push_back(r);

View file

@ -30,83 +30,131 @@ void initialize_thread() {}
void finalize_thread() {}
#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_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) {
if (fns) {
unsigned i = fns->size();
while (i > 0) {
--i;
auto fn = (*fns)[i];
fn();
}
run_thread_finalizers_core(*fns);
delete fns;
}
}
void run_thread_finalizers() {
run_thread_finalizers(g_finalizers);
g_finalizers = nullptr;
g_finalizers = nullptr;
}
void run_post_thread_finalizers() {
run_thread_finalizers(g_post_finalizers);
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
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);
}
}

View file

@ -5,6 +5,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
Author: Leonardo de Moura
*/
#pragma once
#include <iostream>
#if defined(LEAN_MULTI_THREAD)
#if !defined(LEAN_USE_BOOST)
// 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
#endif
#define MK_THREAD_LOCAL_GET(T, GETTER_NAME, DEF_VALUE) \
LEAN_THREAD_PTR(T, GETTER_NAME ## _tlocal); \
static void finalize_ ## GETTER_NAME() { \
delete GETTER_NAME ## _tlocal; \
GETTER_NAME ## _tlocal = nullptr; \
} \
static T & GETTER_NAME() { \
if (!GETTER_NAME ## _tlocal) { \
GETTER_NAME ## _tlocal = new T(DEF_VALUE); \
register_thread_finalizer(finalize_ ## GETTER_NAME); \
} \
return *(GETTER_NAME ## _tlocal); \
#define MK_THREAD_LOCAL_GET(T, GETTER_NAME, DEF_VALUE) \
LEAN_THREAD_PTR(T, GETTER_NAME ## _tlocal); \
static void finalize_ ## GETTER_NAME(void * p) { \
delete reinterpret_cast<T*>(p); \
GETTER_NAME ## _tlocal = nullptr; \
} \
static T & GETTER_NAME() { \
if (!GETTER_NAME ## _tlocal) { \
GETTER_NAME ## _tlocal = new T(DEF_VALUE); \
register_thread_finalizer(finalize_ ## GETTER_NAME, GETTER_NAME ## _tlocal); \
} \
return *(GETTER_NAME ## _tlocal); \
}
#define MK_THREAD_LOCAL_GET_DEF(T, GETTER_NAME) \
LEAN_THREAD_PTR(T, GETTER_NAME ## _tlocal); \
static void finalize_ ## GETTER_NAME() { \
delete GETTER_NAME ## _tlocal; \
GETTER_NAME ## _tlocal = nullptr; \
} \
static T & GETTER_NAME() { \
if (!GETTER_NAME ## _tlocal) { \
GETTER_NAME ## _tlocal = new T(); \
register_thread_finalizer(finalize_ ## GETTER_NAME); \
} \
return *(GETTER_NAME ## _tlocal); \
#define MK_THREAD_LOCAL_GET_DEF(T, GETTER_NAME) \
LEAN_THREAD_PTR(T, GETTER_NAME ## _tlocal); \
static void finalize_ ## GETTER_NAME(void * p) { \
delete reinterpret_cast<T*>(p); \
GETTER_NAME ## _tlocal = nullptr; \
} \
static T & GETTER_NAME() { \
if (!GETTER_NAME ## _tlocal) { \
GETTER_NAME ## _tlocal = new T(); \
register_thread_finalizer(finalize_ ## GETTER_NAME, GETTER_NAME ## _tlocal); \
} \
return *(GETTER_NAME ## _tlocal); \
}
namespace lean {
void initialize_thread();
void finalize_thread();
typedef void (*thread_finalizer)();
void register_post_thread_finalizer(thread_finalizer fn);
void register_thread_finalizer(thread_finalizer fn);
void run_post_thread_finalizers();
typedef void (*thread_finalizer)(void *);
void register_post_thread_finalizer(thread_finalizer fn, void * p);
void register_thread_finalizer(thread_finalizer fn, void * p);
void run_thread_finalizers();
void run_post_thread_finalizers();
}

View file

@ -123,26 +123,25 @@ struct script_state_ref {
LEAN_THREAD_PTR(bool, g_registered);
LEAN_THREAD_PTR(script_state_ref, g_thread_state_ref);
static void finalize_thread_state_ref() {
delete g_thread_state_ref;
g_thread_state_ref = nullptr;
delete g_registered;
static void finalize_registered(void * p) {
delete reinterpret_cast<bool*>(p);
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() {
if (!g_thread_state_ref) {
g_thread_state_ref = new script_state_ref();
if (!g_registered) {
g_registered = new bool(true);
register_thread_finalizer(finalize_thread_state_ref);
}
register_thread_finalizer(finalize_thread_state_ref, g_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;
}
void release_thread_script_state() {
delete g_thread_state_ref;
g_thread_state_ref = nullptr;
}
}

View file

@ -25,19 +25,13 @@ void system_dostring(char const * code);
void system_import(char const * fname);
/**
\brief Retrieve a script_state object for the current thread.
The thread has exclusive access until the thread is destroyed,
or the method \c release_thread_script_state is invoked.
The thread has exclusive access until the thread is destroyed.
\remark For performance reasons global script_state objects
are recycled. So, users should not delete/redefine functions
defined using #system_dostring. So, side-effects are discouraged.
*/
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 initialize_thread_script_state();