qemu/include/qemu/lockable.h
<<
>>
Prefs
   1/*
   2 * Polymorphic locking functions (aka poor man templates)
   3 *
   4 * Copyright Red Hat, Inc. 2017, 2018
   5 *
   6 * Author: Paolo Bonzini <pbonzini@redhat.com>
   7 *
   8 * This work is licensed under the terms of the GNU LGPL, version 2 or later.
   9 * See the COPYING.LIB file in the top-level directory.
  10 *
  11 */
  12
  13#ifndef QEMU_LOCKABLE_H
  14#define QEMU_LOCKABLE_H
  15
  16#include "qemu/coroutine.h"
  17#include "qemu/thread.h"
  18
  19typedef void QemuLockUnlockFunc(void *);
  20
  21struct QemuLockable {
  22    void *object;
  23    QemuLockUnlockFunc *lock;
  24    QemuLockUnlockFunc *unlock;
  25};
  26
  27/* This function gives an error if an invalid, non-NULL pointer type is passed
  28 * to QEMU_MAKE_LOCKABLE.  For optimized builds, we can rely on dead-code elimination
  29 * from the compiler, and give the errors already at link time.
  30 */
  31#if defined(__OPTIMIZE__) && !defined(__SANITIZE_ADDRESS__)
  32void unknown_lock_type(void *);
  33#else
  34static inline void unknown_lock_type(void *unused)
  35{
  36    abort();
  37}
  38#endif
  39
  40static inline __attribute__((__always_inline__)) QemuLockable *
  41qemu_make_lockable(void *x, QemuLockable *lockable)
  42{
  43    /* We cannot test this in a macro, otherwise we get compiler
  44     * warnings like "the address of 'm' will always evaluate as 'true'".
  45     */
  46    return x ? lockable : NULL;
  47}
  48
  49/* Auxiliary macros to simplify QEMU_MAKE_LOCABLE.  */
  50#define QEMU_LOCK_FUNC(x) ((QemuLockUnlockFunc *)    \
  51    QEMU_GENERIC(x,                                  \
  52                 (QemuMutex *, qemu_mutex_lock),     \
  53                 (QemuRecMutex *, qemu_rec_mutex_lock), \
  54                 (CoMutex *, qemu_co_mutex_lock),    \
  55                 (QemuSpin *, qemu_spin_lock),       \
  56                 unknown_lock_type))
  57
  58#define QEMU_UNLOCK_FUNC(x) ((QemuLockUnlockFunc *)  \
  59    QEMU_GENERIC(x,                                  \
  60                 (QemuMutex *, qemu_mutex_unlock),   \
  61                 (QemuRecMutex *, qemu_rec_mutex_unlock), \
  62                 (CoMutex *, qemu_co_mutex_unlock),  \
  63                 (QemuSpin *, qemu_spin_unlock),     \
  64                 unknown_lock_type))
  65
  66/* In C, compound literals have the lifetime of an automatic variable.
  67 * In C++ it would be different, but then C++ wouldn't need QemuLockable
  68 * either...
  69 */
  70#define QEMU_MAKE_LOCKABLE_(x) (&(QemuLockable) {     \
  71        .object = (x),                               \
  72        .lock = QEMU_LOCK_FUNC(x),                   \
  73        .unlock = QEMU_UNLOCK_FUNC(x),               \
  74    })
  75
  76/* QEMU_MAKE_LOCKABLE - Make a polymorphic QemuLockable
  77 *
  78 * @x: a lock object (currently one of QemuMutex, QemuRecMutex, CoMutex, QemuSpin).
  79 *
  80 * Returns a QemuLockable object that can be passed around
  81 * to a function that can operate with locks of any kind, or
  82 * NULL if @x is %NULL.
  83 */
  84#define QEMU_MAKE_LOCKABLE(x)                        \
  85    QEMU_GENERIC(x,                                  \
  86                 (QemuLockable *, (x)),              \
  87                 qemu_make_lockable((x), QEMU_MAKE_LOCKABLE_(x)))
  88
  89/* QEMU_MAKE_LOCKABLE_NONNULL - Make a polymorphic QemuLockable
  90 *
  91 * @x: a lock object (currently one of QemuMutex, QemuRecMutex, CoMutex, QemuSpin).
  92 *
  93 * Returns a QemuLockable object that can be passed around
  94 * to a function that can operate with locks of any kind.
  95 */
  96#define QEMU_MAKE_LOCKABLE_NONNULL(x)                \
  97    QEMU_GENERIC(x,                                  \
  98                 (QemuLockable *, (x)),              \
  99                 QEMU_MAKE_LOCKABLE_(x))
 100
 101static inline void qemu_lockable_lock(QemuLockable *x)
 102{
 103    x->lock(x->object);
 104}
 105
 106static inline void qemu_lockable_unlock(QemuLockable *x)
 107{
 108    x->unlock(x->object);
 109}
 110
 111static inline QemuLockable *qemu_lockable_auto_lock(QemuLockable *x)
 112{
 113    qemu_lockable_lock(x);
 114    return x;
 115}
 116
 117static inline void qemu_lockable_auto_unlock(QemuLockable *x)
 118{
 119    if (x) {
 120        qemu_lockable_unlock(x);
 121    }
 122}
 123
 124G_DEFINE_AUTOPTR_CLEANUP_FUNC(QemuLockable, qemu_lockable_auto_unlock)
 125
 126#define WITH_QEMU_LOCK_GUARD_(x, var) \
 127    for (g_autoptr(QemuLockable) var = \
 128                qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE_NONNULL((x))); \
 129         var; \
 130         qemu_lockable_auto_unlock(var), var = NULL)
 131
 132/**
 133 * WITH_QEMU_LOCK_GUARD - Lock a lock object for scope
 134 *
 135 * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin).
 136 *
 137 * This macro defines a lock scope such that entering the scope takes the lock
 138 * and leaving the scope releases the lock.  Return statements are allowed
 139 * within the scope and release the lock.  Break and continue statements leave
 140 * the scope early and release the lock.
 141 *
 142 *   WITH_QEMU_LOCK_GUARD(&mutex) {
 143 *       ...
 144 *       if (error) {
 145 *           return; <-- mutex is automatically unlocked
 146 *       }
 147 *
 148 *       if (early_exit) {
 149 *           break;  <-- leave this scope early
 150 *       }
 151 *       ...
 152 *   }
 153 */
 154#define WITH_QEMU_LOCK_GUARD(x) \
 155    WITH_QEMU_LOCK_GUARD_((x), glue(qemu_lockable_auto, __COUNTER__))
 156
 157/**
 158 * QEMU_LOCK_GUARD - Lock an object until the end of the scope
 159 *
 160 * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin).
 161 *
 162 * This macro takes a lock until the end of the scope.  Return statements
 163 * release the lock.
 164 *
 165 *   ... <-- mutex not locked
 166 *   QEMU_LOCK_GUARD(&mutex); <-- mutex locked from here onwards
 167 *   ...
 168 *   if (error) {
 169 *       return; <-- mutex is automatically unlocked
 170 *   }
 171 */
 172#define QEMU_LOCK_GUARD(x)                                       \
 173    g_autoptr(QemuLockable)                                      \
 174    glue(qemu_lockable_auto, __COUNTER__) G_GNUC_UNUSED =        \
 175            qemu_lockable_auto_lock(QEMU_MAKE_LOCKABLE((x)))
 176
 177#endif
 178