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