master
c 102 lines 3.02 KB
Raw
1 /*
2 * QEMU coroutine sleep
3 *
4 * Copyright IBM, Corp. 2011
5 *
6 * Authors:
7 * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
8 *
9 * This work is licensed under the terms of the GNU LGPL, version 2 or later.
10 * See the COPYING.LIB file in the top-level directory.
11 *
12 */
13
14 #include "qemu/osdep.h"
15 #include "qemu/coroutine_int.h"
16 #include "qemu/timer.h"
17 #include "qemu/aio.h"
18
19 static const char *qemu_co_sleep_ns__scheduled = "qemu_co_sleep_ns";
20
21 /*
22 * Sentinel stored in QemuCoSleep::to_wake by qemu_co_sleep_wake() when no
23 * sleeper has parked yet. The next qemu_co_sleep() consumes it and returns
24 * without yielding, so a wake that races the arming of a sleep is never
25 * lost.
26 */
27 #define QEMU_CO_SLEEP_PENDING ((Coroutine *)(uintptr_t)1)
28
29 void qemu_co_sleep_wake(QemuCoSleep *w)
30 {
31 Coroutine *co;
32
33 co = qatomic_xchg(&w->to_wake, QEMU_CO_SLEEP_PENDING);
34 if (co == NULL || co == QEMU_CO_SLEEP_PENDING) {
35 /* No sleeper, or a wake is already pending. */
36 return;
37 }
38
39 /* Write of scheduled protected by barrier write in aio_co_schedule */
40 const char *scheduled = qatomic_cmpxchg(&co->scheduled,
41 qemu_co_sleep_ns__scheduled, NULL);
42 assert(scheduled == qemu_co_sleep_ns__scheduled);
43 aio_co_wake(co);
44 }
45
46 static void co_sleep_cb(void *opaque)
47 {
48 QemuCoSleep *w = opaque;
49 qemu_co_sleep_wake(w);
50 }
51
52 void coroutine_fn qemu_co_sleep(QemuCoSleep *w)
53 {
54 Coroutine *co = qemu_coroutine_self();
55 Coroutine *prev;
56
57 const char *scheduled = qatomic_cmpxchg(&co->scheduled, NULL,
58 qemu_co_sleep_ns__scheduled);
59 if (scheduled) {
60 fprintf(stderr,
61 "%s: Co-routine was already scheduled in '%s'\n",
62 __func__, scheduled);
63 abort();
64 }
65
66 /*
67 * Publish ourselves as the sleeper. A wake delivered before we got here,
68 * or one racing this publish, leaves QEMU_CO_SLEEP_PENDING in to_wake;
69 * the cmpxchg then fails and we consume the wake without yielding.
70 */
71 prev = qatomic_cmpxchg(&w->to_wake, NULL, co);
72 if (prev == QEMU_CO_SLEEP_PENDING) {
73 qatomic_set(&w->to_wake, NULL);
74 qatomic_set(&co->scheduled, NULL);
75 return;
76 }
77 assert(prev == NULL);
78
79 qemu_coroutine_yield();
80
81 /* The waker left QEMU_CO_SLEEP_PENDING; clear it for the next sleep. */
82 qatomic_set(&w->to_wake, NULL);
83 }
84
85 void coroutine_fn qemu_co_sleep_ns_wakeable(QemuCoSleep *w,
86 QEMUClockType type, int64_t ns)
87 {
88 AioContext *ctx = qemu_get_current_aio_context();
89 QEMUTimer ts;
90
91 aio_timer_init(ctx, &ts, type, SCALE_NS, co_sleep_cb, w);
92 timer_mod(&ts, qemu_clock_get_ns(type) + ns);
93
94 /*
95 * A wake racing with the arming of the sleep -- including the timer
96 * we just armed firing in another AioContext before qemu_co_sleep()
97 * publishes itself -- is captured by the sticky PENDING state in
98 * qemu_co_sleep_wake() and consumed here without yielding.
99 */
100 qemu_co_sleep(w);
101 timer_del(&ts);
102 }