/* * GThread coroutine initialization code * * Copyright (C) 2006 Anthony Liguori * Copyright (C) 2011 Aneesh Kumar K.V * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.0 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ #include #include "qemu-common.h" #include "qemu-coroutine-int.h" typedef struct { Coroutine base; GThread *thread; bool runnable; bool free_on_thread_exit; CoroutineAction action; } CoroutineGThread; static GStaticMutex coroutine_lock = G_STATIC_MUTEX_INIT; /* GLib 2.31 and beyond deprecated various parts of the thread API, * but the new interfaces are not available in older GLib versions * so we have to cope with both. */ #if GLIB_CHECK_VERSION(2, 31, 0) /* Default zero-initialisation is sufficient for 2.31+ GCond */ static GCond the_coroutine_cond; static GCond *coroutine_cond = &the_coroutine_cond; static inline void init_coroutine_cond(void) { } /* Awkwardly, the GPrivate API doesn't provide a way to update the * GDestroyNotify handler for the coroutine key dynamically. So instead * we track whether or not the CoroutineGThread should be freed on * thread exit / coroutine key update using the free_on_thread_exit * field. */ static void coroutine_destroy_notify(gpointer data) { CoroutineGThread *co = data; if (co && co->free_on_thread_exit) { g_free(co); } } static GPrivate coroutine_key = G_PRIVATE_INIT(coroutine_destroy_notify); static inline CoroutineGThread *get_coroutine_key(void) { return g_private_get(&coroutine_key); } static inline void set_coroutine_key(CoroutineGThread *co, bool free_on_thread_exit) { /* Unlike g_static_private_set() this does not call the GDestroyNotify * if the previous value of the key was NULL. Fortunately we only need * the GDestroyNotify in the non-NULL key case. */ co->free_on_thread_exit = free_on_thread_exit; g_private_replace(&coroutine_key, co); } static inline GThread *create_thread(GThreadFunc func, gpointer data) { return g_thread_new("coroutine", func, data); } #else /* Handle older GLib versions */ static GCond *coroutine_cond; static inline void init_coroutine_cond(void) { coroutine_cond = g_cond_new(); } static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT; static inline CoroutineGThread *get_coroutine_key(void) { return g_static_private_get(&coroutine_key); } static inline void set_coroutine_key(CoroutineGThread *co, bool free_on_thread_exit) { g_static_private_set(&coroutine_key, co, free_on_thread_exit ? (GDestroyNotify)g_free : NULL); } static inline GThread *create_thread(GThreadFunc func, gpointer data) { return g_thread_create_full(func, data, 0, TRUE, TRUE, G_THREAD_PRIORITY_NORMAL, NULL); } #endif static void __attribute__((constructor)) coroutine_init(void) { if (!g_thread_supported()) { #if !GLIB_CHECK_VERSION(2, 31, 0) g_thread_init(NULL); #else fprintf(stderr, "glib threading failed to initialize.\n"); exit(1); #endif } init_coroutine_cond(); } static void coroutine_wait_runnable_locked(CoroutineGThread *co) { while (!co->runnable) { g_cond_wait(coroutine_cond, g_static_mutex_get_mutex(&coroutine_lock)); } } static void coroutine_wait_runnable(CoroutineGThread *co) { g_static_mutex_lock(&coroutine_lock); coroutine_wait_runnable_locked(co); g_static_mutex_unlock(&coroutine_lock); } static gpointer coroutine_thread(gpointer opaque) { CoroutineGThread *co = opaque; set_coroutine_key(co, false); coroutine_wait_runnable(co); co->base.entry(co->base.entry_arg); qemu_coroutine_switch(&co->base, co->base.caller, COROUTINE_TERMINATE); return NULL; } Coroutine *qemu_coroutine_new(void) { CoroutineGThread *co; co = g_malloc0(sizeof(*co)); co->thread = create_thread(coroutine_thread, co); if (!co->thread) { g_free(co); return NULL; } return &co->base; } void qemu_coroutine_delete(Coroutine *co_) { CoroutineGThread *co = DO_UPCAST(CoroutineGThread, base, co_); g_thread_join(co->thread); g_free(co); } CoroutineAction qemu_coroutine_switch(Coroutine *from_, Coroutine *to_, CoroutineAction action) { CoroutineGThread *from = DO_UPCAST(CoroutineGThread, base, from_); CoroutineGThread *to = DO_UPCAST(CoroutineGThread, base, to_); g_static_mutex_lock(&coroutine_lock); from->runnable = false; from->action = action; to->runnable = true; to->action = action; g_cond_broadcast(coroutine_cond); if (action != COROUTINE_TERMINATE) { coroutine_wait_runnable_locked(from); } g_static_mutex_unlock(&coroutine_lock); return from->action; } Coroutine *qemu_coroutine_self(void) { CoroutineGThread *co = get_coroutine_key(); if (!co) { co = g_malloc0(sizeof(*co)); co->runnable = true; set_coroutine_key(co, true); } return &co->base; } bool qemu_in_coroutine(void) { CoroutineGThread *co = get_coroutine_key(); return co && co->base.caller; }