#include <pthread.h>
#include <ucontext.h>
#include "qemu-common.h"
-#include "qemu-coroutine-int.h"
+#include "block/coroutine_int.h"
+
+#ifdef CONFIG_VALGRIND_H
+#include <valgrind/valgrind.h>
+#endif
enum {
/* Maximum free pool size prevents holding too many freed coroutines */
POOL_MAX_SIZE = 64,
};
+/** Free list to speed up creation */
+static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool);
+static unsigned int pool_size;
+
typedef struct {
Coroutine base;
void *stack;
jmp_buf env;
+
+#ifdef CONFIG_VALGRIND_H
+ unsigned int valgrind_stack_id;
+#endif
+
} CoroutineUContext;
/**
/** Currently executing coroutine */
Coroutine *current;
- /** Free list to speed up creation */
- QLIST_HEAD(, Coroutine) pool;
- unsigned int pool_size;
-
/** The default coroutine */
CoroutineUContext leader;
} CoroutineThreadState;
CoroutineThreadState *s = pthread_getspecific(thread_state_key);
if (!s) {
- s = qemu_mallocz(sizeof(*s));
+ s = g_malloc0(sizeof(*s));
s->current = &s->leader.base;
- QLIST_INIT(&s->pool);
pthread_setspecific(thread_state_key, s);
}
return s;
static void qemu_coroutine_thread_cleanup(void *opaque)
{
CoroutineThreadState *s = opaque;
+
+ g_free(s);
+}
+
+static void __attribute__((destructor)) coroutine_cleanup(void)
+{
Coroutine *co;
Coroutine *tmp;
- QLIST_FOREACH_SAFE(co, &s->pool, pool_next, tmp) {
- qemu_free(DO_UPCAST(CoroutineUContext, base, co)->stack);
- qemu_free(co);
+ QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) {
+ g_free(DO_UPCAST(CoroutineUContext, base, co)->stack);
+ g_free(co);
}
- qemu_free(s);
}
static void __attribute__((constructor)) coroutine_init(void)
CoroutineUContext *co;
ucontext_t old_uc, uc;
jmp_buf old_env;
- union cc_arg arg;
+ union cc_arg arg = {0};
/* The ucontext functions preserve signal masks which incurs a system call
* overhead. setjmp()/longjmp() does not preserve signal masks but only
abort();
}
- co = qemu_mallocz(sizeof(*co));
- co->stack = qemu_malloc(stack_size);
+ co = g_malloc0(sizeof(*co));
+ co->stack = g_malloc(stack_size);
co->base.entry_arg = &old_env; /* stash away our jmp_buf */
uc.uc_link = &old_uc;
uc.uc_stack.ss_size = stack_size;
uc.uc_stack.ss_flags = 0;
+#ifdef CONFIG_VALGRIND_H
+ co->valgrind_stack_id =
+ VALGRIND_STACK_REGISTER(co->stack, co->stack + stack_size);
+#endif
+
arg.p = co;
makecontext(&uc, (void (*)(void))coroutine_trampoline,
Coroutine *qemu_coroutine_new(void)
{
- CoroutineThreadState *s = coroutine_get_thread_state();
Coroutine *co;
- co = QLIST_FIRST(&s->pool);
+ co = QSLIST_FIRST(&pool);
if (co) {
- QLIST_REMOVE(co, pool_next);
- s->pool_size--;
+ QSLIST_REMOVE_HEAD(&pool, pool_next);
+ pool_size--;
} else {
co = coroutine_new();
}
return co;
}
+#ifdef CONFIG_VALGRIND_H
+#ifdef CONFIG_PRAGMA_DISABLE_UNUSED_BUT_SET
+/* Work around an unused variable in the valgrind.h macro... */
+#pragma GCC diagnostic ignored "-Wunused-but-set-variable"
+#endif
+static inline void valgrind_stack_deregister(CoroutineUContext *co)
+{
+ VALGRIND_STACK_DEREGISTER(co->valgrind_stack_id);
+}
+#ifdef CONFIG_PRAGMA_DISABLE_UNUSED_BUT_SET
+#pragma GCC diagnostic error "-Wunused-but-set-variable"
+#endif
+#endif
+
void qemu_coroutine_delete(Coroutine *co_)
{
- CoroutineThreadState *s = coroutine_get_thread_state();
CoroutineUContext *co = DO_UPCAST(CoroutineUContext, base, co_);
- if (s->pool_size < POOL_MAX_SIZE) {
- QLIST_INSERT_HEAD(&s->pool, &co->base, pool_next);
+ if (pool_size < POOL_MAX_SIZE) {
+ QSLIST_INSERT_HEAD(&pool, &co->base, pool_next);
co->base.caller = NULL;
- s->pool_size++;
+ pool_size++;
return;
}
- qemu_free(co->stack);
- qemu_free(co);
+#ifdef CONFIG_VALGRIND_H
+ valgrind_stack_deregister(co);
+#endif
+
+ g_free(co->stack);
+ g_free(co);
}
CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_,