summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--util/qemu-coroutine-lock.c19
-rw-r--r--util/qemu-coroutine.c5
2 files changed, 22 insertions, 2 deletions
diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c
index 6328eed26b..b44b5d55eb 100644
--- a/util/qemu-coroutine-lock.c
+++ b/util/qemu-coroutine-lock.c
@@ -77,10 +77,25 @@ void coroutine_fn qemu_co_queue_wait(CoQueue *queue, CoMutex *mutex)
void qemu_co_queue_run_restart(Coroutine *co)
{
Coroutine *next;
+ QSIMPLEQ_HEAD(, Coroutine) tmp_queue_wakeup =
+ QSIMPLEQ_HEAD_INITIALIZER(tmp_queue_wakeup);
trace_qemu_co_queue_run_restart(co);
- while ((next = QSIMPLEQ_FIRST(&co->co_queue_wakeup))) {
- QSIMPLEQ_REMOVE_HEAD(&co->co_queue_wakeup, co_queue_next);
+
+ /* Because "co" has yielded, any coroutine that we wakeup can resume it.
+ * If this happens and "co" terminates, co->co_queue_wakeup becomes
+ * invalid memory. Therefore, use a temporary queue and do not touch
+ * the "co" coroutine as soon as you enter another one.
+ *
+ * In its turn resumed "co" can pupulate "co_queue_wakeup" queue with
+ * new coroutines to be woken up. The caller, who has resumed "co",
+ * will be responsible for traversing the same queue, which may cause
+ * a different wakeup order but not any missing wakeups.
+ */
+ QSIMPLEQ_CONCAT(&tmp_queue_wakeup, &co->co_queue_wakeup);
+
+ while ((next = QSIMPLEQ_FIRST(&tmp_queue_wakeup))) {
+ QSIMPLEQ_REMOVE_HEAD(&tmp_queue_wakeup, co_queue_next);
qemu_coroutine_enter(next);
}
}
diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c
index 486af9a622..d6095c1d5a 100644
--- a/util/qemu-coroutine.c
+++ b/util/qemu-coroutine.c
@@ -126,6 +126,11 @@ void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co)
qemu_co_queue_run_restart(co);
+ /* Beware, if ret == COROUTINE_YIELD and qemu_co_queue_run_restart()
+ * has started any other coroutine, "co" might have been reentered
+ * and even freed by now! So be careful and do not touch it.
+ */
+
switch (ret) {
case COROUTINE_YIELD:
return;