From 1b7f01d966f97b7820f3cdd471461cf0799a93cc Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 11 Aug 2016 17:51:59 +0200 Subject: 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 Reviewed-by: Paolo Bonzini Reviewed-by: Stefan Hajnoczi --- include/qemu/coroutine_int.h | 1 + util/qemu-coroutine-lock.c | 11 +++++++++++ util/qemu-coroutine.c | 1 + 3 files changed, 13 insertions(+) diff --git a/include/qemu/coroutine_int.h b/include/qemu/coroutine_int.h index 581a7f5140..6df9d33352 100644 --- a/include/qemu/coroutine_int.h +++ b/include/qemu/coroutine_int.h @@ -39,6 +39,7 @@ struct Coroutine { void *entry_arg; Coroutine *caller; QSLIST_ENTRY(Coroutine) pool_next; + size_t locks_held; /* Coroutines that should be woken up when we yield or terminate */ QSIMPLEQ_HEAD(, Coroutine) co_queue_wakeup; diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c index f30ee8184d..14cf9ce458 100644 --- a/util/qemu-coroutine-lock.c +++ b/util/qemu-coroutine-lock.c @@ -130,6 +130,7 @@ void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex) mutex->locked = true; mutex->holder = self; + self->locks_held++; 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->holder = NULL; + self->locks_held--; qemu_co_queue_next(&mutex->queue); 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) { + Coroutine *self = qemu_coroutine_self(); + while (lock->writer) { qemu_co_queue_wait(&lock->queue); } lock->reader++; + self->locks_held++; } void qemu_co_rwlock_unlock(CoRwlock *lock) { + Coroutine *self = qemu_coroutine_self(); + assert(qemu_in_coroutine()); if (lock->writer) { lock->writer = false; @@ -179,12 +186,16 @@ void qemu_co_rwlock_unlock(CoRwlock *lock) qemu_co_queue_next(&lock->queue); } } + self->locks_held--; } void qemu_co_rwlock_wrlock(CoRwlock *lock) { + Coroutine *self = qemu_coroutine_self(); + while (lock->writer || lock->reader) { qemu_co_queue_wait(&lock->queue); } lock->writer = true; + self->locks_held++; } diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c index 89f21a9cec..3cbf225487 100644 --- a/util/qemu-coroutine.c +++ b/util/qemu-coroutine.c @@ -122,6 +122,7 @@ void qemu_coroutine_enter(Coroutine *co) case COROUTINE_YIELD: return; case COROUTINE_TERMINATE: + assert(!co->locks_held); trace_qemu_coroutine_terminate(co); coroutine_delete(co); return; -- cgit v1.2.3