coroutine-gthread.c 3.58 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
/*
 * GThread coroutine initialization code
 *
 * Copyright (C) 2006  Anthony Liguori <anthony@codemonkey.ws>
 * Copyright (C) 2011  Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
 *
 * 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 <http://www.gnu.org/licenses/>.
 */

#include <glib.h>
#include "qemu-common.h"
#include "qemu-coroutine-int.h"

typedef struct {
    Coroutine base;
    GThread *thread;
    bool runnable;
    CoroutineAction action;
} CoroutineGThread;

static GCond *coroutine_cond;
static GStaticMutex coroutine_lock = G_STATIC_MUTEX_INIT;
static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT;

static void __attribute__((constructor)) coroutine_init(void)
{
    if (!g_thread_supported()) {
        g_thread_init(NULL);
    }

    coroutine_cond = g_cond_new();
}

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;

    g_static_private_set(&coroutine_key, co, NULL);
    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 = qemu_mallocz(sizeof(*co));
    co->thread = g_thread_create_full(coroutine_thread, co, 0, TRUE, TRUE,
                                      G_THREAD_PRIORITY_NORMAL, NULL);
    if (!co->thread) {
        qemu_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);
    qemu_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 = g_static_private_get(&coroutine_key);

    if (!co) {
        co = qemu_mallocz(sizeof(*co));
        co->runnable = true;
        g_static_private_set(&coroutine_key, co, (GDestroyNotify)qemu_free);
    }

    return &co->base;
}

bool qemu_in_coroutine(void)
{
    CoroutineGThread *co = g_static_private_get(&coroutine_key);

    return co && co->base.caller;
}