Merge remote-tracking branch 'mdroth/qga-pull-4-2-13' into staging
[sdk/emulator/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 "block/coroutine.h"
18 #include "block/coroutine_int.h"
19
20 enum {
21     /* Maximum free pool size prevents holding too many freed coroutines */
22     POOL_MAX_SIZE = 64,
23 };
24
25 /** Free list to speed up creation */
26 static QSLIST_HEAD(, Coroutine) pool = QSLIST_HEAD_INITIALIZER(pool);
27 static unsigned int pool_size;
28
29 Coroutine *qemu_coroutine_create(CoroutineEntry *entry)
30 {
31     Coroutine *co;
32
33     co = QSLIST_FIRST(&pool);
34     if (co) {
35         QSLIST_REMOVE_HEAD(&pool, pool_next);
36         pool_size--;
37     } else {
38         co = qemu_coroutine_new();
39     }
40
41     co->entry = entry;
42     return co;
43 }
44
45 static void coroutine_delete(Coroutine *co)
46 {
47     if (pool_size < POOL_MAX_SIZE) {
48         QSLIST_INSERT_HEAD(&pool, co, pool_next);
49         co->caller = NULL;
50         pool_size++;
51         return;
52     }
53
54     qemu_coroutine_delete(co);
55 }
56
57 static void __attribute__((destructor)) coroutine_cleanup(void)
58 {
59     Coroutine *co;
60     Coroutine *tmp;
61
62     QSLIST_FOREACH_SAFE(co, &pool, pool_next, tmp) {
63         QSLIST_REMOVE_HEAD(&pool, pool_next);
64         qemu_coroutine_delete(co);
65     }
66 }
67
68 static void coroutine_swap(Coroutine *from, Coroutine *to)
69 {
70     CoroutineAction ret;
71
72     ret = qemu_coroutine_switch(from, to, COROUTINE_YIELD);
73
74     switch (ret) {
75     case COROUTINE_YIELD:
76         return;
77     case COROUTINE_TERMINATE:
78         trace_qemu_coroutine_terminate(to);
79         coroutine_delete(to);
80         return;
81     default:
82         abort();
83     }
84 }
85
86 void qemu_coroutine_enter(Coroutine *co, void *opaque)
87 {
88     Coroutine *self = qemu_coroutine_self();
89
90     trace_qemu_coroutine_enter(self, co, opaque);
91
92     if (co->caller) {
93         fprintf(stderr, "Co-routine re-entered recursively\n");
94         abort();
95     }
96
97     co->caller = self;
98     co->entry_arg = opaque;
99     coroutine_swap(self, co);
100 }
101
102 void coroutine_fn qemu_coroutine_yield(void)
103 {
104     Coroutine *self = qemu_coroutine_self();
105     Coroutine *to = self->caller;
106
107     trace_qemu_coroutine_yield(self, to);
108
109     if (!to) {
110         fprintf(stderr, "Co-routine is yielding to no one\n");
111         abort();
112     }
113
114     self->caller = NULL;
115     coroutine_swap(self, to);
116 }