]> git.proxmox.com Git - qemu.git/blob - qemu-coroutine.c
coroutine: protect global pool with a mutex
[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 "block/coroutine.h"
19 #include "block/coroutine_int.h"
20
21 enum {
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 */
27 static QemuMutex pool_lock;
28 static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool);
29 static unsigned int pool_size;
30
31 Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
32 {
33 Coroutine *co;
34
35 qemu_mutex_lock(&pool_lock);
36 co = QSLIST_FIRST(&pool);
37 if (co) {
38 QSLIST_REMOVE_HEAD(&pool, pool_next);
39 pool_size--;
40 }
41 qemu_mutex_unlock(&pool_lock);
42
43 if (!co) {
44 co = qemu_coroutine_new();
45 }
46
47 co->entry = entry;
48 return co;
49 }
50
51 static void coroutine_delete(Coroutine *co)
52 {
53 qemu_mutex_lock(&pool_lock);
54 if (pool_size < POOL_MAX_SIZE) {
55 QSLIST_INSERT_HEAD(&pool, co, pool_next);
56 co->caller = NULL;
57 pool_size++;
58 qemu_mutex_unlock(&pool_lock);
59 return;
60 }
61 qemu_mutex_unlock(&pool_lock);
62
63 qemu_coroutine_delete(co);
64 }
65
66 static void __attribute__((constructor)) coroutine_pool_init(void)
67 {
68 qemu_mutex_init(&pool_lock);
69 }
70
71 static void __attribute__((destructor)) coroutine_pool_cleanup(void)
72 {
73 Coroutine *co;
74 Coroutine *tmp;
75
76 QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) {
77 QSLIST_REMOVE_HEAD(&pool, pool_next);
78 qemu_coroutine_delete(co);
79 }
80
81 qemu_mutex_destroy(&pool_lock);
82 }
83
84 static void coroutine_swap(Coroutine *from, Coroutine *to)
85 {
86 CoroutineAction ret;
87
88 ret = qemu_coroutine_switch(from, to, COROUTINE_YIELD);
89
90 switch (ret) {
91 case COROUTINE_YIELD:
92 return;
93 case COROUTINE_TERMINATE:
94 trace_qemu_coroutine_terminate(to);
95 coroutine_delete(to);
96 return;
97 default:
98 abort();
99 }
100 }
101
102 void qemu_coroutine_enter(Coroutine *co, void *opaque)
103 {
104 Coroutine *self = qemu_coroutine_self();
105
106 trace_qemu_coroutine_enter(self, co, opaque);
107
108 if (co->caller) {
109 fprintf(stderr, "Co-routine re-entered recursively\n");
110 abort();
111 }
112
113 co->caller = self;
114 co->entry_arg = opaque;
115 coroutine_swap(self, co);
116 }
117
118 void coroutine_fn qemu_coroutine_yield(void)
119 {
120 Coroutine *self = qemu_coroutine_self();
121 Coroutine *to = self->caller;
122
123 trace_qemu_coroutine_yield(self, to);
124
125 if (!to) {
126 fprintf(stderr, "Co-routine is yielding to no one\n");
127 abort();
128 }
129
130 self->caller = NULL;
131 coroutine_swap(self, to);
132 }