]> git.ipfire.org Git - thirdparty/qemu.git/blame - util/qemu-thread-posix.c
qemu-thread: fix qemu_event without futexes
[thirdparty/qemu.git] / util / qemu-thread-posix.c
CommitLineData
e5d355d1
AL
1/*
2 * Wrappers around mutex/cond/thread functions
3 *
4 * Copyright Red Hat, Inc. 2009
5 *
6 * Author:
7 * Marcelo Tosatti <mtosatti@redhat.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2 or later.
10 * See the COPYING file in the top-level directory.
11 *
12 */
13#include <stdlib.h>
14#include <stdio.h>
15#include <errno.h>
16#include <time.h>
17#include <signal.h>
18#include <stdint.h>
19#include <string.h>
38b14db3
PB
20#include <limits.h>
21#include <unistd.h>
22#include <sys/time.h>
c7c4d063
PB
23#ifdef __linux__
24#include <sys/syscall.h>
25#include <linux/futex.h>
26#endif
1de7afc9 27#include "qemu/thread.h"
c7c4d063 28#include "qemu/atomic.h"
e5d355d1 29
8f480de0
DDAG
30static bool name_threads;
31
32void qemu_thread_naming(bool enable)
33{
34 name_threads = enable;
5c312079
DDAG
35
36#ifndef CONFIG_THREAD_SETNAME_BYTHREAD
37 /* This is a debugging option, not fatal */
38 if (enable) {
39 fprintf(stderr, "qemu: thread naming not supported on this host\n");
40 }
41#endif
8f480de0
DDAG
42}
43
e5d355d1
AL
44static void error_exit(int err, const char *msg)
45{
46 fprintf(stderr, "qemu: %s: %s\n", msg, strerror(err));
53380ac3 47 abort();
e5d355d1
AL
48}
49
50void qemu_mutex_init(QemuMutex *mutex)
51{
52 int err;
89b48b56 53 pthread_mutexattr_t mutexattr;
e5d355d1 54
89b48b56
PB
55 pthread_mutexattr_init(&mutexattr);
56 pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_ERRORCHECK);
57 err = pthread_mutex_init(&mutex->lock, &mutexattr);
58 pthread_mutexattr_destroy(&mutexattr);
e5d355d1
AL
59 if (err)
60 error_exit(err, __func__);
61}
62
313b1d69
CC
63void qemu_mutex_destroy(QemuMutex *mutex)
64{
65 int err;
66
67 err = pthread_mutex_destroy(&mutex->lock);
68 if (err)
69 error_exit(err, __func__);
70}
71
e5d355d1
AL
72void qemu_mutex_lock(QemuMutex *mutex)
73{
74 int err;
75
76 err = pthread_mutex_lock(&mutex->lock);
77 if (err)
78 error_exit(err, __func__);
79}
80
81int qemu_mutex_trylock(QemuMutex *mutex)
82{
83 return pthread_mutex_trylock(&mutex->lock);
84}
85
e5d355d1
AL
86void qemu_mutex_unlock(QemuMutex *mutex)
87{
88 int err;
89
90 err = pthread_mutex_unlock(&mutex->lock);
91 if (err)
92 error_exit(err, __func__);
93}
94
95void qemu_cond_init(QemuCond *cond)
96{
97 int err;
98
99 err = pthread_cond_init(&cond->cond, NULL);
100 if (err)
101 error_exit(err, __func__);
102}
103
313b1d69
CC
104void qemu_cond_destroy(QemuCond *cond)
105{
106 int err;
107
108 err = pthread_cond_destroy(&cond->cond);
109 if (err)
110 error_exit(err, __func__);
111}
112
e5d355d1
AL
113void qemu_cond_signal(QemuCond *cond)
114{
115 int err;
116
117 err = pthread_cond_signal(&cond->cond);
118 if (err)
119 error_exit(err, __func__);
120}
121
122void qemu_cond_broadcast(QemuCond *cond)
123{
124 int err;
125
126 err = pthread_cond_broadcast(&cond->cond);
127 if (err)
128 error_exit(err, __func__);
129}
130
131void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex)
132{
133 int err;
134
135 err = pthread_cond_wait(&cond->cond, &mutex->lock);
136 if (err)
137 error_exit(err, __func__);
138}
139
38b14db3
PB
140void qemu_sem_init(QemuSemaphore *sem, int init)
141{
142 int rc;
143
927fa909 144#if defined(__APPLE__) || defined(__NetBSD__)
c166cb72
PB
145 rc = pthread_mutex_init(&sem->lock, NULL);
146 if (rc != 0) {
147 error_exit(rc, __func__);
148 }
149 rc = pthread_cond_init(&sem->cond, NULL);
150 if (rc != 0) {
151 error_exit(rc, __func__);
152 }
153 if (init < 0) {
154 error_exit(EINVAL, __func__);
155 }
156 sem->count = init;
157#else
38b14db3
PB
158 rc = sem_init(&sem->sem, 0, init);
159 if (rc < 0) {
160 error_exit(errno, __func__);
161 }
c166cb72 162#endif
38b14db3
PB
163}
164
165void qemu_sem_destroy(QemuSemaphore *sem)
166{
167 int rc;
168
927fa909 169#if defined(__APPLE__) || defined(__NetBSD__)
c166cb72
PB
170 rc = pthread_cond_destroy(&sem->cond);
171 if (rc < 0) {
172 error_exit(rc, __func__);
173 }
174 rc = pthread_mutex_destroy(&sem->lock);
175 if (rc < 0) {
176 error_exit(rc, __func__);
177 }
178#else
38b14db3
PB
179 rc = sem_destroy(&sem->sem);
180 if (rc < 0) {
181 error_exit(errno, __func__);
182 }
c166cb72 183#endif
38b14db3
PB
184}
185
186void qemu_sem_post(QemuSemaphore *sem)
187{
188 int rc;
189
927fa909 190#if defined(__APPLE__) || defined(__NetBSD__)
c166cb72 191 pthread_mutex_lock(&sem->lock);
79761c66 192 if (sem->count == UINT_MAX) {
c166cb72 193 rc = EINVAL;
c166cb72 194 } else {
79761c66
IT
195 sem->count++;
196 rc = pthread_cond_signal(&sem->cond);
c166cb72
PB
197 }
198 pthread_mutex_unlock(&sem->lock);
199 if (rc != 0) {
200 error_exit(rc, __func__);
201 }
202#else
38b14db3
PB
203 rc = sem_post(&sem->sem);
204 if (rc < 0) {
205 error_exit(errno, __func__);
206 }
c166cb72
PB
207#endif
208}
209
210static void compute_abs_deadline(struct timespec *ts, int ms)
211{
212 struct timeval tv;
213 gettimeofday(&tv, NULL);
214 ts->tv_nsec = tv.tv_usec * 1000 + (ms % 1000) * 1000000;
215 ts->tv_sec = tv.tv_sec + ms / 1000;
216 if (ts->tv_nsec >= 1000000000) {
217 ts->tv_sec++;
218 ts->tv_nsec -= 1000000000;
219 }
38b14db3
PB
220}
221
222int qemu_sem_timedwait(QemuSemaphore *sem, int ms)
223{
224 int rc;
c166cb72
PB
225 struct timespec ts;
226
927fa909 227#if defined(__APPLE__) || defined(__NetBSD__)
79761c66 228 rc = 0;
c166cb72
PB
229 compute_abs_deadline(&ts, ms);
230 pthread_mutex_lock(&sem->lock);
79761c66 231 while (sem->count == 0) {
c166cb72
PB
232 rc = pthread_cond_timedwait(&sem->cond, &sem->lock, &ts);
233 if (rc == ETIMEDOUT) {
234 break;
235 }
236 if (rc != 0) {
237 error_exit(rc, __func__);
238 }
239 }
79761c66
IT
240 if (rc != ETIMEDOUT) {
241 --sem->count;
242 }
c166cb72
PB
243 pthread_mutex_unlock(&sem->lock);
244 return (rc == ETIMEDOUT ? -1 : 0);
245#else
38b14db3
PB
246 if (ms <= 0) {
247 /* This is cheaper than sem_timedwait. */
248 do {
249 rc = sem_trywait(&sem->sem);
250 } while (rc == -1 && errno == EINTR);
251 if (rc == -1 && errno == EAGAIN) {
252 return -1;
253 }
254 } else {
c166cb72 255 compute_abs_deadline(&ts, ms);
38b14db3
PB
256 do {
257 rc = sem_timedwait(&sem->sem, &ts);
258 } while (rc == -1 && errno == EINTR);
259 if (rc == -1 && errno == ETIMEDOUT) {
260 return -1;
261 }
262 }
263 if (rc < 0) {
264 error_exit(errno, __func__);
265 }
266 return 0;
c166cb72 267#endif
38b14db3
PB
268}
269
270void qemu_sem_wait(QemuSemaphore *sem)
271{
79761c66
IT
272 int rc;
273
927fa909 274#if defined(__APPLE__) || defined(__NetBSD__)
c166cb72 275 pthread_mutex_lock(&sem->lock);
79761c66
IT
276 while (sem->count == 0) {
277 rc = pthread_cond_wait(&sem->cond, &sem->lock);
278 if (rc != 0) {
279 error_exit(rc, __func__);
280 }
c166cb72 281 }
79761c66 282 --sem->count;
c166cb72
PB
283 pthread_mutex_unlock(&sem->lock);
284#else
38b14db3
PB
285 do {
286 rc = sem_wait(&sem->sem);
287 } while (rc == -1 && errno == EINTR);
288 if (rc < 0) {
289 error_exit(errno, __func__);
290 }
c166cb72 291#endif
38b14db3
PB
292}
293
c7c4d063
PB
294#ifdef __linux__
295#define futex(...) syscall(__NR_futex, __VA_ARGS__)
296
297static inline void futex_wake(QemuEvent *ev, int n)
298{
299 futex(ev, FUTEX_WAKE, n, NULL, NULL, 0);
300}
301
302static inline void futex_wait(QemuEvent *ev, unsigned val)
303{
304 futex(ev, FUTEX_WAIT, (int) val, NULL, NULL, 0);
305}
306#else
307static inline void futex_wake(QemuEvent *ev, int n)
308{
a9eb2b60 309 pthread_mutex_lock(&ev->lock);
c7c4d063
PB
310 if (n == 1) {
311 pthread_cond_signal(&ev->cond);
312 } else {
313 pthread_cond_broadcast(&ev->cond);
314 }
a9eb2b60 315 pthread_mutex_unlock(&ev->lock);
c7c4d063
PB
316}
317
318static inline void futex_wait(QemuEvent *ev, unsigned val)
319{
320 pthread_mutex_lock(&ev->lock);
321 if (ev->value == val) {
322 pthread_cond_wait(&ev->cond, &ev->lock);
323 }
324 pthread_mutex_unlock(&ev->lock);
325}
326#endif
327
328/* Valid transitions:
329 * - free->set, when setting the event
330 * - busy->set, when setting the event, followed by futex_wake
331 * - set->free, when resetting the event
332 * - free->busy, when waiting
333 *
334 * set->busy does not happen (it can be observed from the outside but
335 * it really is set->free->busy).
336 *
337 * busy->free provably cannot happen; to enforce it, the set->free transition
338 * is done with an OR, which becomes a no-op if the event has concurrently
339 * transitioned to free or busy.
340 */
341
342#define EV_SET 0
343#define EV_FREE 1
344#define EV_BUSY -1
345
346void qemu_event_init(QemuEvent *ev, bool init)
347{
348#ifndef __linux__
349 pthread_mutex_init(&ev->lock, NULL);
350 pthread_cond_init(&ev->cond, NULL);
351#endif
352
353 ev->value = (init ? EV_SET : EV_FREE);
354}
355
356void qemu_event_destroy(QemuEvent *ev)
357{
358#ifndef __linux__
359 pthread_mutex_destroy(&ev->lock);
360 pthread_cond_destroy(&ev->cond);
361#endif
362}
363
364void qemu_event_set(QemuEvent *ev)
365{
366 if (atomic_mb_read(&ev->value) != EV_SET) {
367 if (atomic_xchg(&ev->value, EV_SET) == EV_BUSY) {
368 /* There were waiters, wake them up. */
369 futex_wake(ev, INT_MAX);
370 }
371 }
372}
373
374void qemu_event_reset(QemuEvent *ev)
375{
376 if (atomic_mb_read(&ev->value) == EV_SET) {
377 /*
378 * If there was a concurrent reset (or even reset+wait),
379 * do nothing. Otherwise change EV_SET->EV_FREE.
380 */
381 atomic_or(&ev->value, EV_FREE);
382 }
383}
384
385void qemu_event_wait(QemuEvent *ev)
386{
387 unsigned value;
388
389 value = atomic_mb_read(&ev->value);
390 if (value != EV_SET) {
391 if (value == EV_FREE) {
392 /*
393 * Leave the event reset and tell qemu_event_set that there
394 * are waiters. No need to retry, because there cannot be
395 * a concurent busy->free transition. After the CAS, the
396 * event will be either set or busy.
397 */
398 if (atomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) {
399 return;
400 }
401 }
402 futex_wait(ev, EV_BUSY);
403 }
404}
405
5c312079
DDAG
406/* Attempt to set the threads name; note that this is for debug, so
407 * we're not going to fail if we can't set it.
408 */
409static void qemu_thread_set_name(QemuThread *thread, const char *name)
410{
411#ifdef CONFIG_PTHREAD_SETNAME_NP
412 pthread_setname_np(thread->thread, name);
413#endif
414}
415
4900116e 416void qemu_thread_create(QemuThread *thread, const char *name,
e5d355d1 417 void *(*start_routine)(void*),
cf218714 418 void *arg, int mode)
e5d355d1 419{
cf218714 420 sigset_t set, oldset;
e5d355d1 421 int err;
8763046b 422 pthread_attr_t attr;
e5d355d1 423
8763046b
JK
424 err = pthread_attr_init(&attr);
425 if (err) {
426 error_exit(err, __func__);
427 }
428 if (mode == QEMU_THREAD_DETACHED) {
429 err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
430 if (err) {
431 error_exit(err, __func__);
432 }
433 }
55541c8a 434
cf218714 435 /* Leave signal handling to the iothread. */
55541c8a
PB
436 sigfillset(&set);
437 pthread_sigmask(SIG_SETMASK, &set, &oldset);
8763046b 438 err = pthread_create(&thread->thread, &attr, start_routine, arg);
e5d355d1
AL
439 if (err)
440 error_exit(err, __func__);
55541c8a 441
4900116e 442 if (name_threads) {
5c312079 443 qemu_thread_set_name(thread, name);
4900116e 444 }
4900116e 445
55541c8a 446 pthread_sigmask(SIG_SETMASK, &oldset, NULL);
8763046b
JK
447
448 pthread_attr_destroy(&attr);
e5d355d1
AL
449}
450
b7680cb6 451void qemu_thread_get_self(QemuThread *thread)
e5d355d1
AL
452{
453 thread->thread = pthread_self();
454}
455
2d797b65 456bool qemu_thread_is_self(QemuThread *thread)
e5d355d1 457{
b7680cb6 458 return pthread_equal(pthread_self(), thread->thread);
e5d355d1
AL
459}
460
313b1d69
CC
461void qemu_thread_exit(void *retval)
462{
463 pthread_exit(retval);
464}
8763046b
JK
465
466void *qemu_thread_join(QemuThread *thread)
467{
468 int err;
469 void *ret;
470
471 err = pthread_join(thread->thread, &ret);
472 if (err) {
473 error_exit(err, __func__);
474 }
475 return ret;
476}