QemuLockable is a polymorphic lock type that takes an object and knows which function to use for locking and unlocking. The implementation could use C11 _Generic, but since the support is not very widespread I am instead using __builtin_choose_expr and __builtin_types_compatible_p, which are already used by include/qemu/atomic.h. QemuLockable can be used to implement lock guards, or to pass around a lock in such a way that a function can release it and re-acquire it. The next patch will do this for CoQueue. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Message-Id: <20180203153935.8056-3-pbonzini@redhat.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Reviewed-by: Fam Zheng <famz@redhat.com> Signed-off-by: Fam Zheng <famz@redhat.com>
		
			
				
	
	
		
			97 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			97 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Polymorphic locking functions (aka poor man templates)
 | 
						|
 *
 | 
						|
 * Copyright Red Hat, Inc. 2017, 2018
 | 
						|
 *
 | 
						|
 * Author: Paolo Bonzini <pbonzini@redhat.com>
 | 
						|
 *
 | 
						|
 * This work is licensed under the terms of the GNU LGPL, version 2 or later.
 | 
						|
 * See the COPYING.LIB file in the top-level directory.
 | 
						|
 *
 | 
						|
 */
 | 
						|
 | 
						|
#ifndef QEMU_LOCKABLE_H
 | 
						|
#define QEMU_LOCKABLE_H
 | 
						|
 | 
						|
#include "qemu/coroutine.h"
 | 
						|
#include "qemu/thread.h"
 | 
						|
 | 
						|
typedef void QemuLockUnlockFunc(void *);
 | 
						|
 | 
						|
struct QemuLockable {
 | 
						|
    void *object;
 | 
						|
    QemuLockUnlockFunc *lock;
 | 
						|
    QemuLockUnlockFunc *unlock;
 | 
						|
};
 | 
						|
 | 
						|
/* This function gives an error if an invalid, non-NULL pointer type is passed
 | 
						|
 * to QEMU_MAKE_LOCKABLE.  For optimized builds, we can rely on dead-code elimination
 | 
						|
 * from the compiler, and give the errors already at link time.
 | 
						|
 */
 | 
						|
#ifdef __OPTIMIZE__
 | 
						|
void unknown_lock_type(void *);
 | 
						|
#else
 | 
						|
static inline void unknown_lock_type(void *unused)
 | 
						|
{
 | 
						|
    abort();
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
static inline __attribute__((__always_inline__)) QemuLockable *
 | 
						|
qemu_make_lockable(void *x, QemuLockable *lockable)
 | 
						|
{
 | 
						|
    /* We cannot test this in a macro, otherwise we get compiler
 | 
						|
     * warnings like "the address of 'm' will always evaluate as 'true'".
 | 
						|
     */
 | 
						|
    return x ? lockable : NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* Auxiliary macros to simplify QEMU_MAKE_LOCABLE.  */
 | 
						|
#define QEMU_LOCK_FUNC(x) ((QemuLockUnlockFunc *)    \
 | 
						|
    QEMU_GENERIC(x,                                  \
 | 
						|
                 (QemuMutex *, qemu_mutex_lock),     \
 | 
						|
                 (CoMutex *, qemu_co_mutex_lock),    \
 | 
						|
                 (QemuSpin *, qemu_spin_lock),       \
 | 
						|
                 unknown_lock_type))
 | 
						|
 | 
						|
#define QEMU_UNLOCK_FUNC(x) ((QemuLockUnlockFunc *)  \
 | 
						|
    QEMU_GENERIC(x,                                  \
 | 
						|
                 (QemuMutex *, qemu_mutex_unlock),   \
 | 
						|
                 (CoMutex *, qemu_co_mutex_unlock),  \
 | 
						|
                 (QemuSpin *, qemu_spin_unlock),     \
 | 
						|
                 unknown_lock_type))
 | 
						|
 | 
						|
/* In C, compound literals have the lifetime of an automatic variable.
 | 
						|
 * In C++ it would be different, but then C++ wouldn't need QemuLockable
 | 
						|
 * either...
 | 
						|
 */
 | 
						|
#define QEMU_MAKE_LOCKABLE_(x) qemu_make_lockable((x), &(QemuLockable) {    \
 | 
						|
        .object = (x),                               \
 | 
						|
        .lock = QEMU_LOCK_FUNC(x),                   \
 | 
						|
        .unlock = QEMU_UNLOCK_FUNC(x),               \
 | 
						|
    })
 | 
						|
 | 
						|
/* QEMU_MAKE_LOCKABLE - Make a polymorphic QemuLockable
 | 
						|
 *
 | 
						|
 * @x: a lock object (currently one of QemuMutex, CoMutex, QemuSpin).
 | 
						|
 *
 | 
						|
 * Returns a QemuLockable object that can be passed around
 | 
						|
 * to a function that can operate with locks of any kind.
 | 
						|
 */
 | 
						|
#define QEMU_MAKE_LOCKABLE(x)                        \
 | 
						|
    QEMU_GENERIC(x,                                  \
 | 
						|
                 (QemuLockable *, (x)),              \
 | 
						|
                 QEMU_MAKE_LOCKABLE_(x))
 | 
						|
 | 
						|
static inline void qemu_lockable_lock(QemuLockable *x)
 | 
						|
{
 | 
						|
    x->lock(x->object);
 | 
						|
}
 | 
						|
 | 
						|
static inline void qemu_lockable_unlock(QemuLockable *x)
 | 
						|
{
 | 
						|
    x->unlock(x->object);
 | 
						|
}
 | 
						|
 | 
						|
#endif
 |