]> rtime.felk.cvut.cz Git - lisovros/qemu_apohw.git/blob - coroutine-gthread.c
apohw: initial keyboard implementation.
[lisovros/qemu_apohw.git] / coroutine-gthread.c
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 "qemu-coroutine-int.h"
24
25 typedef struct {
26     Coroutine base;
27     GThread *thread;
28     bool runnable;
29     CoroutineAction action;
30 } CoroutineGThread;
31
32 static GCond *coroutine_cond;
33 static GStaticMutex coroutine_lock = G_STATIC_MUTEX_INIT;
34 static GStaticPrivate coroutine_key = G_STATIC_PRIVATE_INIT;
35
36 static void __attribute__((constructor)) coroutine_init(void)
37 {
38     if (!g_thread_supported()) {
39 #if !GLIB_CHECK_VERSION(2, 31, 0)
40         g_thread_init(NULL);
41 #else
42         fprintf(stderr, "glib threading failed to initialize.\n");
43         exit(1);
44 #endif
45     }
46
47     coroutine_cond = g_cond_new();
48 }
49
50 static void coroutine_wait_runnable_locked(CoroutineGThread *co)
51 {
52     while (!co->runnable) {
53         g_cond_wait(coroutine_cond, g_static_mutex_get_mutex(&coroutine_lock));
54     }
55 }
56
57 static void coroutine_wait_runnable(CoroutineGThread *co)
58 {
59     g_static_mutex_lock(&coroutine_lock);
60     coroutine_wait_runnable_locked(co);
61     g_static_mutex_unlock(&coroutine_lock);
62 }
63
64 static gpointer coroutine_thread(gpointer opaque)
65 {
66     CoroutineGThread *co = opaque;
67
68     g_static_private_set(&coroutine_key, co, NULL);
69     coroutine_wait_runnable(co);
70     co->base.entry(co->base.entry_arg);
71     qemu_coroutine_switch(&co->base, co->base.caller, COROUTINE_TERMINATE);
72     return NULL;
73 }
74
75 Coroutine *qemu_coroutine_new(void)
76 {
77     CoroutineGThread *co;
78
79     co = g_malloc0(sizeof(*co));
80     co->thread = g_thread_create_full(coroutine_thread, co, 0, TRUE, TRUE,
81                                       G_THREAD_PRIORITY_NORMAL, NULL);
82     if (!co->thread) {
83         g_free(co);
84         return NULL;
85     }
86     return &co->base;
87 }
88
89 void qemu_coroutine_delete(Coroutine *co_)
90 {
91     CoroutineGThread *co = DO_UPCAST(CoroutineGThread, base, co_);
92
93     g_thread_join(co->thread);
94     g_free(co);
95 }
96
97 CoroutineAction qemu_coroutine_switch(Coroutine *from_,
98                                       Coroutine *to_,
99                                       CoroutineAction action)
100 {
101     CoroutineGThread *from = DO_UPCAST(CoroutineGThread, base, from_);
102     CoroutineGThread *to = DO_UPCAST(CoroutineGThread, base, to_);
103
104     g_static_mutex_lock(&coroutine_lock);
105     from->runnable = false;
106     from->action = action;
107     to->runnable = true;
108     to->action = action;
109     g_cond_broadcast(coroutine_cond);
110
111     if (action != COROUTINE_TERMINATE) {
112         coroutine_wait_runnable_locked(from);
113     }
114     g_static_mutex_unlock(&coroutine_lock);
115     return from->action;
116 }
117
118 Coroutine *qemu_coroutine_self(void)
119 {
120     CoroutineGThread *co = g_static_private_get(&coroutine_key);
121
122     if (!co) {
123         co = g_malloc0(sizeof(*co));
124         co->runnable = true;
125         g_static_private_set(&coroutine_key, co, (GDestroyNotify)g_free);
126     }
127
128     return &co->base;
129 }
130
131 bool qemu_in_coroutine(void)
132 {
133     CoroutineGThread *co = g_static_private_get(&coroutine_key);
134
135     return co && co->base.caller;
136 }