xref: /openbmc/qemu/util/qemu-coroutine-sleep.c (revision 2e1cacfb)
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 "block/aio.h"
18 
19 static const char *qemu_co_sleep_ns__scheduled = "qemu_co_sleep_ns";
20 
21 void qemu_co_sleep_wake(QemuCoSleep *w)
22 {
23     Coroutine *co;
24 
25     co = w->to_wake;
26     w->to_wake = NULL;
27     if (co) {
28         /* Write of schedule protected by barrier write in aio_co_schedule */
29         const char *scheduled = qatomic_cmpxchg(&co->scheduled,
30                                                 qemu_co_sleep_ns__scheduled, NULL);
31 
32         assert(scheduled == qemu_co_sleep_ns__scheduled);
33         aio_co_wake(co);
34     }
35 }
36 
37 static void co_sleep_cb(void *opaque)
38 {
39     QemuCoSleep *w = opaque;
40     qemu_co_sleep_wake(w);
41 }
42 
43 void coroutine_fn qemu_co_sleep(QemuCoSleep *w)
44 {
45     Coroutine *co = qemu_coroutine_self();
46 
47     const char *scheduled = qatomic_cmpxchg(&co->scheduled, NULL,
48                                             qemu_co_sleep_ns__scheduled);
49     if (scheduled) {
50         fprintf(stderr,
51                 "%s: Co-routine was already scheduled in '%s'\n",
52                 __func__, scheduled);
53         abort();
54     }
55 
56     w->to_wake = co;
57     qemu_coroutine_yield();
58 
59     /* w->to_wake is cleared before resuming this coroutine.  */
60     assert(w->to_wake == NULL);
61 }
62 
63 void coroutine_fn qemu_co_sleep_ns_wakeable(QemuCoSleep *w,
64                                             QEMUClockType type, int64_t ns)
65 {
66     AioContext *ctx = qemu_get_current_aio_context();
67     QEMUTimer ts;
68 
69     aio_timer_init(ctx, &ts, type, SCALE_NS, co_sleep_cb, w);
70     timer_mod(&ts, qemu_clock_get_ns(type) + ns);
71 
72     /*
73      * The timer will fire in the current AiOContext, so the callback
74      * must happen after qemu_co_sleep yields and there is no race
75      * between timer_mod and qemu_co_sleep.
76      */
77     qemu_co_sleep(w);
78     timer_del(&ts);
79 }
80