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
  44/**
  45 * qemu_co_queue_run_restart:
  46 *
  47 * Enter each coroutine that was previously marked for restart by
  48 * qemu_co_queue_next() or qemu_co_queue_restart_all().  This function is
  49 * invoked by the core coroutine code when the current coroutine yields or
  50 * terminates.
  51 */
  52void qemu_co_queue_run_restart(Coroutine *co)
  53{
  54    Coroutine *next;
  55
  56    trace_qemu_co_queue_run_restart(co);
  57    while ((next = QTAILQ_FIRST(&co->co_queue_wakeup))) {
  58        QTAILQ_REMOVE(&co->co_queue_wakeup, next, co_queue_next);
  59        qemu_coroutine_enter(next, NULL);
  60    }
  61}
  62
  63static bool qemu_co_queue_do_restart(CoQueue *queue, bool single)
  64{
  65    Coroutine *self = qemu_coroutine_self();
  66    Coroutine *next;
  67
  68    if (QTAILQ_EMPTY(&queue->entries)) {
  69        return false;
  70    }
  71
  72    while ((next = QTAILQ_FIRST(&queue->entries)) != NULL) {
  73        QTAILQ_REMOVE(&queue->entries, next, co_queue_next);
  74        QTAILQ_INSERT_TAIL(&self->co_queue_wakeup, next, co_queue_next);
  75        trace_qemu_co_queue_next(next);
  76        if (single) {
  77            break;
  78        }
  79    }
  80    return true;
  81}
  82
  83bool coroutine_fn qemu_co_queue_next(CoQueue *queue)
  84{
  85    assert(qemu_in_coroutine());
  86    return qemu_co_queue_do_restart(queue, true);
  87}
  88
  89void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue)
  90{
  91    assert(qemu_in_coroutine());
  92    qemu_co_queue_do_restart(queue, false);
  93}
  94
  95bool qemu_co_enter_next(CoQueue *queue)
  96{
  97    Coroutine *next;
  98
  99    next = QTAILQ_FIRST(&queue->entries);
 100    if (!next) {
 101        return false;
 102    }
 103
 104    QTAILQ_REMOVE(&queue->entries, next, co_queue_next);
 105    qemu_coroutine_enter(next, NULL);
 106    return true;
 107}
 108
 109bool qemu_co_queue_empty(CoQueue *queue)
 110{
 111    return QTAILQ_FIRST(&queue->entries) == NULL;
 112}
 113
 114void qemu_co_mutex_init(CoMutex *mutex)
 115{
 116    memset(mutex, 0, sizeof(*mutex));
 117    qemu_co_queue_init(&mutex->queue);
 118}
 119
 120void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex)
 121{
 122    Coroutine *self = qemu_coroutine_self();
 123
 124    trace_qemu_co_mutex_lock_entry(mutex, self);
 125
 126    while (mutex->locked) {
 127        qemu_co_queue_wait(&mutex->queue);
 128    }
 129
 130    mutex->locked = true;
 131
 132    trace_qemu_co_mutex_lock_return(mutex, self);
 133}
 134
 135void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex)
 136{
 137    Coroutine *self = qemu_coroutine_self();
 138
 139    trace_qemu_co_mutex_unlock_entry(mutex, self);
 140
 141    assert(mutex->locked == true);
 142    assert(qemu_in_coroutine());
 143
 144    mutex->locked = false;
 145    qemu_co_queue_next(&mutex->queue);
 146
 147    trace_qemu_co_mutex_unlock_return(mutex, self);
 148}
 149
 150void qemu_co_rwlock_init(CoRwlock *lock)
 151{
 152    memset(lock, 0, sizeof(*lock));
 153    qemu_co_queue_init(&lock->queue);
 154}
 155
 156void qemu_co_rwlock_rdlock(CoRwlock *lock)
 157{
 158    while (lock->writer) {
 159        qemu_co_queue_wait(&lock->queue);
 160    }
 161    lock->reader++;
 162}
 163
 164void qemu_co_rwlock_unlock(CoRwlock *lock)
 165{
 166    assert(qemu_in_coroutine());
 167    if (lock->writer) {
 168        lock->writer = false;
 169        qemu_co_queue_restart_all(&lock->queue);
 170    } else {
 171        lock->reader--;
 172        assert(lock->reader >= 0);
 173        /* Wakeup only one waiting writer */
 174        if (!lock->reader) {
 175            qemu_co_queue_next(&lock->queue);
 176        }
 177    }
 178}
 179
 180void qemu_co_rwlock_wrlock(CoRwlock *lock)
 181{
 182    while (lock->writer || lock->reader) {
 183        qemu_co_queue_wait(&lock->queue);
 184    }
 185    lock->writer = true;
 186}
 187