root / qemu-thread-win32.c @ d6e58090
History | View | Annotate | Download (7.6 kB)
1 | 9257d46d | Paolo Bonzini | /*
|
---|---|---|---|
2 | 9257d46d | Paolo Bonzini | * Win32 implementation for mutex/cond/thread functions
|
3 | 9257d46d | Paolo Bonzini | *
|
4 | 9257d46d | Paolo Bonzini | * Copyright Red Hat, Inc. 2010
|
5 | 9257d46d | Paolo Bonzini | *
|
6 | 9257d46d | Paolo Bonzini | * Author:
|
7 | 9257d46d | Paolo Bonzini | * Paolo Bonzini <pbonzini@redhat.com>
|
8 | 9257d46d | Paolo Bonzini | *
|
9 | 9257d46d | Paolo Bonzini | * This work is licensed under the terms of the GNU GPL, version 2 or later.
|
10 | 9257d46d | Paolo Bonzini | * See the COPYING file in the top-level directory.
|
11 | 9257d46d | Paolo Bonzini | *
|
12 | 9257d46d | Paolo Bonzini | */
|
13 | 9257d46d | Paolo Bonzini | #include "qemu-common.h" |
14 | 9257d46d | Paolo Bonzini | #include "qemu-thread.h" |
15 | 9257d46d | Paolo Bonzini | #include <process.h> |
16 | 9257d46d | Paolo Bonzini | #include <assert.h> |
17 | 9257d46d | Paolo Bonzini | #include <limits.h> |
18 | 9257d46d | Paolo Bonzini | |
19 | 9257d46d | Paolo Bonzini | static void error_exit(int err, const char *msg) |
20 | 9257d46d | Paolo Bonzini | { |
21 | 9257d46d | Paolo Bonzini | char *pstr;
|
22 | 9257d46d | Paolo Bonzini | |
23 | 9257d46d | Paolo Bonzini | FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, |
24 | 9257d46d | Paolo Bonzini | NULL, err, 0, (LPTSTR)&pstr, 2, NULL); |
25 | 9257d46d | Paolo Bonzini | fprintf(stderr, "qemu: %s: %s\n", msg, pstr);
|
26 | 9257d46d | Paolo Bonzini | LocalFree(pstr); |
27 | 9257d46d | Paolo Bonzini | exit(1);
|
28 | 9257d46d | Paolo Bonzini | } |
29 | 9257d46d | Paolo Bonzini | |
30 | 9257d46d | Paolo Bonzini | void qemu_mutex_init(QemuMutex *mutex)
|
31 | 9257d46d | Paolo Bonzini | { |
32 | 9257d46d | Paolo Bonzini | mutex->owner = 0;
|
33 | 9257d46d | Paolo Bonzini | InitializeCriticalSection(&mutex->lock); |
34 | 9257d46d | Paolo Bonzini | } |
35 | 9257d46d | Paolo Bonzini | |
36 | 1a290aea | Stefan Weil | void qemu_mutex_destroy(QemuMutex *mutex)
|
37 | 1a290aea | Stefan Weil | { |
38 | 1a290aea | Stefan Weil | assert(mutex->owner == 0);
|
39 | 1a290aea | Stefan Weil | DeleteCriticalSection(&mutex->lock); |
40 | 1a290aea | Stefan Weil | } |
41 | 1a290aea | Stefan Weil | |
42 | 9257d46d | Paolo Bonzini | void qemu_mutex_lock(QemuMutex *mutex)
|
43 | 9257d46d | Paolo Bonzini | { |
44 | 9257d46d | Paolo Bonzini | EnterCriticalSection(&mutex->lock); |
45 | 9257d46d | Paolo Bonzini | |
46 | 9257d46d | Paolo Bonzini | /* Win32 CRITICAL_SECTIONs are recursive. Assert that we're not
|
47 | 9257d46d | Paolo Bonzini | * using them as such.
|
48 | 9257d46d | Paolo Bonzini | */
|
49 | 9257d46d | Paolo Bonzini | assert(mutex->owner == 0);
|
50 | 9257d46d | Paolo Bonzini | mutex->owner = GetCurrentThreadId(); |
51 | 9257d46d | Paolo Bonzini | } |
52 | 9257d46d | Paolo Bonzini | |
53 | 9257d46d | Paolo Bonzini | int qemu_mutex_trylock(QemuMutex *mutex)
|
54 | 9257d46d | Paolo Bonzini | { |
55 | 9257d46d | Paolo Bonzini | int owned;
|
56 | 9257d46d | Paolo Bonzini | |
57 | 9257d46d | Paolo Bonzini | owned = TryEnterCriticalSection(&mutex->lock); |
58 | 9257d46d | Paolo Bonzini | if (owned) {
|
59 | 9257d46d | Paolo Bonzini | assert(mutex->owner == 0);
|
60 | 9257d46d | Paolo Bonzini | mutex->owner = GetCurrentThreadId(); |
61 | 9257d46d | Paolo Bonzini | } |
62 | 9257d46d | Paolo Bonzini | return !owned;
|
63 | 9257d46d | Paolo Bonzini | } |
64 | 9257d46d | Paolo Bonzini | |
65 | 9257d46d | Paolo Bonzini | void qemu_mutex_unlock(QemuMutex *mutex)
|
66 | 9257d46d | Paolo Bonzini | { |
67 | 9257d46d | Paolo Bonzini | assert(mutex->owner == GetCurrentThreadId()); |
68 | 9257d46d | Paolo Bonzini | mutex->owner = 0;
|
69 | 9257d46d | Paolo Bonzini | LeaveCriticalSection(&mutex->lock); |
70 | 9257d46d | Paolo Bonzini | } |
71 | 9257d46d | Paolo Bonzini | |
72 | 9257d46d | Paolo Bonzini | void qemu_cond_init(QemuCond *cond)
|
73 | 9257d46d | Paolo Bonzini | { |
74 | 9257d46d | Paolo Bonzini | memset(cond, 0, sizeof(*cond)); |
75 | 9257d46d | Paolo Bonzini | |
76 | 9257d46d | Paolo Bonzini | cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL); |
77 | 9257d46d | Paolo Bonzini | if (!cond->sema) {
|
78 | 9257d46d | Paolo Bonzini | error_exit(GetLastError(), __func__); |
79 | 9257d46d | Paolo Bonzini | } |
80 | 9257d46d | Paolo Bonzini | cond->continue_event = CreateEvent(NULL, /* security */ |
81 | 9257d46d | Paolo Bonzini | FALSE, /* auto-reset */
|
82 | 9257d46d | Paolo Bonzini | FALSE, /* not signaled */
|
83 | 9257d46d | Paolo Bonzini | NULL); /* name */ |
84 | 9257d46d | Paolo Bonzini | if (!cond->continue_event) {
|
85 | 9257d46d | Paolo Bonzini | error_exit(GetLastError(), __func__); |
86 | 9257d46d | Paolo Bonzini | } |
87 | 9257d46d | Paolo Bonzini | } |
88 | 9257d46d | Paolo Bonzini | |
89 | 1a290aea | Stefan Weil | void qemu_cond_destroy(QemuCond *cond)
|
90 | 1a290aea | Stefan Weil | { |
91 | 1a290aea | Stefan Weil | BOOL result; |
92 | 1a290aea | Stefan Weil | result = CloseHandle(cond->continue_event); |
93 | 1a290aea | Stefan Weil | if (!result) {
|
94 | 1a290aea | Stefan Weil | error_exit(GetLastError(), __func__); |
95 | 1a290aea | Stefan Weil | } |
96 | 1a290aea | Stefan Weil | cond->continue_event = 0;
|
97 | 1a290aea | Stefan Weil | result = CloseHandle(cond->sema); |
98 | 1a290aea | Stefan Weil | if (!result) {
|
99 | 1a290aea | Stefan Weil | error_exit(GetLastError(), __func__); |
100 | 1a290aea | Stefan Weil | } |
101 | 1a290aea | Stefan Weil | cond->sema = 0;
|
102 | 1a290aea | Stefan Weil | } |
103 | 1a290aea | Stefan Weil | |
104 | 9257d46d | Paolo Bonzini | void qemu_cond_signal(QemuCond *cond)
|
105 | 9257d46d | Paolo Bonzini | { |
106 | 9257d46d | Paolo Bonzini | DWORD result; |
107 | 9257d46d | Paolo Bonzini | |
108 | 9257d46d | Paolo Bonzini | /*
|
109 | 9257d46d | Paolo Bonzini | * Signal only when there are waiters. cond->waiters is
|
110 | 9257d46d | Paolo Bonzini | * incremented by pthread_cond_wait under the external lock,
|
111 | 9257d46d | Paolo Bonzini | * so we are safe about that.
|
112 | 9257d46d | Paolo Bonzini | */
|
113 | 9257d46d | Paolo Bonzini | if (cond->waiters == 0) { |
114 | 9257d46d | Paolo Bonzini | return;
|
115 | 9257d46d | Paolo Bonzini | } |
116 | 9257d46d | Paolo Bonzini | |
117 | 9257d46d | Paolo Bonzini | /*
|
118 | 9257d46d | Paolo Bonzini | * Waiting threads decrement it outside the external lock, but
|
119 | 9257d46d | Paolo Bonzini | * only if another thread is executing pthread_cond_broadcast and
|
120 | 9257d46d | Paolo Bonzini | * has the mutex. So, it also cannot be decremented concurrently
|
121 | 9257d46d | Paolo Bonzini | * with this particular access.
|
122 | 9257d46d | Paolo Bonzini | */
|
123 | 9257d46d | Paolo Bonzini | cond->target = cond->waiters - 1;
|
124 | 9257d46d | Paolo Bonzini | result = SignalObjectAndWait(cond->sema, cond->continue_event, |
125 | 9257d46d | Paolo Bonzini | INFINITE, FALSE); |
126 | 9257d46d | Paolo Bonzini | if (result == WAIT_ABANDONED || result == WAIT_FAILED) {
|
127 | 9257d46d | Paolo Bonzini | error_exit(GetLastError(), __func__); |
128 | 9257d46d | Paolo Bonzini | } |
129 | 9257d46d | Paolo Bonzini | } |
130 | 9257d46d | Paolo Bonzini | |
131 | 9257d46d | Paolo Bonzini | void qemu_cond_broadcast(QemuCond *cond)
|
132 | 9257d46d | Paolo Bonzini | { |
133 | 9257d46d | Paolo Bonzini | BOOLEAN result; |
134 | 9257d46d | Paolo Bonzini | /*
|
135 | 9257d46d | Paolo Bonzini | * As in pthread_cond_signal, access to cond->waiters and
|
136 | 9257d46d | Paolo Bonzini | * cond->target is locked via the external mutex.
|
137 | 9257d46d | Paolo Bonzini | */
|
138 | 9257d46d | Paolo Bonzini | if (cond->waiters == 0) { |
139 | 9257d46d | Paolo Bonzini | return;
|
140 | 9257d46d | Paolo Bonzini | } |
141 | 9257d46d | Paolo Bonzini | |
142 | 9257d46d | Paolo Bonzini | cond->target = 0;
|
143 | 9257d46d | Paolo Bonzini | result = ReleaseSemaphore(cond->sema, cond->waiters, NULL);
|
144 | 9257d46d | Paolo Bonzini | if (!result) {
|
145 | 9257d46d | Paolo Bonzini | error_exit(GetLastError(), __func__); |
146 | 9257d46d | Paolo Bonzini | } |
147 | 9257d46d | Paolo Bonzini | |
148 | 9257d46d | Paolo Bonzini | /*
|
149 | 9257d46d | Paolo Bonzini | * At this point all waiters continue. Each one takes its
|
150 | 9257d46d | Paolo Bonzini | * slice of the semaphore. Now it's our turn to wait: Since
|
151 | 9257d46d | Paolo Bonzini | * the external mutex is held, no thread can leave cond_wait,
|
152 | 9257d46d | Paolo Bonzini | * yet. For this reason, we can be sure that no thread gets
|
153 | 9257d46d | Paolo Bonzini | * a chance to eat *more* than one slice. OTOH, it means
|
154 | 9257d46d | Paolo Bonzini | * that the last waiter must send us a wake-up.
|
155 | 9257d46d | Paolo Bonzini | */
|
156 | 9257d46d | Paolo Bonzini | WaitForSingleObject(cond->continue_event, INFINITE); |
157 | 9257d46d | Paolo Bonzini | } |
158 | 9257d46d | Paolo Bonzini | |
159 | 9257d46d | Paolo Bonzini | void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex)
|
160 | 9257d46d | Paolo Bonzini | { |
161 | 9257d46d | Paolo Bonzini | /*
|
162 | 9257d46d | Paolo Bonzini | * This access is protected under the mutex.
|
163 | 9257d46d | Paolo Bonzini | */
|
164 | 9257d46d | Paolo Bonzini | cond->waiters++; |
165 | 9257d46d | Paolo Bonzini | |
166 | 9257d46d | Paolo Bonzini | /*
|
167 | 9257d46d | Paolo Bonzini | * Unlock external mutex and wait for signal.
|
168 | 9257d46d | Paolo Bonzini | * NOTE: we've held mutex locked long enough to increment
|
169 | 9257d46d | Paolo Bonzini | * waiters count above, so there's no problem with
|
170 | 9257d46d | Paolo Bonzini | * leaving mutex unlocked before we wait on semaphore.
|
171 | 9257d46d | Paolo Bonzini | */
|
172 | 9257d46d | Paolo Bonzini | qemu_mutex_unlock(mutex); |
173 | 9257d46d | Paolo Bonzini | WaitForSingleObject(cond->sema, INFINITE); |
174 | 9257d46d | Paolo Bonzini | |
175 | 9257d46d | Paolo Bonzini | /* Now waiters must rendez-vous with the signaling thread and
|
176 | 9257d46d | Paolo Bonzini | * let it continue. For cond_broadcast this has heavy contention
|
177 | 9257d46d | Paolo Bonzini | * and triggers thundering herd. So goes life.
|
178 | 9257d46d | Paolo Bonzini | *
|
179 | 9257d46d | Paolo Bonzini | * Decrease waiters count. The mutex is not taken, so we have
|
180 | 9257d46d | Paolo Bonzini | * to do this atomically.
|
181 | 9257d46d | Paolo Bonzini | *
|
182 | 9257d46d | Paolo Bonzini | * All waiters contend for the mutex at the end of this function
|
183 | 9257d46d | Paolo Bonzini | * until the signaling thread relinquishes it. To ensure
|
184 | 9257d46d | Paolo Bonzini | * each waiter consumes exactly one slice of the semaphore,
|
185 | 9257d46d | Paolo Bonzini | * the signaling thread stops until it is told by the last
|
186 | 9257d46d | Paolo Bonzini | * waiter that it can go on.
|
187 | 9257d46d | Paolo Bonzini | */
|
188 | 9257d46d | Paolo Bonzini | if (InterlockedDecrement(&cond->waiters) == cond->target) {
|
189 | 9257d46d | Paolo Bonzini | SetEvent(cond->continue_event); |
190 | 9257d46d | Paolo Bonzini | } |
191 | 9257d46d | Paolo Bonzini | |
192 | 9257d46d | Paolo Bonzini | qemu_mutex_lock(mutex); |
193 | 9257d46d | Paolo Bonzini | } |
194 | 9257d46d | Paolo Bonzini | |
195 | 9257d46d | Paolo Bonzini | struct QemuThreadData {
|
196 | 9257d46d | Paolo Bonzini | QemuThread *thread; |
197 | 9257d46d | Paolo Bonzini | void *(*start_routine)(void *); |
198 | 9257d46d | Paolo Bonzini | void *arg;
|
199 | 9257d46d | Paolo Bonzini | }; |
200 | 9257d46d | Paolo Bonzini | |
201 | 9257d46d | Paolo Bonzini | static int qemu_thread_tls_index = TLS_OUT_OF_INDEXES; |
202 | 9257d46d | Paolo Bonzini | |
203 | 9257d46d | Paolo Bonzini | static unsigned __stdcall win32_start_routine(void *arg) |
204 | 9257d46d | Paolo Bonzini | { |
205 | 9257d46d | Paolo Bonzini | struct QemuThreadData data = *(struct QemuThreadData *) arg; |
206 | 9257d46d | Paolo Bonzini | QemuThread *thread = data.thread; |
207 | 9257d46d | Paolo Bonzini | |
208 | 9257d46d | Paolo Bonzini | free(arg); |
209 | 9257d46d | Paolo Bonzini | TlsSetValue(qemu_thread_tls_index, thread); |
210 | 9257d46d | Paolo Bonzini | |
211 | 9257d46d | Paolo Bonzini | /*
|
212 | 9257d46d | Paolo Bonzini | * Use DuplicateHandle instead of assigning thread->thread in the
|
213 | 9257d46d | Paolo Bonzini | * creating thread to avoid races. It's simpler this way than with
|
214 | 9257d46d | Paolo Bonzini | * synchronization.
|
215 | 9257d46d | Paolo Bonzini | */
|
216 | 9257d46d | Paolo Bonzini | DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), |
217 | 9257d46d | Paolo Bonzini | GetCurrentProcess(), &thread->thread, |
218 | 9257d46d | Paolo Bonzini | 0, FALSE, DUPLICATE_SAME_ACCESS);
|
219 | 9257d46d | Paolo Bonzini | |
220 | 9257d46d | Paolo Bonzini | qemu_thread_exit(data.start_routine(data.arg)); |
221 | 9257d46d | Paolo Bonzini | abort(); |
222 | 9257d46d | Paolo Bonzini | } |
223 | 9257d46d | Paolo Bonzini | |
224 | 9257d46d | Paolo Bonzini | void qemu_thread_exit(void *arg) |
225 | 9257d46d | Paolo Bonzini | { |
226 | 9257d46d | Paolo Bonzini | QemuThread *thread = TlsGetValue(qemu_thread_tls_index); |
227 | 9257d46d | Paolo Bonzini | thread->ret = arg; |
228 | 9257d46d | Paolo Bonzini | CloseHandle(thread->thread); |
229 | 9257d46d | Paolo Bonzini | thread->thread = NULL;
|
230 | 9257d46d | Paolo Bonzini | ExitThread(0);
|
231 | 9257d46d | Paolo Bonzini | } |
232 | 9257d46d | Paolo Bonzini | |
233 | 9257d46d | Paolo Bonzini | static inline void qemu_thread_init(void) |
234 | 9257d46d | Paolo Bonzini | { |
235 | 9257d46d | Paolo Bonzini | if (qemu_thread_tls_index == TLS_OUT_OF_INDEXES) {
|
236 | 9257d46d | Paolo Bonzini | qemu_thread_tls_index = TlsAlloc(); |
237 | 9257d46d | Paolo Bonzini | if (qemu_thread_tls_index == TLS_OUT_OF_INDEXES) {
|
238 | 9257d46d | Paolo Bonzini | error_exit(ERROR_NO_SYSTEM_RESOURCES, __func__); |
239 | 9257d46d | Paolo Bonzini | } |
240 | 9257d46d | Paolo Bonzini | } |
241 | 9257d46d | Paolo Bonzini | } |
242 | 9257d46d | Paolo Bonzini | |
243 | 9257d46d | Paolo Bonzini | |
244 | 9257d46d | Paolo Bonzini | void qemu_thread_create(QemuThread *thread,
|
245 | 9257d46d | Paolo Bonzini | void *(*start_routine)(void *), |
246 | 9257d46d | Paolo Bonzini | void *arg)
|
247 | 9257d46d | Paolo Bonzini | { |
248 | 9257d46d | Paolo Bonzini | HANDLE hThread; |
249 | 9257d46d | Paolo Bonzini | |
250 | 9257d46d | Paolo Bonzini | struct QemuThreadData *data;
|
251 | 9257d46d | Paolo Bonzini | qemu_thread_init(); |
252 | 9257d46d | Paolo Bonzini | data = qemu_malloc(sizeof *data);
|
253 | 9257d46d | Paolo Bonzini | data->thread = thread; |
254 | 9257d46d | Paolo Bonzini | data->start_routine = start_routine; |
255 | 9257d46d | Paolo Bonzini | data->arg = arg; |
256 | 9257d46d | Paolo Bonzini | |
257 | 9257d46d | Paolo Bonzini | hThread = (HANDLE) _beginthreadex(NULL, 0, win32_start_routine, |
258 | 9257d46d | Paolo Bonzini | data, 0, NULL); |
259 | 9257d46d | Paolo Bonzini | if (!hThread) {
|
260 | 9257d46d | Paolo Bonzini | error_exit(GetLastError(), __func__); |
261 | 9257d46d | Paolo Bonzini | } |
262 | 9257d46d | Paolo Bonzini | CloseHandle(hThread); |
263 | 9257d46d | Paolo Bonzini | } |
264 | 9257d46d | Paolo Bonzini | |
265 | 9257d46d | Paolo Bonzini | void qemu_thread_get_self(QemuThread *thread)
|
266 | 9257d46d | Paolo Bonzini | { |
267 | 9257d46d | Paolo Bonzini | if (!thread->thread) {
|
268 | 9257d46d | Paolo Bonzini | /* In the main thread of the process. Initialize the QemuThread
|
269 | 9257d46d | Paolo Bonzini | pointer in TLS, and use the dummy GetCurrentThread handle as
|
270 | 9257d46d | Paolo Bonzini | the identifier for qemu_thread_is_self. */
|
271 | 9257d46d | Paolo Bonzini | qemu_thread_init(); |
272 | 9257d46d | Paolo Bonzini | TlsSetValue(qemu_thread_tls_index, thread); |
273 | 9257d46d | Paolo Bonzini | thread->thread = GetCurrentThread(); |
274 | 9257d46d | Paolo Bonzini | } |
275 | 9257d46d | Paolo Bonzini | } |
276 | 9257d46d | Paolo Bonzini | |
277 | 9257d46d | Paolo Bonzini | int qemu_thread_is_self(QemuThread *thread)
|
278 | 9257d46d | Paolo Bonzini | { |
279 | 9257d46d | Paolo Bonzini | QemuThread *this_thread = TlsGetValue(qemu_thread_tls_index); |
280 | 9257d46d | Paolo Bonzini | return this_thread->thread == thread->thread;
|
281 | 9257d46d | Paolo Bonzini | } |