qemu/qemu-coroutine-lock.c
<<
>>
Prefs
   1/*
   2 * coroutine queues and locks
   3 *
   4 * Copyright (c) 2011 Kevin Wolf <kwolf@redhat.com>
   5 *
   6 * Permission is hereby granted, free of charge, to any person obtaining a copy
   7 * of this software and associated documentation files (the "Software"), to deal
   8 * in the Software without restriction, including without limitation the rights
   9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10 * copies of the Software, and to permit persons to whom the Software is
  11 * furnished to do so, subject to the following conditions:
  12 *
  13 * The above copyright notice and this permission notice shall be included in
  14 * all copies or substantial portions of the Software.
  15 *
  16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22 * THE SOFTWARE.
  23 */
  24
  25#include "qemu-common.h"
  26#include "block/coroutine.h"
  27#include "block/coroutine_int.h"
  28#include "qemu/queue.h"
  29#include "trace.h"
  30
  31void qemu_co_queue_init(CoQueue *queue)
  32{
  33    QTAILQ_INIT(&queue->entries);
  34}
  35
  36void coroutine_fn qemu_co_queue_wait(CoQueue *queue)
  37{
  38    Coroutine *self = qemu_coroutine_self();
  39    QTAILQ_INSERT_TAIL(&queue->entries, self, co_queue_next);
  40    qemu_coroutine_yield();
  41    assert(qemu_in_coroutine());
  42}
  43
  44void coroutine_fn qemu_co_queue_wait_insert_head(CoQueue *queue)
  45{
  46    Coroutine *self = qemu_coroutine_self();
  47    QTAILQ_INSERT_HEAD(&queue->entries, self, co_queue_next);
  48    qemu_coroutine_yield();
  49    assert(qemu_in_coroutine());
  50}
  51
  52/**
  53 * qemu_co_queue_run_restart:
  54 *
  55 * Enter each coroutine that was previously marked for restart by
  56 * qemu_co_queue_next() or qemu_co_queue_restart_all().  This function is
  57 * invoked by the core coroutine code when the current coroutine yields or
  58 * terminates.
  59 */
  60void qemu_co_queue_run_restart(Coroutine *co)
  61{
  62    Coroutine *next;
  63
  64    trace_qemu_co_queue_run_restart(co);
  65    while ((next = QTAILQ_FIRST(&co->co_queue_wakeup))) {
  66        QTAILQ_REMOVE(&co->co_queue_wakeup, next, co_queue_next);
  67        qemu_coroutine_enter(next, NULL);
  68    }
  69}
  70
  71static bool qemu_co_queue_do_restart(CoQueue *queue, bool single)
  72{
  73    Coroutine *self = qemu_coroutine_self();
  74    Coroutine *next;
  75
  76    if (QTAILQ_EMPTY(&queue->entries)) {
  77        return false;
  78    }
  79
  80    while ((next = QTAILQ_FIRST(&queue->entries)) != NULL) {
  81        QTAILQ_REMOVE(&queue->entries, next, co_queue_next);
  82        QTAILQ_INSERT_TAIL(&self->co_queue_wakeup, next, co_queue_next);
  83        trace_qemu_co_queue_next(next);
  84        if (single) {
  85            break;
  86        }
  87    }
  88    return true;
  89}
  90
  91bool coroutine_fn qemu_co_queue_next(CoQueue *queue)
  92{
  93    assert(qemu_in_coroutine());
  94    return qemu_co_queue_do_restart(queue, true);
  95}
  96
  97void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue)
  98{
  99    assert(qemu_in_coroutine());
 100    qemu_co_queue_do_restart(queue, false);
 101}
 102
 103bool qemu_co_enter_next(CoQueue *queue)
 104{
 105    Coroutine *next;
 106
 107    next = QTAILQ_FIRST(&queue->entries);
 108    if (!next) {
 109        return false;
 110    }
 111
 112    QTAILQ_REMOVE(&queue->entries, next, co_queue_next);
 113    qemu_coroutine_enter(next, NULL);
 114    return true;
 115}
 116
 117bool qemu_co_queue_empty(CoQueue *queue)
 118{
 119    return (QTAILQ_FIRST(&queue->entries) == NULL);
 120}
 121
 122void qemu_co_mutex_init(CoMutex *mutex)
 123{
 124    memset(mutex, 0, sizeof(*mutex));
 125    qemu_co_queue_init(&mutex->queue);
 126}
 127
 128void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex)
 129{
 130    Coroutine *self = qemu_coroutine_self();
 131
 132    trace_qemu_co_mutex_lock_entry(mutex, self);
 133
 134    while (mutex->locked) {
 135        qemu_co_queue_wait(&mutex->queue);
 136    }
 137
 138    mutex->locked = true;
 139
 140    trace_qemu_co_mutex_lock_return(mutex, self);
 141}
 142
 143void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex)
 144{
 145    Coroutine *self = qemu_coroutine_self();
 146
 147    trace_qemu_co_mutex_unlock_entry(mutex, self);
 148
 149    assert(mutex->locked == true);
 150    assert(qemu_in_coroutine());
 151
 152    mutex->locked = false;
 153    qemu_co_queue_next(&mutex->queue);
 154
 155    trace_qemu_co_mutex_unlock_return(mutex, self);
 156}
 157
 158void qemu_co_rwlock_init(CoRwlock *lock)
 159{
 160    memset(lock, 0, sizeof(*lock));
 161    qemu_co_queue_init(&lock->queue);
 162}
 163
 164void qemu_co_rwlock_rdlock(CoRwlock *lock)
 165{
 166    while (lock->writer) {
 167        qemu_co_queue_wait(&lock->queue);
 168    }
 169    lock->reader++;
 170}
 171
 172void qemu_co_rwlock_unlock(CoRwlock *lock)
 173{
 174    assert(qemu_in_coroutine());
 175    if (lock->writer) {
 176        lock->writer = false;
 177        qemu_co_queue_restart_all(&lock->queue);
 178    } else {
 179        lock->reader--;
 180        assert(lock->reader >= 0);
 181        /* Wakeup only one waiting writer */
 182        if (!lock->reader) {
 183            qemu_co_queue_next(&lock->queue);
 184        }
 185    }
 186}
 187
 188void qemu_co_rwlock_wrlock(CoRwlock *lock)
 189{
 190    while (lock->writer || lock->reader) {
 191        qemu_co_queue_wait(&lock->queue);
 192    }
 193    lock->writer = true;
 194}
 195