Commit 1b7f01d9 authored by Kevin Wolf's avatar Kevin Wolf
Browse files

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: default avatarKevin Wolf <kwolf@redhat.com>
Reviewed-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
Reviewed-by: default avatarStefan Hajnoczi <stefanha@redhat.com>
parent 0e438cdc
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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;
+11 −0
Original line number Diff line number Diff line
@@ -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++;
}
+1 −0
Original line number Diff line number Diff line
@@ -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;