#include "qemu/thread.h"
#include "qemu/atomic.h"
#include "qemu/notify.h"
+#include "qemu-thread-common.h"
static bool name_threads;
abort();
}
+static void compute_abs_deadline(struct timespec *ts, int ms)
+{
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ ts->tv_nsec = tv.tv_usec * 1000 + (ms % 1000) * 1000000;
+ ts->tv_sec = tv.tv_sec + ms / 1000;
+ if (ts->tv_nsec >= 1000000000) {
+ ts->tv_sec++;
+ ts->tv_nsec -= 1000000000;
+ }
+}
+
void qemu_mutex_init(QemuMutex *mutex)
{
int err;
err = pthread_mutex_init(&mutex->lock, NULL);
if (err)
error_exit(err, __func__);
+ qemu_mutex_post_init(mutex);
}
void qemu_mutex_destroy(QemuMutex *mutex)
{
int err;
+ assert(mutex->initialized);
+ mutex->initialized = false;
err = pthread_mutex_destroy(&mutex->lock);
if (err)
error_exit(err, __func__);
}
-void qemu_mutex_lock(QemuMutex *mutex)
+void qemu_mutex_lock_impl(QemuMutex *mutex, const char *file, const int line)
{
int err;
+ assert(mutex->initialized);
+ qemu_mutex_pre_lock(mutex, file, line);
err = pthread_mutex_lock(&mutex->lock);
if (err)
error_exit(err, __func__);
+ qemu_mutex_post_lock(mutex, file, line);
}
-int qemu_mutex_trylock(QemuMutex *mutex)
+int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line)
{
- return pthread_mutex_trylock(&mutex->lock);
+ int err;
+
+ assert(mutex->initialized);
+ err = pthread_mutex_trylock(&mutex->lock);
+ if (err == 0) {
+ qemu_mutex_post_lock(mutex, file, line);
+ return 0;
+ }
+ if (err != EBUSY) {
+ error_exit(err, __func__);
+ }
+ return -EBUSY;
}
-void qemu_mutex_unlock(QemuMutex *mutex)
+void qemu_mutex_unlock_impl(QemuMutex *mutex, const char *file, const int line)
{
int err;
+ assert(mutex->initialized);
+ qemu_mutex_pre_unlock(mutex, file, line);
err = pthread_mutex_unlock(&mutex->lock);
if (err)
error_exit(err, __func__);
if (err) {
error_exit(err, __func__);
}
+ mutex->initialized = true;
}
void qemu_cond_init(QemuCond *cond)
err = pthread_cond_init(&cond->cond, NULL);
if (err)
error_exit(err, __func__);
+ cond->initialized = true;
}
void qemu_cond_destroy(QemuCond *cond)
{
int err;
+ assert(cond->initialized);
+ cond->initialized = false;
err = pthread_cond_destroy(&cond->cond);
if (err)
error_exit(err, __func__);
{
int err;
+ assert(cond->initialized);
err = pthread_cond_signal(&cond->cond);
if (err)
error_exit(err, __func__);
{
int err;
+ assert(cond->initialized);
err = pthread_cond_broadcast(&cond->cond);
if (err)
error_exit(err, __func__);
}
-void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex)
+void qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, const char *file, const int line)
{
int err;
+ assert(cond->initialized);
+ qemu_mutex_pre_unlock(mutex, file, line);
err = pthread_cond_wait(&cond->cond, &mutex->lock);
+ qemu_mutex_post_lock(mutex, file, line);
if (err)
error_exit(err, __func__);
}
+bool qemu_cond_timedwait_impl(QemuCond *cond, QemuMutex *mutex, int ms,
+ const char *file, const int line)
+{
+ int err;
+ struct timespec ts;
+
+ assert(cond->initialized);
+ trace_qemu_mutex_unlock(mutex, file, line);
+ compute_abs_deadline(&ts, ms);
+ err = pthread_cond_timedwait(&cond->cond, &mutex->lock, &ts);
+ trace_qemu_mutex_locked(mutex, file, line);
+ if (err && err != ETIMEDOUT) {
+ error_exit(err, __func__);
+ }
+ return err != ETIMEDOUT;
+}
+
void qemu_sem_init(QemuSemaphore *sem, int init)
{
int rc;
-#if defined(__APPLE__) || defined(__NetBSD__)
+#ifndef CONFIG_SEM_TIMEDWAIT
rc = pthread_mutex_init(&sem->lock, NULL);
if (rc != 0) {
error_exit(rc, __func__);
error_exit(errno, __func__);
}
#endif
+ sem->initialized = true;
}
void qemu_sem_destroy(QemuSemaphore *sem)
{
int rc;
-#if defined(__APPLE__) || defined(__NetBSD__)
+ assert(sem->initialized);
+ sem->initialized = false;
+#ifndef CONFIG_SEM_TIMEDWAIT
rc = pthread_cond_destroy(&sem->cond);
if (rc < 0) {
error_exit(rc, __func__);
{
int rc;
-#if defined(__APPLE__) || defined(__NetBSD__)
+ assert(sem->initialized);
+#ifndef CONFIG_SEM_TIMEDWAIT
pthread_mutex_lock(&sem->lock);
if (sem->count == UINT_MAX) {
rc = EINVAL;
#endif
}
-static void compute_abs_deadline(struct timespec *ts, int ms)
-{
- struct timeval tv;
- gettimeofday(&tv, NULL);
- ts->tv_nsec = tv.tv_usec * 1000 + (ms % 1000) * 1000000;
- ts->tv_sec = tv.tv_sec + ms / 1000;
- if (ts->tv_nsec >= 1000000000) {
- ts->tv_sec++;
- ts->tv_nsec -= 1000000000;
- }
-}
-
int qemu_sem_timedwait(QemuSemaphore *sem, int ms)
{
int rc;
struct timespec ts;
-#if defined(__APPLE__) || defined(__NetBSD__)
+ assert(sem->initialized);
+#ifndef CONFIG_SEM_TIMEDWAIT
rc = 0;
compute_abs_deadline(&ts, ms);
pthread_mutex_lock(&sem->lock);
{
int rc;
-#if defined(__APPLE__) || defined(__NetBSD__)
+ assert(sem->initialized);
+#ifndef CONFIG_SEM_TIMEDWAIT
pthread_mutex_lock(&sem->lock);
while (sem->count == 0) {
rc = pthread_cond_wait(&sem->cond, &sem->lock);
#else
static inline void qemu_futex_wake(QemuEvent *ev, int n)
{
+ assert(ev->initialized);
pthread_mutex_lock(&ev->lock);
if (n == 1) {
pthread_cond_signal(&ev->cond);
static inline void qemu_futex_wait(QemuEvent *ev, unsigned val)
{
+ assert(ev->initialized);
pthread_mutex_lock(&ev->lock);
if (ev->value == val) {
pthread_cond_wait(&ev->cond, &ev->lock);
#endif
ev->value = (init ? EV_SET : EV_FREE);
+ ev->initialized = true;
}
void qemu_event_destroy(QemuEvent *ev)
{
+ assert(ev->initialized);
+ ev->initialized = false;
#ifndef __linux__
pthread_mutex_destroy(&ev->lock);
pthread_cond_destroy(&ev->cond);
/* qemu_event_set has release semantics, but because it *loads*
* ev->value we need a full memory barrier here.
*/
+ assert(ev->initialized);
smp_mb();
if (atomic_read(&ev->value) != EV_SET) {
if (atomic_xchg(&ev->value, EV_SET) == EV_BUSY) {
{
unsigned value;
+ assert(ev->initialized);
value = atomic_read(&ev->value);
smp_mb_acquire();
if (value == EV_SET) {
{
unsigned value;
+ assert(ev->initialized);
value = atomic_read(&ev->value);
smp_mb_acquire();
if (value != EV_SET) {
}
}
-static pthread_key_t exit_key;
-
-union NotifierThreadData {
- void *ptr;
- NotifierList list;
-};
-QEMU_BUILD_BUG_ON(sizeof(union NotifierThreadData) != sizeof(void *));
+static __thread NotifierList thread_exit;
+/*
+ * Note that in this implementation you can register a thread-exit
+ * notifier for the main thread, but it will never be called.
+ * This is OK because main thread exit can only happen when the
+ * entire process is exiting, and the API allows notifiers to not
+ * be called on process exit.
+ */
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);
+ notifier_list_add(&thread_exit, notifier);
}
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)
+static void qemu_thread_atexit_notify(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);
+ /*
+ * Called when non-main thread exits (via qemu_thread_exit()
+ * or by returning from its start routine.)
+ */
+ notifier_list_notify(&thread_exit, NULL);
}
+typedef struct {
+ void *(*start_routine)(void *);
+ void *arg;
+ char *name;
+} QemuThreadArgs;
-/* 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.
- */
-static void qemu_thread_set_name(QemuThread *thread, const char *name)
+static void *qemu_thread_start(void *args)
{
-#ifdef CONFIG_PTHREAD_SETNAME_NP
- pthread_setname_np(thread->thread, name);
+ QemuThreadArgs *qemu_thread_args = args;
+ void *(*start_routine)(void *) = qemu_thread_args->start_routine;
+ void *arg = qemu_thread_args->arg;
+ void *r;
+
+#ifdef CONFIG_THREAD_SETNAME_BYTHREAD
+ /* 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.
+ */
+ if (name_threads && qemu_thread_args->name) {
+# if defined(CONFIG_PTHREAD_SETNAME_NP_W_TID)
+ pthread_setname_np(pthread_self(), qemu_thread_args->name);
+# elif defined(CONFIG_PTHREAD_SETNAME_NP_WO_TID)
+ pthread_setname_np(qemu_thread_args->name);
+# endif
+ }
#endif
+ g_free(qemu_thread_args->name);
+ g_free(qemu_thread_args);
+ pthread_cleanup_push(qemu_thread_atexit_notify, NULL);
+ r = start_routine(arg);
+ pthread_cleanup_pop(1);
+ return r;
}
void qemu_thread_create(QemuThread *thread, const char *name,
sigset_t set, oldset;
int err;
pthread_attr_t attr;
+ QemuThreadArgs *qemu_thread_args;
err = pthread_attr_init(&attr);
if (err) {
error_exit(err, __func__);
}
+ if (mode == QEMU_THREAD_DETACHED) {
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+ }
+
/* Leave signal handling to the iothread. */
sigfillset(&set);
+ /* Blocking the signals can result in undefined behaviour. */
+ sigdelset(&set, SIGSEGV);
+ sigdelset(&set, SIGFPE);
+ sigdelset(&set, SIGILL);
+ /* TODO avoid SIGBUS loss on macOS */
pthread_sigmask(SIG_SETMASK, &set, &oldset);
- err = pthread_create(&thread->thread, &attr, start_routine, arg);
+
+ qemu_thread_args = g_new0(QemuThreadArgs, 1);
+ qemu_thread_args->name = g_strdup(name);
+ qemu_thread_args->start_routine = start_routine;
+ qemu_thread_args->arg = arg;
+
+ err = pthread_create(&thread->thread, &attr,
+ qemu_thread_start, qemu_thread_args);
+
if (err)
error_exit(err, __func__);
- if (name_threads) {
- qemu_thread_set_name(thread, name);
- }
-
- if (mode == QEMU_THREAD_DETACHED) {
- err = pthread_detach(thread->thread);
- if (err) {
- error_exit(err, __func__);
- }
- }
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
pthread_attr_destroy(&attr);