mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 08:57:34 +00:00
LibPthread: Implement destruction of pthread_keys
Add a function to destroy any keys that were set on the current thread using the algorithm from Dr. POSIX's pthread_key_create. Add some defines to pthread.h for pthread key use, and implement pthread_key_delete. It has a prototype in pthread.h, but any program trying to actually use it would be in for a link-time surprise. Currently, keys are destroyed either via global destructors, with the s_key_destroyer object, or in exit_thread. exit_thread is invoked by pthread_exit, and transitively by pthread_create, via the pthread_create_helper that ensures all threads created with the pthread API properly clean up for themselves when they exit gracefully. A future patch might make s_key_destroyer a C++11 thread_local instead, assuming we get thread_local and thread_local destructors working.
This commit is contained in:
parent
9dc8bea3e7
commit
7b94ca21b3
2 changed files with 57 additions and 5 deletions
|
@ -42,12 +42,23 @@
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using PthreadAttrImpl = Syscall::SC_create_thread_params;
|
using PthreadAttrImpl = Syscall::SC_create_thread_params;
|
||||||
|
|
||||||
|
struct KeyDestroyer {
|
||||||
|
~KeyDestroyer() { destroy_for_current_thread(); }
|
||||||
|
static void destroy_for_current_thread();
|
||||||
|
};
|
||||||
|
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
|
||||||
constexpr size_t required_stack_alignment = 4 * MiB;
|
constexpr size_t required_stack_alignment = 4 * MiB;
|
||||||
constexpr size_t highest_reasonable_guard_size = 32 * PAGE_SIZE;
|
constexpr size_t highest_reasonable_guard_size = 32 * PAGE_SIZE;
|
||||||
constexpr size_t highest_reasonable_stack_size = 8 * MiB; // That's the default in Ubuntu?
|
constexpr size_t highest_reasonable_stack_size = 8 * MiB; // That's the default in Ubuntu?
|
||||||
|
|
||||||
|
// Create an RAII object with a global destructor to destroy pthread keys for the main thread.
|
||||||
|
// Impact of this: Any global object that wants to do something with pthread_getspecific
|
||||||
|
// in its destructor from the main thread might be in for a nasty surprise.
|
||||||
|
static KeyDestroyer s_key_destroyer;
|
||||||
|
|
||||||
#define __RETURN_PTHREAD_ERROR(rc) \
|
#define __RETURN_PTHREAD_ERROR(rc) \
|
||||||
return ((rc) < 0 ? -(rc) : 0)
|
return ((rc) < 0 ? -(rc) : 0)
|
||||||
|
|
||||||
|
@ -91,6 +102,7 @@ static int create_thread(pthread_t* thread, void* (*entry)(void*), void* argumen
|
||||||
|
|
||||||
[[noreturn]] static void exit_thread(void* code)
|
[[noreturn]] static void exit_thread(void* code)
|
||||||
{
|
{
|
||||||
|
KeyDestroyer::destroy_for_current_thread();
|
||||||
syscall(SC_exit_thread, code);
|
syscall(SC_exit_thread, code);
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
@ -540,19 +552,18 @@ int pthread_cond_broadcast(pthread_cond_t* cond)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const int max_keys = 64;
|
static constexpr int max_keys = PTHREAD_KEYS_MAX;
|
||||||
|
|
||||||
typedef void (*KeyDestructor)(void*);
|
typedef void (*KeyDestructor)(void*);
|
||||||
|
|
||||||
struct KeyTable {
|
struct KeyTable {
|
||||||
// FIXME: Invoke key destructors on thread exit!
|
KeyDestructor destructors[max_keys] { nullptr };
|
||||||
KeyDestructor destructors[64] { nullptr };
|
|
||||||
int next { 0 };
|
int next { 0 };
|
||||||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SpecificTable {
|
struct SpecificTable {
|
||||||
void* values[64] { nullptr };
|
void* values[max_keys] { nullptr };
|
||||||
};
|
};
|
||||||
|
|
||||||
static KeyTable s_keys;
|
static KeyTable s_keys;
|
||||||
|
@ -564,7 +575,7 @@ int pthread_key_create(pthread_key_t* key, KeyDestructor destructor)
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
pthread_mutex_lock(&s_keys.mutex);
|
pthread_mutex_lock(&s_keys.mutex);
|
||||||
if (s_keys.next >= max_keys) {
|
if (s_keys.next >= max_keys) {
|
||||||
ret = ENOMEM;
|
ret = EAGAIN;
|
||||||
} else {
|
} else {
|
||||||
*key = s_keys.next++;
|
*key = s_keys.next++;
|
||||||
s_keys.destructors[*key] = destructor;
|
s_keys.destructors[*key] = destructor;
|
||||||
|
@ -574,6 +585,16 @@ int pthread_key_create(pthread_key_t* key, KeyDestructor destructor)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int pthread_key_delete(pthread_key_t key)
|
||||||
|
{
|
||||||
|
if (key < 0 || key >= max_keys)
|
||||||
|
return EINVAL;
|
||||||
|
pthread_mutex_lock(&s_keys.mutex);
|
||||||
|
s_keys.destructors[key] = nullptr;
|
||||||
|
pthread_mutex_unlock(&s_keys.mutex);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void* pthread_getspecific(pthread_key_t key)
|
void* pthread_getspecific(pthread_key_t key)
|
||||||
{
|
{
|
||||||
if (key < 0)
|
if (key < 0)
|
||||||
|
@ -593,6 +614,34 @@ int pthread_setspecific(pthread_key_t key, const void* value)
|
||||||
t_specifics.values[key] = const_cast<void*>(value);
|
t_specifics.values[key] = const_cast<void*>(value);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void KeyDestroyer::destroy_for_current_thread()
|
||||||
|
{
|
||||||
|
// This function will either be called during exit_thread, for a pthread, or
|
||||||
|
// during global program shutdown for the main thread.
|
||||||
|
|
||||||
|
pthread_mutex_lock(&s_keys.mutex);
|
||||||
|
size_t num_used_keys = s_keys.next;
|
||||||
|
|
||||||
|
// Dr. POSIX accounts for weird key destructors setting their own key again.
|
||||||
|
// Or even, setting other unrelated keys? Odd, but whatever the Doc says goes.
|
||||||
|
|
||||||
|
for (size_t destruct_iteration = 0; destruct_iteration < PTHREAD_DESTRUCTOR_ITERATIONS; ++destruct_iteration) {
|
||||||
|
bool any_nonnull_destructors = false;
|
||||||
|
for (size_t key_index = 0; key_index < num_used_keys; ++key_index) {
|
||||||
|
void* value = exchange(t_specifics.values[key_index], nullptr);
|
||||||
|
|
||||||
|
if (value && s_keys.destructors[key_index]) {
|
||||||
|
any_nonnull_destructors = true;
|
||||||
|
(*s_keys.destructors[key_index])(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!any_nonnull_destructors)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&s_keys.mutex);
|
||||||
|
}
|
||||||
|
|
||||||
int pthread_setname_np(pthread_t thread, const char* name)
|
int pthread_setname_np(pthread_t thread, const char* name)
|
||||||
{
|
{
|
||||||
if (!name)
|
if (!name)
|
||||||
|
|
|
@ -87,6 +87,9 @@ int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param
|
||||||
0, 0, CLOCK_MONOTONIC_COARSE \
|
0, 0, CLOCK_MONOTONIC_COARSE \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define PTHREAD_KEYS_MAX 64
|
||||||
|
#define PTHREAD_DESTRUCTOR_ITERATIONS 4
|
||||||
|
|
||||||
int pthread_key_create(pthread_key_t* key, void (*destructor)(void*));
|
int pthread_key_create(pthread_key_t* key, void (*destructor)(void*));
|
||||||
int pthread_key_delete(pthread_key_t key);
|
int pthread_key_delete(pthread_key_t key);
|
||||||
int pthread_cond_broadcast(pthread_cond_t*);
|
int pthread_cond_broadcast(pthread_cond_t*);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue