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