coroutine: Assert that no locks are held on termination

A coroutine that takes a lock must also release it again. If the
coroutine terminates without having released all its locks, it's buggy
and we'll probably run into a deadlock sooner or later. Make sure that
we don't get such cases.

Signed-off-by: Kevin Wolf <kwolf@redhat.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
This commit is contained in:
Kevin Wolf 2016-08-11 17:51:59 +02:00
parent 0e438cdc93
commit 1b7f01d966
3 changed files with 13 additions and 0 deletions

View File

@ -39,6 +39,7 @@ struct Coroutine {
void *entry_arg; void *entry_arg;
Coroutine *caller; Coroutine *caller;
QSLIST_ENTRY(Coroutine) pool_next; QSLIST_ENTRY(Coroutine) pool_next;
size_t locks_held;
/* Coroutines that should be woken up when we yield or terminate */ /* Coroutines that should be woken up when we yield or terminate */
QSIMPLEQ_HEAD(, Coroutine) co_queue_wakeup; QSIMPLEQ_HEAD(, Coroutine) co_queue_wakeup;

View File

@ -130,6 +130,7 @@ void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex)
mutex->locked = true; mutex->locked = true;
mutex->holder = self; mutex->holder = self;
self->locks_held++;
trace_qemu_co_mutex_lock_return(mutex, self); trace_qemu_co_mutex_lock_return(mutex, self);
} }
@ -146,6 +147,7 @@ void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex)
mutex->locked = false; mutex->locked = false;
mutex->holder = NULL; mutex->holder = NULL;
self->locks_held--;
qemu_co_queue_next(&mutex->queue); qemu_co_queue_next(&mutex->queue);
trace_qemu_co_mutex_unlock_return(mutex, self); trace_qemu_co_mutex_unlock_return(mutex, self);
@ -159,14 +161,19 @@ void qemu_co_rwlock_init(CoRwlock *lock)
void qemu_co_rwlock_rdlock(CoRwlock *lock) void qemu_co_rwlock_rdlock(CoRwlock *lock)
{ {
Coroutine *self = qemu_coroutine_self();
while (lock->writer) { while (lock->writer) {
qemu_co_queue_wait(&lock->queue); qemu_co_queue_wait(&lock->queue);
} }
lock->reader++; lock->reader++;
self->locks_held++;
} }
void qemu_co_rwlock_unlock(CoRwlock *lock) void qemu_co_rwlock_unlock(CoRwlock *lock)
{ {
Coroutine *self = qemu_coroutine_self();
assert(qemu_in_coroutine()); assert(qemu_in_coroutine());
if (lock->writer) { if (lock->writer) {
lock->writer = false; lock->writer = false;
@ -179,12 +186,16 @@ void qemu_co_rwlock_unlock(CoRwlock *lock)
qemu_co_queue_next(&lock->queue); qemu_co_queue_next(&lock->queue);
} }
} }
self->locks_held--;
} }
void qemu_co_rwlock_wrlock(CoRwlock *lock) void qemu_co_rwlock_wrlock(CoRwlock *lock)
{ {
Coroutine *self = qemu_coroutine_self();
while (lock->writer || lock->reader) { while (lock->writer || lock->reader) {
qemu_co_queue_wait(&lock->queue); qemu_co_queue_wait(&lock->queue);
} }
lock->writer = true; lock->writer = true;
self->locks_held++;
} }

View File

@ -122,6 +122,7 @@ void qemu_coroutine_enter(Coroutine *co)
case COROUTINE_YIELD: case COROUTINE_YIELD:
return; return;
case COROUTINE_TERMINATE: case COROUTINE_TERMINATE:
assert(!co->locks_held);
trace_qemu_coroutine_terminate(co); trace_qemu_coroutine_terminate(co);
coroutine_delete(co); coroutine_delete(co);
return; return;