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:
Paolo Bonzini 2014-12-02 12:05:45 +01:00 committed by Stefan Hajnoczi
parent d1d1b206b0
commit ef57137f1b
3 changed files with 78 additions and 11 deletions

View File

@ -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

View File

@ -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.
*/ */

View File

@ -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,