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 "qemu-coroutine.h"
  27#include "qemu-coroutine-int.h"
  28#include "qemu-queue.h"
  29#include "qemu-aio.h"
  30#include "trace.h"
  31
  32static QTAILQ_HEAD(, Coroutine) unlock_bh_queue =
  33    QTAILQ_HEAD_INITIALIZER(unlock_bh_queue);
  34static QEMUBH* unlock_bh;
  35
  36static void qemu_co_queue_next_bh(void *opaque)
  37{
  38    Coroutine *next;
  39
  40    trace_qemu_co_queue_next_bh();
  41    while ((next = QTAILQ_FIRST(&unlock_bh_queue))) {
  42        QTAILQ_REMOVE(&unlock_bh_queue, next, co_queue_next);
  43        qemu_coroutine_enter(next, NULL);
  44    }
  45}
  46
  47void qemu_co_queue_init(CoQueue *queue)
  48{
  49    QTAILQ_INIT(&queue->entries);
  50
  51    if (!unlock_bh) {
  52        unlock_bh = qemu_bh_new(qemu_co_queue_next_bh, NULL);
  53    }
  54}
  55
  56void coroutine_fn qemu_co_queue_wait(CoQueue *queue)
  57{
  58    Coroutine *self = qemu_coroutine_self();
  59    QTAILQ_INSERT_TAIL(&queue->entries, self, co_queue_next);
  60    qemu_coroutine_yield();
  61    assert(qemu_in_coroutine());
  62}
  63
  64void coroutine_fn qemu_co_queue_wait_insert_head(CoQueue *queue)
  65{
  66    Coroutine *self = qemu_coroutine_self();
  67    QTAILQ_INSERT_HEAD(&queue->entries, self, co_queue_next);
  68    qemu_coroutine_yield();
  69    assert(qemu_in_coroutine());
  70}
  71
  72bool qemu_co_queue_next(CoQueue *queue)
  73{
  74    Coroutine *next;
  75
  76    next = QTAILQ_FIRST(&queue->entries);
  77    if (next) {
  78        QTAILQ_REMOVE(&queue->entries, next, co_queue_next);
  79        QTAILQ_INSERT_TAIL(&unlock_bh_queue, next, co_queue_next);
  80        trace_qemu_co_queue_next(next);
  81        qemu_bh_schedule(unlock_bh);
  82    }
  83
  84    return (next != NULL);
  85}
  86
  87void qemu_co_queue_restart_all(CoQueue *queue)
  88{
  89    while (qemu_co_queue_next(queue)) {
  90        /* Do nothing */
  91    }
  92}
  93
  94bool qemu_co_queue_empty(CoQueue *queue)
  95{
  96    return (QTAILQ_FIRST(&queue->entries) == NULL);
  97}
  98
  99void qemu_co_mutex_init(CoMutex *mutex)
 100{
 101    memset(mutex, 0, sizeof(*mutex));
 102    qemu_co_queue_init(&mutex->queue);
 103}
 104
 105void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex)
 106{
 107    Coroutine *self = qemu_coroutine_self();
 108
 109    trace_qemu_co_mutex_lock_entry(mutex, self);
 110
 111    while (mutex->locked) {
 112        qemu_co_queue_wait(&mutex->queue);
 113    }
 114
 115    mutex->locked = true;
 116
 117    trace_qemu_co_mutex_lock_return(mutex, self);
 118}
 119
 120void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex)
 121{
 122    Coroutine *self = qemu_coroutine_self();
 123
 124    trace_qemu_co_mutex_unlock_entry(mutex, self);
 125
 126    assert(mutex->locked == true);
 127    assert(qemu_in_coroutine());
 128
 129    mutex->locked = false;
 130    qemu_co_queue_next(&mutex->queue);
 131
 132    trace_qemu_co_mutex_unlock_return(mutex, self);
 133}
 134
 135void qemu_co_rwlock_init(CoRwlock *lock)
 136{
 137    memset(lock, 0, sizeof(*lock));
 138    qemu_co_queue_init(&lock->queue);
 139}
 140
 141void qemu_co_rwlock_rdlock(CoRwlock *lock)
 142{
 143    while (lock->writer) {
 144        qemu_co_queue_wait(&lock->queue);
 145    }
 146    lock->reader++;
 147}
 148
 149void qemu_co_rwlock_unlock(CoRwlock *lock)
 150{
 151    assert(qemu_in_coroutine());
 152    if (lock->writer) {
 153        lock->writer = false;
 154        qemu_co_queue_restart_all(&lock->queue);
 155    } else {
 156        lock->reader--;
 157        assert(lock->reader >= 0);
 158        /* Wakeup only one waiting writer */
 159        if (!lock->reader) {
 160            qemu_co_queue_next(&lock->queue);
 161        }
 162    }
 163}
 164
 165void qemu_co_rwlock_wrlock(CoRwlock *lock)
 166{
 167    while (lock->writer || lock->reader) {
 168        qemu_co_queue_wait(&lock->queue);
 169    }
 170    lock->writer = true;
 171}
 172