qemu/qemu-coroutine.c
<<
>>
Prefs
   1/*
   2 * QEMU coroutines
   3 *
   4 * Copyright IBM, Corp. 2011
   5 *
   6 * Authors:
   7 *  Stefan Hajnoczi    <stefanha@linux.vnet.ibm.com>
   8 *  Kevin Wolf         <kwolf@redhat.com>
   9 *
  10 * This work is licensed under the terms of the GNU LGPL, version 2 or later.
  11 * See the COPYING.LIB file in the top-level directory.
  12 *
  13 */
  14
  15#include "trace.h"
  16#include "qemu-common.h"
  17#include "qemu/thread.h"
  18#include "block/coroutine.h"
  19#include "block/coroutine_int.h"
  20
  21enum {
  22    /* Maximum free pool size prevents holding too many freed coroutines */
  23    POOL_MAX_SIZE = 64,
  24};
  25
  26/** Free list to speed up creation */
  27static QemuMutex pool_lock;
  28static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool);
  29static unsigned int pool_size;
  30
  31Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
  32{
  33    Coroutine *co = NULL;
  34
  35    if (CONFIG_COROUTINE_POOL) {
  36        qemu_mutex_lock(&pool_lock);
  37        co = QSLIST_FIRST(&pool);
  38        if (co) {
  39            QSLIST_REMOVE_HEAD(&pool, pool_next);
  40            pool_size--;
  41        }
  42        qemu_mutex_unlock(&pool_lock);
  43    }
  44
  45    if (!co) {
  46        co = qemu_coroutine_new();
  47    }
  48
  49    co->entry = entry;
  50    QTAILQ_INIT(&co->co_queue_wakeup);
  51    return co;
  52}
  53
  54static void coroutine_delete(Coroutine *co)
  55{
  56    if (CONFIG_COROUTINE_POOL) {
  57        qemu_mutex_lock(&pool_lock);
  58        if (pool_size < POOL_MAX_SIZE) {
  59            QSLIST_INSERT_HEAD(&pool, co, pool_next);
  60            co->caller = NULL;
  61            pool_size++;
  62            qemu_mutex_unlock(&pool_lock);
  63            return;
  64        }
  65        qemu_mutex_unlock(&pool_lock);
  66    }
  67
  68    qemu_coroutine_delete(co);
  69}
  70
  71static void __attribute__((constructor)) coroutine_pool_init(void)
  72{
  73    qemu_mutex_init(&pool_lock);
  74}
  75
  76static void __attribute__((destructor)) coroutine_pool_cleanup(void)
  77{
  78    Coroutine *co;
  79    Coroutine *tmp;
  80
  81    QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) {
  82        QSLIST_REMOVE_HEAD(&pool, pool_next);
  83        qemu_coroutine_delete(co);
  84    }
  85
  86    qemu_mutex_destroy(&pool_lock);
  87}
  88
  89static void coroutine_swap(Coroutine *from, Coroutine *to)
  90{
  91    CoroutineAction ret;
  92
  93    ret = qemu_coroutine_switch(from, to, COROUTINE_YIELD);
  94
  95    qemu_co_queue_run_restart(to);
  96
  97    switch (ret) {
  98    case COROUTINE_YIELD:
  99        return;
 100    case COROUTINE_TERMINATE:
 101        trace_qemu_coroutine_terminate(to);
 102        coroutine_delete(to);
 103        return;
 104    default:
 105        abort();
 106    }
 107}
 108
 109void qemu_coroutine_enter(Coroutine *co, void *opaque)
 110{
 111    Coroutine *self = qemu_coroutine_self();
 112
 113    trace_qemu_coroutine_enter(self, co, opaque);
 114
 115    if (co->caller) {
 116        fprintf(stderr, "Co-routine re-entered recursively\n");
 117        abort();
 118    }
 119
 120    co->caller = self;
 121    co->entry_arg = opaque;
 122    coroutine_swap(self, co);
 123}
 124
 125void coroutine_fn qemu_coroutine_yield(void)
 126{
 127    Coroutine *self = qemu_coroutine_self();
 128    Coroutine *to = self->caller;
 129
 130    trace_qemu_coroutine_yield(self, to);
 131
 132    if (!to) {
 133        fprintf(stderr, "Co-routine is yielding to no one\n");
 134        abort();
 135    }
 136
 137    self->caller = NULL;
 138    coroutine_swap(self, to);
 139}
 140