qemu-coroutine.c 3.02 KB
Newer Older
Kevin Wolf's avatar
Kevin Wolf committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * QEMU coroutines
 *
 * Copyright IBM, Corp. 2011
 *
 * Authors:
 *  Stefan Hajnoczi    <stefanha@linux.vnet.ibm.com>
 *  Kevin Wolf         <kwolf@redhat.com>
 *
 * This work is licensed under the terms of the GNU LGPL, version 2 or later.
 * See the COPYING.LIB file in the top-level directory.
 *
 */

#include "trace.h"
#include "qemu-common.h"
17
#include "qemu/thread.h"
18 19
#include "block/coroutine.h"
#include "block/coroutine_int.h"
Kevin Wolf's avatar
Kevin Wolf committed
20

21 22 23 24 25 26
enum {
    /* Maximum free pool size prevents holding too many freed coroutines */
    POOL_MAX_SIZE = 64,
};

/** Free list to speed up creation */
27
static QemuMutex pool_lock;
28 29 30
static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool);
static unsigned int pool_size;

Kevin Wolf's avatar
Kevin Wolf committed
31 32
Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
{
33
    Coroutine *co = NULL;
34

35 36 37 38 39 40 41 42
    if (CONFIG_COROUTINE_POOL) {
        qemu_mutex_lock(&pool_lock);
        co = QSLIST_FIRST(&pool);
        if (co) {
            QSLIST_REMOVE_HEAD(&pool, pool_next);
            pool_size--;
        }
        qemu_mutex_unlock(&pool_lock);
43 44 45
    }

    if (!co) {
46 47 48
        co = qemu_coroutine_new();
    }

Kevin Wolf's avatar
Kevin Wolf committed
49
    co->entry = entry;
50
    QTAILQ_INIT(&co->co_queue_wakeup);
Kevin Wolf's avatar
Kevin Wolf committed
51 52 53
    return co;
}

54 55
static void coroutine_delete(Coroutine *co)
{
56 57 58 59 60 61 62 63 64
    if (CONFIG_COROUTINE_POOL) {
        qemu_mutex_lock(&pool_lock);
        if (pool_size < POOL_MAX_SIZE) {
            QSLIST_INSERT_HEAD(&pool, co, pool_next);
            co->caller = NULL;
            pool_size++;
            qemu_mutex_unlock(&pool_lock);
            return;
        }
65
        qemu_mutex_unlock(&pool_lock);
66 67 68 69 70
    }

    qemu_coroutine_delete(co);
}

71 72 73 74 75 76
static void __attribute__((constructor)) coroutine_pool_init(void)
{
    qemu_mutex_init(&pool_lock);
}

static void __attribute__((destructor)) coroutine_pool_cleanup(void)
77 78 79 80 81 82 83 84
{
    Coroutine *co;
    Coroutine *tmp;

    QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) {
        QSLIST_REMOVE_HEAD(&pool, pool_next);
        qemu_coroutine_delete(co);
    }
85 86

    qemu_mutex_destroy(&pool_lock);
87 88
}

Kevin Wolf's avatar
Kevin Wolf committed
89 90 91 92 93 94
static void coroutine_swap(Coroutine *from, Coroutine *to)
{
    CoroutineAction ret;

    ret = qemu_coroutine_switch(from, to, COROUTINE_YIELD);

95 96
    qemu_co_queue_run_restart(to);

Kevin Wolf's avatar
Kevin Wolf committed
97 98 99 100 101
    switch (ret) {
    case COROUTINE_YIELD:
        return;
    case COROUTINE_TERMINATE:
        trace_qemu_coroutine_terminate(to);
102
        coroutine_delete(to);
Kevin Wolf's avatar
Kevin Wolf committed
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 132 133 134 135 136 137 138 139
        return;
    default:
        abort();
    }
}

void qemu_coroutine_enter(Coroutine *co, void *opaque)
{
    Coroutine *self = qemu_coroutine_self();

    trace_qemu_coroutine_enter(self, co, opaque);

    if (co->caller) {
        fprintf(stderr, "Co-routine re-entered recursively\n");
        abort();
    }

    co->caller = self;
    co->entry_arg = opaque;
    coroutine_swap(self, co);
}

void coroutine_fn qemu_coroutine_yield(void)
{
    Coroutine *self = qemu_coroutine_self();
    Coroutine *to = self->caller;

    trace_qemu_coroutine_yield(self, to);

    if (!to) {
        fprintf(stderr, "Co-routine is yielding to no one\n");
        abort();
    }

    self->caller = NULL;
    coroutine_swap(self, to);
}