mirror of https://github.com/xemu-project/xemu.git
qemu-thread: add per-thread atexit functions
Destructors are the main additional feature of pthread TLS compared to __thread. If we were using C++ (hint, hint!) we could have used thread-local objects with a destructor. Since we are not, instead, we add a simple Notifier-based API. Note that the notifier must be per-thread as well. We can add a global list as well later, perhaps. The Win32 implementation has some complications because a) detached threads used not to have a QemuThreadData; b) the main thread does not go through win32_start_routine, so we have to use atexit too. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Fam Zheng <famz@redhat.com> Message-id: 1417518350-6167-3-git-send-email-pbonzini@redhat.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
parent
d1d1b206b0
commit
ef57137f1b
|
@ -61,4 +61,8 @@ bool qemu_thread_is_self(QemuThread *thread);
|
||||||
void qemu_thread_exit(void *retval);
|
void qemu_thread_exit(void *retval);
|
||||||
void qemu_thread_naming(bool enable);
|
void qemu_thread_naming(bool enable);
|
||||||
|
|
||||||
|
struct Notifier;
|
||||||
|
void qemu_thread_atexit_add(struct Notifier *notifier);
|
||||||
|
void qemu_thread_atexit_remove(struct Notifier *notifier);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#endif
|
#endif
|
||||||
#include "qemu/thread.h"
|
#include "qemu/thread.h"
|
||||||
#include "qemu/atomic.h"
|
#include "qemu/atomic.h"
|
||||||
|
#include "qemu/notify.h"
|
||||||
|
|
||||||
static bool name_threads;
|
static bool name_threads;
|
||||||
|
|
||||||
|
@ -401,6 +402,42 @@ void qemu_event_wait(QemuEvent *ev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static pthread_key_t exit_key;
|
||||||
|
|
||||||
|
union NotifierThreadData {
|
||||||
|
void *ptr;
|
||||||
|
NotifierList list;
|
||||||
|
};
|
||||||
|
QEMU_BUILD_BUG_ON(sizeof(union NotifierThreadData) != sizeof(void *));
|
||||||
|
|
||||||
|
void qemu_thread_atexit_add(Notifier *notifier)
|
||||||
|
{
|
||||||
|
union NotifierThreadData ntd;
|
||||||
|
ntd.ptr = pthread_getspecific(exit_key);
|
||||||
|
notifier_list_add(&ntd.list, notifier);
|
||||||
|
pthread_setspecific(exit_key, ntd.ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void qemu_thread_atexit_remove(Notifier *notifier)
|
||||||
|
{
|
||||||
|
union NotifierThreadData ntd;
|
||||||
|
ntd.ptr = pthread_getspecific(exit_key);
|
||||||
|
notifier_remove(notifier);
|
||||||
|
pthread_setspecific(exit_key, ntd.ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qemu_thread_atexit_run(void *arg)
|
||||||
|
{
|
||||||
|
union NotifierThreadData ntd = { .ptr = arg };
|
||||||
|
notifier_list_notify(&ntd.list, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __attribute__((constructor)) qemu_thread_atexit_init(void)
|
||||||
|
{
|
||||||
|
pthread_key_create(&exit_key, qemu_thread_atexit_run);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Attempt to set the threads name; note that this is for debug, so
|
/* Attempt to set the threads name; note that this is for debug, so
|
||||||
* we're not going to fail if we can't set it.
|
* we're not going to fail if we can't set it.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
*/
|
*/
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu/thread.h"
|
#include "qemu/thread.h"
|
||||||
|
#include "qemu/notify.h"
|
||||||
#include <process.h>
|
#include <process.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
@ -268,6 +269,7 @@ struct QemuThreadData {
|
||||||
void *(*start_routine)(void *);
|
void *(*start_routine)(void *);
|
||||||
void *arg;
|
void *arg;
|
||||||
short mode;
|
short mode;
|
||||||
|
NotifierList exit;
|
||||||
|
|
||||||
/* Only used for joinable threads. */
|
/* Only used for joinable threads. */
|
||||||
bool exited;
|
bool exited;
|
||||||
|
@ -275,18 +277,40 @@ struct QemuThreadData {
|
||||||
CRITICAL_SECTION cs;
|
CRITICAL_SECTION cs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool atexit_registered;
|
||||||
|
static NotifierList main_thread_exit;
|
||||||
|
|
||||||
static __thread QemuThreadData *qemu_thread_data;
|
static __thread QemuThreadData *qemu_thread_data;
|
||||||
|
|
||||||
|
static void run_main_thread_exit(void)
|
||||||
|
{
|
||||||
|
notifier_list_notify(&main_thread_exit, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void qemu_thread_atexit_add(Notifier *notifier)
|
||||||
|
{
|
||||||
|
if (!qemu_thread_data) {
|
||||||
|
if (!atexit_registered) {
|
||||||
|
atexit_registered = true;
|
||||||
|
atexit(run_main_thread_exit);
|
||||||
|
}
|
||||||
|
notifier_list_add(&main_thread_exit, notifier);
|
||||||
|
} else {
|
||||||
|
notifier_list_add(&qemu_thread_data->exit, notifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void qemu_thread_atexit_remove(Notifier *notifier)
|
||||||
|
{
|
||||||
|
notifier_remove(notifier);
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned __stdcall win32_start_routine(void *arg)
|
static unsigned __stdcall win32_start_routine(void *arg)
|
||||||
{
|
{
|
||||||
QemuThreadData *data = (QemuThreadData *) arg;
|
QemuThreadData *data = (QemuThreadData *) arg;
|
||||||
void *(*start_routine)(void *) = data->start_routine;
|
void *(*start_routine)(void *) = data->start_routine;
|
||||||
void *thread_arg = data->arg;
|
void *thread_arg = data->arg;
|
||||||
|
|
||||||
if (data->mode == QEMU_THREAD_DETACHED) {
|
|
||||||
g_free(data);
|
|
||||||
data = NULL;
|
|
||||||
}
|
|
||||||
qemu_thread_data = data;
|
qemu_thread_data = data;
|
||||||
qemu_thread_exit(start_routine(thread_arg));
|
qemu_thread_exit(start_routine(thread_arg));
|
||||||
abort();
|
abort();
|
||||||
|
@ -296,12 +320,14 @@ void qemu_thread_exit(void *arg)
|
||||||
{
|
{
|
||||||
QemuThreadData *data = qemu_thread_data;
|
QemuThreadData *data = qemu_thread_data;
|
||||||
|
|
||||||
if (data) {
|
notifier_list_notify(&data->exit, NULL);
|
||||||
assert(data->mode != QEMU_THREAD_DETACHED);
|
if (data->mode == QEMU_THREAD_JOINABLE) {
|
||||||
data->ret = arg;
|
data->ret = arg;
|
||||||
EnterCriticalSection(&data->cs);
|
EnterCriticalSection(&data->cs);
|
||||||
data->exited = true;
|
data->exited = true;
|
||||||
LeaveCriticalSection(&data->cs);
|
LeaveCriticalSection(&data->cs);
|
||||||
|
} else {
|
||||||
|
g_free(data);
|
||||||
}
|
}
|
||||||
_endthreadex(0);
|
_endthreadex(0);
|
||||||
}
|
}
|
||||||
|
@ -313,9 +339,10 @@ void *qemu_thread_join(QemuThread *thread)
|
||||||
HANDLE handle;
|
HANDLE handle;
|
||||||
|
|
||||||
data = thread->data;
|
data = thread->data;
|
||||||
if (!data) {
|
if (data->mode == QEMU_THREAD_DETACHED) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Because multiple copies of the QemuThread can exist via
|
* Because multiple copies of the QemuThread can exist via
|
||||||
* qemu_thread_get_self, we need to store a value that cannot
|
* qemu_thread_get_self, we need to store a value that cannot
|
||||||
|
@ -329,7 +356,6 @@ void *qemu_thread_join(QemuThread *thread)
|
||||||
CloseHandle(handle);
|
CloseHandle(handle);
|
||||||
}
|
}
|
||||||
ret = data->ret;
|
ret = data->ret;
|
||||||
assert(data->mode != QEMU_THREAD_DETACHED);
|
|
||||||
DeleteCriticalSection(&data->cs);
|
DeleteCriticalSection(&data->cs);
|
||||||
g_free(data);
|
g_free(data);
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -347,6 +373,7 @@ void qemu_thread_create(QemuThread *thread, const char *name,
|
||||||
data->arg = arg;
|
data->arg = arg;
|
||||||
data->mode = mode;
|
data->mode = mode;
|
||||||
data->exited = false;
|
data->exited = false;
|
||||||
|
notifier_list_init(&data->exit);
|
||||||
|
|
||||||
if (data->mode != QEMU_THREAD_DETACHED) {
|
if (data->mode != QEMU_THREAD_DETACHED) {
|
||||||
InitializeCriticalSection(&data->cs);
|
InitializeCriticalSection(&data->cs);
|
||||||
|
@ -358,7 +385,7 @@ void qemu_thread_create(QemuThread *thread, const char *name,
|
||||||
error_exit(GetLastError(), __func__);
|
error_exit(GetLastError(), __func__);
|
||||||
}
|
}
|
||||||
CloseHandle(hThread);
|
CloseHandle(hThread);
|
||||||
thread->data = (mode == QEMU_THREAD_DETACHED) ? NULL : data;
|
thread->data = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qemu_thread_get_self(QemuThread *thread)
|
void qemu_thread_get_self(QemuThread *thread)
|
||||||
|
@ -373,11 +400,10 @@ HANDLE qemu_thread_get_handle(QemuThread *thread)
|
||||||
HANDLE handle;
|
HANDLE handle;
|
||||||
|
|
||||||
data = thread->data;
|
data = thread->data;
|
||||||
if (!data) {
|
if (data->mode == QEMU_THREAD_DETACHED) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(data->mode != QEMU_THREAD_DETACHED);
|
|
||||||
EnterCriticalSection(&data->cs);
|
EnterCriticalSection(&data->cs);
|
||||||
if (!data->exited) {
|
if (!data->exited) {
|
||||||
handle = OpenThread(SYNCHRONIZE | THREAD_SUSPEND_RESUME, FALSE,
|
handle = OpenThread(SYNCHRONIZE | THREAD_SUSPEND_RESUME, FALSE,
|
||||||
|
|
Loading…
Reference in New Issue