coroutine: rewrite pool to avoid mutex
[qemu/rayw.git] / qemu-coroutine.c
blob93fddc7dfbdd2d769eb58276206b45cd41a433d6
1 /*
2 * QEMU coroutines
4 * Copyright IBM, Corp. 2011
6 * Authors:
7 * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
8 * Kevin Wolf <kwolf@redhat.com>
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.
15 #include "trace.h"
16 #include "qemu-common.h"
17 #include "qemu/thread.h"
18 #include "qemu/atomic.h"
19 #include "block/coroutine.h"
20 #include "block/coroutine_int.h"
22 enum {
23 POOL_BATCH_SIZE = 64,
26 /** Free list to speed up creation */
27 static QSLIST_HEAD(, Coroutine) release_pool = QSLIST_HEAD_INITIALIZER(pool);
28 static unsigned int release_pool_size;
29 static __thread QSLIST_HEAD(, Coroutine) alloc_pool = QSLIST_HEAD_INITIALIZER(pool);
30 static __thread Notifier coroutine_pool_cleanup_notifier;
32 static void coroutine_pool_cleanup(Notifier *n, void *value)
34 Coroutine *co;
35 Coroutine *tmp;
37 QSLIST_FOREACH_SAFE(co, &alloc_pool, pool_next, tmp) {
38 QSLIST_REMOVE_HEAD(&alloc_pool, pool_next);
39 qemu_coroutine_delete(co);
43 Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
45 Coroutine *co = NULL;
47 if (CONFIG_COROUTINE_POOL) {
48 co = QSLIST_FIRST(&alloc_pool);
49 if (!co) {
50 if (release_pool_size > POOL_BATCH_SIZE) {
51 /* Slow path; a good place to register the destructor, too. */
52 if (!coroutine_pool_cleanup_notifier.notify) {
53 coroutine_pool_cleanup_notifier.notify = coroutine_pool_cleanup;
54 qemu_thread_atexit_add(&coroutine_pool_cleanup_notifier);
57 /* This is not exact; there could be a little skew between
58 * release_pool_size and the actual size of release_pool. But
59 * it is just a heuristic, it does not need to be perfect.
61 release_pool_size = 0;
62 QSLIST_MOVE_ATOMIC(&alloc_pool, &release_pool);
63 co = QSLIST_FIRST(&alloc_pool);
66 if (co) {
67 QSLIST_REMOVE_HEAD(&alloc_pool, pool_next);
71 if (!co) {
72 co = qemu_coroutine_new();
75 co->entry = entry;
76 QTAILQ_INIT(&co->co_queue_wakeup);
77 return co;
80 static void coroutine_delete(Coroutine *co)
82 co->caller = NULL;
84 if (CONFIG_COROUTINE_POOL) {
85 if (release_pool_size < POOL_BATCH_SIZE * 2) {
86 QSLIST_INSERT_HEAD_ATOMIC(&release_pool, co, pool_next);
87 atomic_inc(&release_pool_size);
88 return;
92 qemu_coroutine_delete(co);
95 static void coroutine_swap(Coroutine *from, Coroutine *to)
97 CoroutineAction ret;
99 ret = qemu_coroutine_switch(from, to, COROUTINE_YIELD);
101 qemu_co_queue_run_restart(to);
103 switch (ret) {
104 case COROUTINE_YIELD:
105 return;
106 case COROUTINE_TERMINATE:
107 trace_qemu_coroutine_terminate(to);
108 coroutine_delete(to);
109 return;
110 default:
111 abort();
115 void qemu_coroutine_enter(Coroutine *co, void *opaque)
117 Coroutine *self = qemu_coroutine_self();
119 trace_qemu_coroutine_enter(self, co, opaque);
121 if (co->caller) {
122 fprintf(stderr, "Co-routine re-entered recursively\n");
123 abort();
126 co->caller = self;
127 co->entry_arg = opaque;
128 coroutine_swap(self, co);
131 void coroutine_fn qemu_coroutine_yield(void)
133 Coroutine *self = qemu_coroutine_self();
134 Coroutine *to = self->caller;
136 trace_qemu_coroutine_yield(self, to);
138 if (!to) {
139 fprintf(stderr, "Co-routine is yielding to no one\n");
140 abort();
143 self->caller = NULL;
144 coroutine_swap(self, to);
147 void qemu_coroutine_adjust_pool_size(int n)