root / coroutine-gthread.c @ 737e150e
History | View | Annotate | Download (5.8 kB)
1 | d0e2fce5 | Aneesh Kumar K.V | /*
|
---|---|---|---|
2 | d0e2fce5 | Aneesh Kumar K.V | * GThread coroutine initialization code
|
3 | d0e2fce5 | Aneesh Kumar K.V | *
|
4 | d0e2fce5 | Aneesh Kumar K.V | * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
|
5 | d0e2fce5 | Aneesh Kumar K.V | * Copyright (C) 2011 Aneesh Kumar K.V <aneesh.kumar@linux.vnet.ibm.com>
|
6 | d0e2fce5 | Aneesh Kumar K.V | *
|
7 | d0e2fce5 | Aneesh Kumar K.V | * This library is free software; you can redistribute it and/or
|
8 | d0e2fce5 | Aneesh Kumar K.V | * modify it under the terms of the GNU Lesser General Public
|
9 | d0e2fce5 | Aneesh Kumar K.V | * License as published by the Free Software Foundation; either
|
10 | d0e2fce5 | Aneesh Kumar K.V | * version 2.0 of the License, or (at your option) any later version.
|
11 | d0e2fce5 | Aneesh Kumar K.V | *
|
12 | d0e2fce5 | Aneesh Kumar K.V | * This library is distributed in the hope that it will be useful,
|
13 | d0e2fce5 | Aneesh Kumar K.V | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 | d0e2fce5 | Aneesh Kumar K.V | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15 | d0e2fce5 | Aneesh Kumar K.V | * Lesser General Public License for more details.
|
16 | d0e2fce5 | Aneesh Kumar K.V | *
|
17 | d0e2fce5 | Aneesh Kumar K.V | * You should have received a copy of the GNU Lesser General Public
|
18 | d0e2fce5 | Aneesh Kumar K.V | * License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
19 | d0e2fce5 | Aneesh Kumar K.V | */
|
20 | d0e2fce5 | Aneesh Kumar K.V | |
21 | d0e2fce5 | Aneesh Kumar K.V | #include <glib.h> |
22 | d0e2fce5 | Aneesh Kumar K.V | #include "qemu-common.h" |
23 | 737e150e | Paolo Bonzini | #include "block/coroutine_int.h" |
24 | d0e2fce5 | Aneesh Kumar K.V | |
25 | d0e2fce5 | Aneesh Kumar K.V | typedef struct { |
26 | d0e2fce5 | Aneesh Kumar K.V | Coroutine base; |
27 | d0e2fce5 | Aneesh Kumar K.V | GThread *thread; |
28 | d0e2fce5 | Aneesh Kumar K.V | bool runnable;
|
29 | d1b719e9 | Peter Maydell | bool free_on_thread_exit;
|
30 | d0e2fce5 | Aneesh Kumar K.V | CoroutineAction action; |
31 | d0e2fce5 | Aneesh Kumar K.V | } CoroutineGThread; |
32 | d0e2fce5 | Aneesh Kumar K.V | |
33 | d0e2fce5 | Aneesh Kumar K.V | static GStaticMutex coroutine_lock = G_STATIC_MUTEX_INIT;
|
34 | d1b719e9 | Peter Maydell | |
35 | d1b719e9 | Peter Maydell | /* GLib 2.31 and beyond deprecated various parts of the thread API,
|
36 | d1b719e9 | Peter Maydell | * but the new interfaces are not available in older GLib versions
|
37 | d1b719e9 | Peter Maydell | * so we have to cope with both.
|
38 | d1b719e9 | Peter Maydell | */
|
39 | d1b719e9 | Peter Maydell | #if GLIB_CHECK_VERSION(2, 31, 0) |
40 | d1b719e9 | Peter Maydell | /* Default zero-initialisation is sufficient for 2.31+ GCond */
|
41 | d1b719e9 | Peter Maydell | static GCond the_coroutine_cond;
|
42 | d1b719e9 | Peter Maydell | static GCond *coroutine_cond = &the_coroutine_cond;
|
43 | d1b719e9 | Peter Maydell | static inline void init_coroutine_cond(void) |
44 | d1b719e9 | Peter Maydell | { |
45 | d1b719e9 | Peter Maydell | } |
46 | d1b719e9 | Peter Maydell | |
47 | d1b719e9 | Peter Maydell | /* Awkwardly, the GPrivate API doesn't provide a way to update the
|
48 | d1b719e9 | Peter Maydell | * GDestroyNotify handler for the coroutine key dynamically. So instead
|
49 | d1b719e9 | Peter Maydell | * we track whether or not the CoroutineGThread should be freed on
|
50 | d1b719e9 | Peter Maydell | * thread exit / coroutine key update using the free_on_thread_exit
|
51 | d1b719e9 | Peter Maydell | * field.
|
52 | d1b719e9 | Peter Maydell | */
|
53 | d1b719e9 | Peter Maydell | static void coroutine_destroy_notify(gpointer data) |
54 | d1b719e9 | Peter Maydell | { |
55 | d1b719e9 | Peter Maydell | CoroutineGThread *co = data; |
56 | d1b719e9 | Peter Maydell | if (co && co->free_on_thread_exit) {
|
57 | d1b719e9 | Peter Maydell | g_free(co); |
58 | d1b719e9 | Peter Maydell | } |
59 | d1b719e9 | Peter Maydell | } |
60 | d1b719e9 | Peter Maydell | |
61 | d1b719e9 | Peter Maydell | static GPrivate coroutine_key = G_PRIVATE_INIT(coroutine_destroy_notify);
|
62 | d1b719e9 | Peter Maydell | |
63 | d1b719e9 | Peter Maydell | static inline CoroutineGThread *get_coroutine_key(void) |
64 | d1b719e9 | Peter Maydell | { |
65 | d1b719e9 | Peter Maydell | return g_private_get(&coroutine_key);
|
66 | d1b719e9 | Peter Maydell | } |
67 | d1b719e9 | Peter Maydell | |
68 | d1b719e9 | Peter Maydell | static inline void set_coroutine_key(CoroutineGThread *co, |
69 | d1b719e9 | Peter Maydell | bool free_on_thread_exit)
|
70 | d1b719e9 | Peter Maydell | { |
71 | d1b719e9 | Peter Maydell | /* Unlike g_static_private_set() this does not call the GDestroyNotify
|
72 | d1b719e9 | Peter Maydell | * if the previous value of the key was NULL. Fortunately we only need
|
73 | d1b719e9 | Peter Maydell | * the GDestroyNotify in the non-NULL key case.
|
74 | d1b719e9 | Peter Maydell | */
|
75 | d1b719e9 | Peter Maydell | co->free_on_thread_exit = free_on_thread_exit; |
76 | d1b719e9 | Peter Maydell | g_private_replace(&coroutine_key, co); |
77 | d1b719e9 | Peter Maydell | } |
78 | d1b719e9 | Peter Maydell | |
79 | d1b719e9 | Peter Maydell | static inline GThread *create_thread(GThreadFunc func, gpointer data) |
80 | d1b719e9 | Peter Maydell | { |
81 | d1b719e9 | Peter Maydell | return g_thread_new("coroutine", func, data); |
82 | d1b719e9 | Peter Maydell | } |
83 | d1b719e9 | Peter Maydell | |
84 | d1b719e9 | Peter Maydell | #else
|
85 | d1b719e9 | Peter Maydell | |
86 | d1b719e9 | Peter Maydell | /* Handle older GLib versions */
|
87 | d1b719e9 | Peter Maydell | static GCond *coroutine_cond;
|
88 | d1b719e9 | Peter Maydell | static inline void init_coroutine_cond(void) |
89 | d1b719e9 | Peter Maydell | { |
90 | d1b719e9 | Peter Maydell | coroutine_cond = g_cond_new(); |
91 | d1b719e9 | Peter Maydell | } |
92 | d1b719e9 | Peter Maydell | |
93 | d0e2fce5 | Aneesh Kumar K.V | static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT;
|
94 | d0e2fce5 | Aneesh Kumar K.V | |
95 | d1b719e9 | Peter Maydell | static inline CoroutineGThread *get_coroutine_key(void) |
96 | d1b719e9 | Peter Maydell | { |
97 | d1b719e9 | Peter Maydell | return g_static_private_get(&coroutine_key);
|
98 | d1b719e9 | Peter Maydell | } |
99 | d1b719e9 | Peter Maydell | |
100 | d1b719e9 | Peter Maydell | static inline void set_coroutine_key(CoroutineGThread *co, |
101 | d1b719e9 | Peter Maydell | bool free_on_thread_exit)
|
102 | d1b719e9 | Peter Maydell | { |
103 | d1b719e9 | Peter Maydell | g_static_private_set(&coroutine_key, co, |
104 | d1b719e9 | Peter Maydell | free_on_thread_exit ? (GDestroyNotify)g_free : NULL);
|
105 | d1b719e9 | Peter Maydell | } |
106 | d1b719e9 | Peter Maydell | |
107 | d1b719e9 | Peter Maydell | static inline GThread *create_thread(GThreadFunc func, gpointer data) |
108 | d1b719e9 | Peter Maydell | { |
109 | d1b719e9 | Peter Maydell | return g_thread_create_full(func, data, 0, TRUE, TRUE, |
110 | d1b719e9 | Peter Maydell | G_THREAD_PRIORITY_NORMAL, NULL);
|
111 | d1b719e9 | Peter Maydell | } |
112 | d1b719e9 | Peter Maydell | |
113 | d1b719e9 | Peter Maydell | #endif
|
114 | d1b719e9 | Peter Maydell | |
115 | d1b719e9 | Peter Maydell | |
116 | d0e2fce5 | Aneesh Kumar K.V | static void __attribute__((constructor)) coroutine_init(void) |
117 | d0e2fce5 | Aneesh Kumar K.V | { |
118 | d0e2fce5 | Aneesh Kumar K.V | if (!g_thread_supported()) {
|
119 | 42ed3727 | Alon Levy | #if !GLIB_CHECK_VERSION(2, 31, 0) |
120 | d0e2fce5 | Aneesh Kumar K.V | g_thread_init(NULL);
|
121 | 42ed3727 | Alon Levy | #else
|
122 | 42ed3727 | Alon Levy | fprintf(stderr, "glib threading failed to initialize.\n");
|
123 | 42ed3727 | Alon Levy | exit(1);
|
124 | 42ed3727 | Alon Levy | #endif
|
125 | d0e2fce5 | Aneesh Kumar K.V | } |
126 | d0e2fce5 | Aneesh Kumar K.V | |
127 | d1b719e9 | Peter Maydell | init_coroutine_cond(); |
128 | d0e2fce5 | Aneesh Kumar K.V | } |
129 | d0e2fce5 | Aneesh Kumar K.V | |
130 | d0e2fce5 | Aneesh Kumar K.V | static void coroutine_wait_runnable_locked(CoroutineGThread *co) |
131 | d0e2fce5 | Aneesh Kumar K.V | { |
132 | d0e2fce5 | Aneesh Kumar K.V | while (!co->runnable) {
|
133 | d0e2fce5 | Aneesh Kumar K.V | g_cond_wait(coroutine_cond, g_static_mutex_get_mutex(&coroutine_lock)); |
134 | d0e2fce5 | Aneesh Kumar K.V | } |
135 | d0e2fce5 | Aneesh Kumar K.V | } |
136 | d0e2fce5 | Aneesh Kumar K.V | |
137 | d0e2fce5 | Aneesh Kumar K.V | static void coroutine_wait_runnable(CoroutineGThread *co) |
138 | d0e2fce5 | Aneesh Kumar K.V | { |
139 | d0e2fce5 | Aneesh Kumar K.V | g_static_mutex_lock(&coroutine_lock); |
140 | d0e2fce5 | Aneesh Kumar K.V | coroutine_wait_runnable_locked(co); |
141 | d0e2fce5 | Aneesh Kumar K.V | g_static_mutex_unlock(&coroutine_lock); |
142 | d0e2fce5 | Aneesh Kumar K.V | } |
143 | d0e2fce5 | Aneesh Kumar K.V | |
144 | d0e2fce5 | Aneesh Kumar K.V | static gpointer coroutine_thread(gpointer opaque)
|
145 | d0e2fce5 | Aneesh Kumar K.V | { |
146 | d0e2fce5 | Aneesh Kumar K.V | CoroutineGThread *co = opaque; |
147 | d0e2fce5 | Aneesh Kumar K.V | |
148 | d1b719e9 | Peter Maydell | set_coroutine_key(co, false);
|
149 | d0e2fce5 | Aneesh Kumar K.V | coroutine_wait_runnable(co); |
150 | d0e2fce5 | Aneesh Kumar K.V | co->base.entry(co->base.entry_arg); |
151 | d0e2fce5 | Aneesh Kumar K.V | qemu_coroutine_switch(&co->base, co->base.caller, COROUTINE_TERMINATE); |
152 | d0e2fce5 | Aneesh Kumar K.V | return NULL; |
153 | d0e2fce5 | Aneesh Kumar K.V | } |
154 | d0e2fce5 | Aneesh Kumar K.V | |
155 | d0e2fce5 | Aneesh Kumar K.V | Coroutine *qemu_coroutine_new(void)
|
156 | d0e2fce5 | Aneesh Kumar K.V | { |
157 | d0e2fce5 | Aneesh Kumar K.V | CoroutineGThread *co; |
158 | d0e2fce5 | Aneesh Kumar K.V | |
159 | 7267c094 | Anthony Liguori | co = g_malloc0(sizeof(*co));
|
160 | d1b719e9 | Peter Maydell | co->thread = create_thread(coroutine_thread, co); |
161 | d0e2fce5 | Aneesh Kumar K.V | if (!co->thread) {
|
162 | 7267c094 | Anthony Liguori | g_free(co); |
163 | d0e2fce5 | Aneesh Kumar K.V | return NULL; |
164 | d0e2fce5 | Aneesh Kumar K.V | } |
165 | d0e2fce5 | Aneesh Kumar K.V | return &co->base;
|
166 | d0e2fce5 | Aneesh Kumar K.V | } |
167 | d0e2fce5 | Aneesh Kumar K.V | |
168 | d0e2fce5 | Aneesh Kumar K.V | void qemu_coroutine_delete(Coroutine *co_)
|
169 | d0e2fce5 | Aneesh Kumar K.V | { |
170 | d0e2fce5 | Aneesh Kumar K.V | CoroutineGThread *co = DO_UPCAST(CoroutineGThread, base, co_); |
171 | d0e2fce5 | Aneesh Kumar K.V | |
172 | d0e2fce5 | Aneesh Kumar K.V | g_thread_join(co->thread); |
173 | 7267c094 | Anthony Liguori | g_free(co); |
174 | d0e2fce5 | Aneesh Kumar K.V | } |
175 | d0e2fce5 | Aneesh Kumar K.V | |
176 | d0e2fce5 | Aneesh Kumar K.V | CoroutineAction qemu_coroutine_switch(Coroutine *from_, |
177 | d0e2fce5 | Aneesh Kumar K.V | Coroutine *to_, |
178 | d0e2fce5 | Aneesh Kumar K.V | CoroutineAction action) |
179 | d0e2fce5 | Aneesh Kumar K.V | { |
180 | d0e2fce5 | Aneesh Kumar K.V | CoroutineGThread *from = DO_UPCAST(CoroutineGThread, base, from_); |
181 | d0e2fce5 | Aneesh Kumar K.V | CoroutineGThread *to = DO_UPCAST(CoroutineGThread, base, to_); |
182 | d0e2fce5 | Aneesh Kumar K.V | |
183 | d0e2fce5 | Aneesh Kumar K.V | g_static_mutex_lock(&coroutine_lock); |
184 | d0e2fce5 | Aneesh Kumar K.V | from->runnable = false;
|
185 | d0e2fce5 | Aneesh Kumar K.V | from->action = action; |
186 | d0e2fce5 | Aneesh Kumar K.V | to->runnable = true;
|
187 | d0e2fce5 | Aneesh Kumar K.V | to->action = action; |
188 | d0e2fce5 | Aneesh Kumar K.V | g_cond_broadcast(coroutine_cond); |
189 | d0e2fce5 | Aneesh Kumar K.V | |
190 | d0e2fce5 | Aneesh Kumar K.V | if (action != COROUTINE_TERMINATE) {
|
191 | d0e2fce5 | Aneesh Kumar K.V | coroutine_wait_runnable_locked(from); |
192 | d0e2fce5 | Aneesh Kumar K.V | } |
193 | d0e2fce5 | Aneesh Kumar K.V | g_static_mutex_unlock(&coroutine_lock); |
194 | d0e2fce5 | Aneesh Kumar K.V | return from->action;
|
195 | d0e2fce5 | Aneesh Kumar K.V | } |
196 | d0e2fce5 | Aneesh Kumar K.V | |
197 | d0e2fce5 | Aneesh Kumar K.V | Coroutine *qemu_coroutine_self(void)
|
198 | d0e2fce5 | Aneesh Kumar K.V | { |
199 | d1b719e9 | Peter Maydell | CoroutineGThread *co = get_coroutine_key(); |
200 | d0e2fce5 | Aneesh Kumar K.V | if (!co) {
|
201 | 7267c094 | Anthony Liguori | co = g_malloc0(sizeof(*co));
|
202 | d0e2fce5 | Aneesh Kumar K.V | co->runnable = true;
|
203 | d1b719e9 | Peter Maydell | set_coroutine_key(co, true);
|
204 | d0e2fce5 | Aneesh Kumar K.V | } |
205 | d0e2fce5 | Aneesh Kumar K.V | |
206 | d0e2fce5 | Aneesh Kumar K.V | return &co->base;
|
207 | d0e2fce5 | Aneesh Kumar K.V | } |
208 | d0e2fce5 | Aneesh Kumar K.V | |
209 | d0e2fce5 | Aneesh Kumar K.V | bool qemu_in_coroutine(void) |
210 | d0e2fce5 | Aneesh Kumar K.V | { |
211 | d1b719e9 | Peter Maydell | CoroutineGThread *co = get_coroutine_key(); |
212 | d0e2fce5 | Aneesh Kumar K.V | |
213 | d0e2fce5 | Aneesh Kumar K.V | return co && co->base.caller;
|
214 | d0e2fce5 | Aneesh Kumar K.V | } |