]> git.proxmox.com Git - mirror_qemu.git/blob - qemu-coroutine.c
da1b9615d0ed02afd0116d389db3c948cf9e91b3
[mirror_qemu.git] / qemu-coroutine.c
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 "qemu/atomic.h"
19 #include "block/coroutine.h"
20 #include "block/coroutine_int.h"
21
22 enum {
23 POOL_BATCH_SIZE = 64,
24 };
25
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;
31
32 static void coroutine_pool_cleanup(Notifier *n, void *value)
33 {
34 Coroutine *co;
35 Coroutine *tmp;
36
37 QSLIST_FOREACH_SAFE(co, &alloc_pool, pool_next, tmp) {
38 QSLIST_REMOVE_HEAD(&alloc_pool, pool_next);
39 qemu_coroutine_delete(co);
40 }
41 }
42
43 Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
44 {
45 Coroutine *co = NULL;
46
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);
55 }
56
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.
60 */
61 release_pool_size = 0;
62 QSLIST_MOVE_ATOMIC(&alloc_pool, &release_pool);
63 co = QSLIST_FIRST(&alloc_pool);
64 }
65 }
66 if (co) {
67 QSLIST_REMOVE_HEAD(&alloc_pool, pool_next);
68 }
69 }
70
71 if (!co) {
72 co = qemu_coroutine_new();
73 }
74
75 co->entry = entry;
76 QTAILQ_INIT(&co->co_queue_wakeup);
77 return co;
78 }
79
80 static void coroutine_delete(Coroutine *co)
81 {
82 co->caller = NULL;
83
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;
89 }
90 }
91
92 qemu_coroutine_delete(co);
93 }
94
95 static void coroutine_swap(Coroutine *from, Coroutine *to)
96 {
97 CoroutineAction ret;
98
99 ret = qemu_coroutine_switch(from, to, COROUTINE_YIELD);
100
101 qemu_co_queue_run_restart(to);
102
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();
112 }
113 }
114
115 void qemu_coroutine_enter(Coroutine *co, void *opaque)
116 {
117 Coroutine *self = qemu_coroutine_self();
118
119 trace_qemu_coroutine_enter(self, co, opaque);
120
121 if (co->caller) {
122 fprintf(stderr, "Co-routine re-entered recursively\n");
123 abort();
124 }
125
126 co->caller = self;
127 co->entry_arg = opaque;
128 coroutine_swap(self, co);
129 }
130
131 void coroutine_fn qemu_coroutine_yield(void)
132 {
133 Coroutine *self = qemu_coroutine_self();
134 Coroutine *to = self->caller;
135
136 trace_qemu_coroutine_yield(self, to);
137
138 if (!to) {
139 fprintf(stderr, "Co-routine is yielding to no one\n");
140 abort();
141 }
142
143 self->caller = NULL;
144 coroutine_swap(self, to);
145 }