When curl_co_preadv is called it sets up an ACB block which points to
current coroutine. It will then call curl_setup_preadv and wait until
request is completed by polling return status and yeilding:
curl_setup_preadv(bs, &acb);
while (acb.ret == -EINPROGRESS) {
qemu_coroutine_yield();
}
curl_setup_preadv will ask libcurl to handle read request and to use
curl_read_cb as completion callback for each completed chunk.
When curl_read_cb sees request as completed it will attempt to wake up
issuing coroutine assuming that yield was called previously:
qemu_mutex_unlock(&s->s->mutex);
aio_co_wake(acb->co);
qemu_mutex_lock(&s->s->mutex);
However if request is short enough (< 16K in our test) curl_read_cb will
be called right away before returning from libcurl to curl_setup_preadv.
Request will be completed before yield was called from the same
coroutine, which asserts in aio_co_enter.
This change attempts to fix this by waking completion coroutine only if
it is not the current one.
Signed-off-by: Evgeny Yakovlev <wrfsh@yandex-team.ru>
---
block/curl.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/block/curl.c b/block/curl.c
index 2a244e2..b1106d6 100644
--- a/block/curl.c
+++ b/block/curl.c
@@ -271,9 +271,12 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque)
acb->ret = 0;
s->acb[i] = NULL;
- qemu_mutex_unlock(&s->s->mutex);
- aio_co_wake(acb->co);
- qemu_mutex_lock(&s->s->mutex);
+
+ if (qemu_coroutine_self() != acb->co) {
+ qemu_mutex_unlock(&s->s->mutex);
+ aio_co_wake(acb->co);
+ qemu_mutex_lock(&s->s->mutex);
+ }
}
}
--
2.7.4