coroutine-lock: make qemu_co_enter_next thread-safe
qemu_co_queue_next does not need to release and re-acquire the mutex, because the queued coroutine does not run immediately. However, this does not hold for qemu_co_enter_next. Now that qemu_co_queue_wait can synchronize (via QemuLockable) with code that is not running in coroutine context, it's important that code using qemu_co_enter_next can easily use a standardized locking idiom. First of all, qemu_co_enter_next must use aio_co_wake to restart the coroutine. Second, the function gains a second argument, a QemuLockable*, and the comments of qemu_co_queue_next and qemu_co_queue_restart_all are adjusted to clarify the difference. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Message-Id: <20180203153935.8056-5-pbonzini@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Reviewed-by: Fam Zheng <famz@redhat.com> Signed-off-by: Fam Zheng <famz@redhat.com>
This commit is contained in:
parent
1a957cf9c4
commit
5261dd7b01
@ -20,13 +20,13 @@
|
||||
static void fsdev_throttle_read_timer_cb(void *opaque)
|
||||
{
|
||||
FsThrottle *fst = opaque;
|
||||
qemu_co_enter_next(&fst->throttled_reqs[false]);
|
||||
qemu_co_enter_next(&fst->throttled_reqs[false], NULL);
|
||||
}
|
||||
|
||||
static void fsdev_throttle_write_timer_cb(void *opaque)
|
||||
{
|
||||
FsThrottle *fst = opaque;
|
||||
qemu_co_enter_next(&fst->throttled_reqs[true]);
|
||||
qemu_co_enter_next(&fst->throttled_reqs[true], NULL);
|
||||
}
|
||||
|
||||
void fsdev_throttle_parse_opts(QemuOpts *opts, FsThrottle *fst, Error **errp)
|
||||
|
@ -188,21 +188,28 @@ void qemu_co_queue_init(CoQueue *queue);
|
||||
void coroutine_fn qemu_co_queue_wait_impl(CoQueue *queue, QemuLockable *lock);
|
||||
|
||||
/**
|
||||
* Restarts the next coroutine in the CoQueue and removes it from the queue.
|
||||
*
|
||||
* Returns true if a coroutine was restarted, false if the queue is empty.
|
||||
* Removes the next coroutine from the CoQueue, and wake it up.
|
||||
* Returns true if a coroutine was removed, false if the queue is empty.
|
||||
*/
|
||||
bool coroutine_fn qemu_co_queue_next(CoQueue *queue);
|
||||
|
||||
/**
|
||||
* Restarts all coroutines in the CoQueue and leaves the queue empty.
|
||||
* Empties the CoQueue; all coroutines are woken up.
|
||||
*/
|
||||
void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue);
|
||||
|
||||
/**
|
||||
* Enter the next coroutine in the queue
|
||||
* Removes the next coroutine from the CoQueue, and wake it up. Unlike
|
||||
* qemu_co_queue_next, this function releases the lock during aio_co_wake
|
||||
* because it is meant to be used outside coroutine context; in that case, the
|
||||
* coroutine is entered immediately, before qemu_co_enter_next returns.
|
||||
*
|
||||
* If used in coroutine context, qemu_co_enter_next is equivalent to
|
||||
* qemu_co_queue_next.
|
||||
*/
|
||||
bool qemu_co_enter_next(CoQueue *queue);
|
||||
#define qemu_co_enter_next(queue, lock) \
|
||||
qemu_co_enter_next_impl(queue, QEMU_MAKE_LOCKABLE(lock))
|
||||
bool qemu_co_enter_next_impl(CoQueue *queue, QemuLockable *lock);
|
||||
|
||||
/**
|
||||
* Checks if the CoQueue is empty.
|
||||
|
@ -132,7 +132,7 @@ void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue)
|
||||
qemu_co_queue_do_restart(queue, false);
|
||||
}
|
||||
|
||||
bool qemu_co_enter_next(CoQueue *queue)
|
||||
bool qemu_co_enter_next_impl(CoQueue *queue, QemuLockable *lock)
|
||||
{
|
||||
Coroutine *next;
|
||||
|
||||
@ -142,7 +142,13 @@ bool qemu_co_enter_next(CoQueue *queue)
|
||||
}
|
||||
|
||||
QSIMPLEQ_REMOVE_HEAD(&queue->entries, co_queue_next);
|
||||
qemu_coroutine_enter(next);
|
||||
if (lock) {
|
||||
qemu_lockable_unlock(lock);
|
||||
}
|
||||
aio_co_wake(next);
|
||||
if (lock) {
|
||||
qemu_lockable_lock(lock);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user