aboutsummaryrefslogtreecommitdiffstats
path: root/util/qemu-thread-win32.c
diff options
context:
space:
mode:
Diffstat (limited to 'util/qemu-thread-win32.c')
-rw-r--r--util/qemu-thread-win32.c461
1 files changed, 461 insertions, 0 deletions
diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c
new file mode 100644
index 000000000..52eb19f35
--- /dev/null
+++ b/util/qemu-thread-win32.c
@@ -0,0 +1,461 @@
+/*
+ * Win32 implementation for mutex/cond/thread functions
+ *
+ * Copyright Red Hat, Inc. 2010
+ *
+ * Author:
+ * Paolo Bonzini <pbonzini@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
+ * See the COPYING file in the top-level directory.
+ *
+ */
+
+#include "qemu/osdep.h"
+#include "qemu-common.h"
+#include "qemu/thread.h"
+#include "qemu/notify.h"
+#include "qemu-thread-common.h"
+#include <process.h>
+
+static bool name_threads;
+
+void qemu_thread_naming(bool enable)
+{
+ /* But note we don't actually name them on Windows yet */
+ name_threads = enable;
+
+ fprintf(stderr, "qemu: thread naming not supported on this host\n");
+}
+
+static void error_exit(int err, const char *msg)
+{
+ char *pstr;
+
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL, err, 0, (LPTSTR)&pstr, 2, NULL);
+ fprintf(stderr, "qemu: %s: %s\n", msg, pstr);
+ LocalFree(pstr);
+ abort();
+}
+
+void qemu_mutex_init(QemuMutex *mutex)
+{
+ InitializeSRWLock(&mutex->lock);
+ qemu_mutex_post_init(mutex);
+}
+
+void qemu_mutex_destroy(QemuMutex *mutex)
+{
+ assert(mutex->initialized);
+ mutex->initialized = false;
+ InitializeSRWLock(&mutex->lock);
+}
+
+void qemu_mutex_lock_impl(QemuMutex *mutex, const char *file, const int line)
+{
+ assert(mutex->initialized);
+ qemu_mutex_pre_lock(mutex, file, line);
+ AcquireSRWLockExclusive(&mutex->lock);
+ qemu_mutex_post_lock(mutex, file, line);
+}
+
+int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line)
+{
+ int owned;
+
+ assert(mutex->initialized);
+ owned = TryAcquireSRWLockExclusive(&mutex->lock);
+ if (owned) {
+ qemu_mutex_post_lock(mutex, file, line);
+ return 0;
+ }
+ return -EBUSY;
+}
+
+void qemu_mutex_unlock_impl(QemuMutex *mutex, const char *file, const int line)
+{
+ assert(mutex->initialized);
+ qemu_mutex_pre_unlock(mutex, file, line);
+ ReleaseSRWLockExclusive(&mutex->lock);
+}
+
+void qemu_rec_mutex_init(QemuRecMutex *mutex)
+{
+ InitializeCriticalSection(&mutex->lock);
+ mutex->initialized = true;
+}
+
+void qemu_rec_mutex_destroy(QemuRecMutex *mutex)
+{
+ assert(mutex->initialized);
+ mutex->initialized = false;
+ DeleteCriticalSection(&mutex->lock);
+}
+
+void qemu_rec_mutex_lock_impl(QemuRecMutex *mutex, const char *file, int line)
+{
+ assert(mutex->initialized);
+ EnterCriticalSection(&mutex->lock);
+}
+
+int qemu_rec_mutex_trylock_impl(QemuRecMutex *mutex, const char *file, int line)
+{
+ assert(mutex->initialized);
+ return !TryEnterCriticalSection(&mutex->lock);
+}
+
+void qemu_rec_mutex_unlock_impl(QemuRecMutex *mutex, const char *file, int line)
+{
+ assert(mutex->initialized);
+ LeaveCriticalSection(&mutex->lock);
+}
+
+void qemu_cond_init(QemuCond *cond)
+{
+ memset(cond, 0, sizeof(*cond));
+ InitializeConditionVariable(&cond->var);
+ cond->initialized = true;
+}
+
+void qemu_cond_destroy(QemuCond *cond)
+{
+ assert(cond->initialized);
+ cond->initialized = false;
+ InitializeConditionVariable(&cond->var);
+}
+
+void qemu_cond_signal(QemuCond *cond)
+{
+ assert(cond->initialized);
+ WakeConditionVariable(&cond->var);
+}
+
+void qemu_cond_broadcast(QemuCond *cond)
+{
+ assert(cond->initialized);
+ WakeAllConditionVariable(&cond->var);
+}
+
+void qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, const char *file, const int line)
+{
+ assert(cond->initialized);
+ qemu_mutex_pre_unlock(mutex, file, line);
+ SleepConditionVariableSRW(&cond->var, &mutex->lock, INFINITE, 0);
+ qemu_mutex_post_lock(mutex, file, line);
+}
+
+bool qemu_cond_timedwait_impl(QemuCond *cond, QemuMutex *mutex, int ms,
+ const char *file, const int line)
+{
+ int rc = 0;
+
+ assert(cond->initialized);
+ trace_qemu_mutex_unlock(mutex, file, line);
+ if (!SleepConditionVariableSRW(&cond->var, &mutex->lock, ms, 0)) {
+ rc = GetLastError();
+ }
+ trace_qemu_mutex_locked(mutex, file, line);
+ if (rc && rc != ERROR_TIMEOUT) {
+ error_exit(rc, __func__);
+ }
+ return rc != ERROR_TIMEOUT;
+}
+
+void qemu_sem_init(QemuSemaphore *sem, int init)
+{
+ /* Manual reset. */
+ sem->sema = CreateSemaphore(NULL, init, LONG_MAX, NULL);
+ sem->initialized = true;
+}
+
+void qemu_sem_destroy(QemuSemaphore *sem)
+{
+ assert(sem->initialized);
+ sem->initialized = false;
+ CloseHandle(sem->sema);
+}
+
+void qemu_sem_post(QemuSemaphore *sem)
+{
+ assert(sem->initialized);
+ ReleaseSemaphore(sem->sema, 1, NULL);
+}
+
+int qemu_sem_timedwait(QemuSemaphore *sem, int ms)
+{
+ int rc;
+
+ assert(sem->initialized);
+ rc = WaitForSingleObject(sem->sema, ms);
+ if (rc == WAIT_OBJECT_0) {
+ return 0;
+ }
+ if (rc != WAIT_TIMEOUT) {
+ error_exit(GetLastError(), __func__);
+ }
+ return -1;
+}
+
+void qemu_sem_wait(QemuSemaphore *sem)
+{
+ assert(sem->initialized);
+ if (WaitForSingleObject(sem->sema, INFINITE) != WAIT_OBJECT_0) {
+ error_exit(GetLastError(), __func__);
+ }
+}
+
+/* Wrap a Win32 manual-reset event with a fast userspace path. The idea
+ * is to reset the Win32 event lazily, as part of a test-reset-test-wait
+ * sequence. Such a sequence is, indeed, how QemuEvents are used by
+ * RCU and other subsystems!
+ *
+ * Valid transitions:
+ * - free->set, when setting the event
+ * - busy->set, when setting the event, followed by SetEvent
+ * - set->free, when resetting the event
+ * - free->busy, when waiting
+ *
+ * set->busy does not happen (it can be observed from the outside but
+ * it really is set->free->busy).
+ *
+ * busy->free provably cannot happen; to enforce it, the set->free transition
+ * is done with an OR, which becomes a no-op if the event has concurrently
+ * transitioned to free or busy (and is faster than cmpxchg).
+ */
+
+#define EV_SET 0
+#define EV_FREE 1
+#define EV_BUSY -1
+
+void qemu_event_init(QemuEvent *ev, bool init)
+{
+ /* Manual reset. */
+ ev->event = CreateEvent(NULL, TRUE, TRUE, NULL);
+ ev->value = (init ? EV_SET : EV_FREE);
+ ev->initialized = true;
+}
+
+void qemu_event_destroy(QemuEvent *ev)
+{
+ assert(ev->initialized);
+ ev->initialized = false;
+ CloseHandle(ev->event);
+}
+
+void qemu_event_set(QemuEvent *ev)
+{
+ assert(ev->initialized);
+ /* qemu_event_set has release semantics, but because it *loads*
+ * ev->value we need a full memory barrier here.
+ */
+ smp_mb();
+ if (qatomic_read(&ev->value) != EV_SET) {
+ if (qatomic_xchg(&ev->value, EV_SET) == EV_BUSY) {
+ /* There were waiters, wake them up. */
+ SetEvent(ev->event);
+ }
+ }
+}
+
+void qemu_event_reset(QemuEvent *ev)
+{
+ unsigned value;
+
+ assert(ev->initialized);
+ value = qatomic_read(&ev->value);
+ smp_mb_acquire();
+ if (value == EV_SET) {
+ /* If there was a concurrent reset (or even reset+wait),
+ * do nothing. Otherwise change EV_SET->EV_FREE.
+ */
+ qatomic_or(&ev->value, EV_FREE);
+ }
+}
+
+void qemu_event_wait(QemuEvent *ev)
+{
+ unsigned value;
+
+ assert(ev->initialized);
+ value = qatomic_read(&ev->value);
+ smp_mb_acquire();
+ if (value != EV_SET) {
+ if (value == EV_FREE) {
+ /* qemu_event_set is not yet going to call SetEvent, but we are
+ * going to do another check for EV_SET below when setting EV_BUSY.
+ * At that point it is safe to call WaitForSingleObject.
+ */
+ ResetEvent(ev->event);
+
+ /* Tell qemu_event_set that there are waiters. No need to retry
+ * because there cannot be a concurrent busy->free transition.
+ * After the CAS, the event will be either set or busy.
+ */
+ if (qatomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) {
+ value = EV_SET;
+ } else {
+ value = EV_BUSY;
+ }
+ }
+ if (value == EV_BUSY) {
+ WaitForSingleObject(ev->event, INFINITE);
+ }
+ }
+}
+
+struct QemuThreadData {
+ /* Passed to win32_start_routine. */
+ void *(*start_routine)(void *);
+ void *arg;
+ short mode;
+ NotifierList exit;
+
+ /* Only used for joinable threads. */
+ bool exited;
+ void *ret;
+ CRITICAL_SECTION cs;
+};
+
+static bool atexit_registered;
+static NotifierList main_thread_exit;
+
+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)
+{
+ QemuThreadData *data = (QemuThreadData *) arg;
+ void *(*start_routine)(void *) = data->start_routine;
+ void *thread_arg = data->arg;
+
+ qemu_thread_data = data;
+ qemu_thread_exit(start_routine(thread_arg));
+ abort();
+}
+
+void qemu_thread_exit(void *arg)
+{
+ QemuThreadData *data = qemu_thread_data;
+
+ notifier_list_notify(&data->exit, NULL);
+ if (data->mode == QEMU_THREAD_JOINABLE) {
+ data->ret = arg;
+ EnterCriticalSection(&data->cs);
+ data->exited = true;
+ LeaveCriticalSection(&data->cs);
+ } else {
+ g_free(data);
+ }
+ _endthreadex(0);
+}
+
+void *qemu_thread_join(QemuThread *thread)
+{
+ QemuThreadData *data;
+ void *ret;
+ HANDLE handle;
+
+ data = thread->data;
+ if (data->mode == QEMU_THREAD_DETACHED) {
+ return NULL;
+ }
+
+ /*
+ * Because multiple copies of the QemuThread can exist via
+ * qemu_thread_get_self, we need to store a value that cannot
+ * leak there. The simplest, non racy way is to store the TID,
+ * discard the handle that _beginthreadex gives back, and
+ * get another copy of the handle here.
+ */
+ handle = qemu_thread_get_handle(thread);
+ if (handle) {
+ WaitForSingleObject(handle, INFINITE);
+ CloseHandle(handle);
+ }
+ ret = data->ret;
+ DeleteCriticalSection(&data->cs);
+ g_free(data);
+ return ret;
+}
+
+void qemu_thread_create(QemuThread *thread, const char *name,
+ void *(*start_routine)(void *),
+ void *arg, int mode)
+{
+ HANDLE hThread;
+ struct QemuThreadData *data;
+
+ data = g_malloc(sizeof *data);
+ data->start_routine = start_routine;
+ data->arg = arg;
+ data->mode = mode;
+ data->exited = false;
+ notifier_list_init(&data->exit);
+
+ if (data->mode != QEMU_THREAD_DETACHED) {
+ InitializeCriticalSection(&data->cs);
+ }
+
+ hThread = (HANDLE) _beginthreadex(NULL, 0, win32_start_routine,
+ data, 0, &thread->tid);
+ if (!hThread) {
+ error_exit(GetLastError(), __func__);
+ }
+ CloseHandle(hThread);
+ thread->data = data;
+}
+
+void qemu_thread_get_self(QemuThread *thread)
+{
+ thread->data = qemu_thread_data;
+ thread->tid = GetCurrentThreadId();
+}
+
+HANDLE qemu_thread_get_handle(QemuThread *thread)
+{
+ QemuThreadData *data;
+ HANDLE handle;
+
+ data = thread->data;
+ if (data->mode == QEMU_THREAD_DETACHED) {
+ return NULL;
+ }
+
+ EnterCriticalSection(&data->cs);
+ if (!data->exited) {
+ handle = OpenThread(SYNCHRONIZE | THREAD_SUSPEND_RESUME |
+ THREAD_SET_CONTEXT, FALSE, thread->tid);
+ } else {
+ handle = NULL;
+ }
+ LeaveCriticalSection(&data->cs);
+ return handle;
+}
+
+bool qemu_thread_is_self(QemuThread *thread)
+{
+ return GetCurrentThreadId() == thread->tid;
+}