]>
Commit | Line | Data |
---|---|---|
d0e2fce5 AK |
1 | /* |
2 | * GThread coroutine initialization code | |
3 | * | |
4 | * Copyright (C) 2006 Anthony Liguori <[email protected]> | |
5 | * Copyright (C) 2011 Aneesh Kumar K.V <[email protected]> | |
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" | |
737e150e | 23 | #include "block/coroutine_int.h" |
d0e2fce5 AK |
24 | |
25 | typedef struct { | |
26 | Coroutine base; | |
27 | GThread *thread; | |
28 | bool runnable; | |
d1b719e9 | 29 | bool free_on_thread_exit; |
d0e2fce5 AK |
30 | CoroutineAction action; |
31 | } CoroutineGThread; | |
32 | ||
86946a2d MT |
33 | static CompatGMutex coroutine_lock; |
34 | static CompatGCond coroutine_cond; | |
d1b719e9 PM |
35 | |
36 | /* GLib 2.31 and beyond deprecated various parts of the thread API, | |
37 | * but the new interfaces are not available in older GLib versions | |
38 | * so we have to cope with both. | |
39 | */ | |
40 | #if GLIB_CHECK_VERSION(2, 31, 0) | |
d1b719e9 PM |
41 | /* Awkwardly, the GPrivate API doesn't provide a way to update the |
42 | * GDestroyNotify handler for the coroutine key dynamically. So instead | |
43 | * we track whether or not the CoroutineGThread should be freed on | |
44 | * thread exit / coroutine key update using the free_on_thread_exit | |
45 | * field. | |
46 | */ | |
47 | static void coroutine_destroy_notify(gpointer data) | |
48 | { | |
49 | CoroutineGThread *co = data; | |
50 | if (co && co->free_on_thread_exit) { | |
51 | g_free(co); | |
52 | } | |
53 | } | |
54 | ||
55 | static GPrivate coroutine_key = G_PRIVATE_INIT(coroutine_destroy_notify); | |
56 | ||
57 | static inline CoroutineGThread *get_coroutine_key(void) | |
58 | { | |
59 | return g_private_get(&coroutine_key); | |
60 | } | |
61 | ||
62 | static inline void set_coroutine_key(CoroutineGThread *co, | |
63 | bool free_on_thread_exit) | |
64 | { | |
65 | /* Unlike g_static_private_set() this does not call the GDestroyNotify | |
66 | * if the previous value of the key was NULL. Fortunately we only need | |
67 | * the GDestroyNotify in the non-NULL key case. | |
68 | */ | |
69 | co->free_on_thread_exit = free_on_thread_exit; | |
70 | g_private_replace(&coroutine_key, co); | |
71 | } | |
72 | ||
73 | static inline GThread *create_thread(GThreadFunc func, gpointer data) | |
74 | { | |
75 | return g_thread_new("coroutine", func, data); | |
76 | } | |
77 | ||
78 | #else | |
79 | ||
80 | /* Handle older GLib versions */ | |
d1b719e9 | 81 | |
d0e2fce5 AK |
82 | static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT; |
83 | ||
d1b719e9 PM |
84 | static inline CoroutineGThread *get_coroutine_key(void) |
85 | { | |
86 | return g_static_private_get(&coroutine_key); | |
87 | } | |
88 | ||
89 | static inline void set_coroutine_key(CoroutineGThread *co, | |
90 | bool free_on_thread_exit) | |
91 | { | |
92 | g_static_private_set(&coroutine_key, co, | |
93 | free_on_thread_exit ? (GDestroyNotify)g_free : NULL); | |
94 | } | |
95 | ||
96 | static inline GThread *create_thread(GThreadFunc func, gpointer data) | |
97 | { | |
98 | return g_thread_create_full(func, data, 0, TRUE, TRUE, | |
99 | G_THREAD_PRIORITY_NORMAL, NULL); | |
100 | } | |
101 | ||
102 | #endif | |
103 | ||
104 | ||
d0e2fce5 AK |
105 | static void __attribute__((constructor)) coroutine_init(void) |
106 | { | |
42ed3727 | 107 | #if !GLIB_CHECK_VERSION(2, 31, 0) |
f33cc84d | 108 | if (!g_thread_supported()) { |
d0e2fce5 AK |
109 | g_thread_init(NULL); |
110 | } | |
f33cc84d | 111 | #endif |
d0e2fce5 AK |
112 | } |
113 | ||
114 | static void coroutine_wait_runnable_locked(CoroutineGThread *co) | |
115 | { | |
116 | while (!co->runnable) { | |
86946a2d | 117 | g_cond_wait(&coroutine_cond, &coroutine_lock); |
d0e2fce5 AK |
118 | } |
119 | } | |
120 | ||
121 | static void coroutine_wait_runnable(CoroutineGThread *co) | |
122 | { | |
86946a2d | 123 | g_mutex_lock(&coroutine_lock); |
d0e2fce5 | 124 | coroutine_wait_runnable_locked(co); |
86946a2d | 125 | g_mutex_unlock(&coroutine_lock); |
d0e2fce5 AK |
126 | } |
127 | ||
128 | static gpointer coroutine_thread(gpointer opaque) | |
129 | { | |
130 | CoroutineGThread *co = opaque; | |
131 | ||
d1b719e9 | 132 | set_coroutine_key(co, false); |
d0e2fce5 AK |
133 | coroutine_wait_runnable(co); |
134 | co->base.entry(co->base.entry_arg); | |
135 | qemu_coroutine_switch(&co->base, co->base.caller, COROUTINE_TERMINATE); | |
136 | return NULL; | |
137 | } | |
138 | ||
139 | Coroutine *qemu_coroutine_new(void) | |
140 | { | |
141 | CoroutineGThread *co; | |
142 | ||
7267c094 | 143 | co = g_malloc0(sizeof(*co)); |
d1b719e9 | 144 | co->thread = create_thread(coroutine_thread, co); |
d0e2fce5 | 145 | if (!co->thread) { |
7267c094 | 146 | g_free(co); |
d0e2fce5 AK |
147 | return NULL; |
148 | } | |
149 | return &co->base; | |
150 | } | |
151 | ||
152 | void qemu_coroutine_delete(Coroutine *co_) | |
153 | { | |
154 | CoroutineGThread *co = DO_UPCAST(CoroutineGThread, base, co_); | |
155 | ||
156 | g_thread_join(co->thread); | |
7267c094 | 157 | g_free(co); |
d0e2fce5 AK |
158 | } |
159 | ||
160 | CoroutineAction qemu_coroutine_switch(Coroutine *from_, | |
161 | Coroutine *to_, | |
162 | CoroutineAction action) | |
163 | { | |
164 | CoroutineGThread *from = DO_UPCAST(CoroutineGThread, base, from_); | |
165 | CoroutineGThread *to = DO_UPCAST(CoroutineGThread, base, to_); | |
166 | ||
86946a2d | 167 | g_mutex_lock(&coroutine_lock); |
d0e2fce5 AK |
168 | from->runnable = false; |
169 | from->action = action; | |
170 | to->runnable = true; | |
171 | to->action = action; | |
86946a2d | 172 | g_cond_broadcast(&coroutine_cond); |
d0e2fce5 AK |
173 | |
174 | if (action != COROUTINE_TERMINATE) { | |
175 | coroutine_wait_runnable_locked(from); | |
176 | } | |
86946a2d | 177 | g_mutex_unlock(&coroutine_lock); |
d0e2fce5 AK |
178 | return from->action; |
179 | } | |
180 | ||
181 | Coroutine *qemu_coroutine_self(void) | |
182 | { | |
d1b719e9 | 183 | CoroutineGThread *co = get_coroutine_key(); |
d0e2fce5 | 184 | if (!co) { |
7267c094 | 185 | co = g_malloc0(sizeof(*co)); |
d0e2fce5 | 186 | co->runnable = true; |
d1b719e9 | 187 | set_coroutine_key(co, true); |
d0e2fce5 AK |
188 | } |
189 | ||
190 | return &co->base; | |
191 | } | |
192 | ||
193 | bool qemu_in_coroutine(void) | |
194 | { | |
d1b719e9 | 195 | CoroutineGThread *co = get_coroutine_key(); |
d0e2fce5 AK |
196 | |
197 | return co && co->base.caller; | |
198 | } |