mirror of
https://github.com/qemu/qemu.git
synced 2024-12-05 17:53:36 +08:00
7c9b2bf677
QemuEvents are used heavily by call_rcu. We do not want them to be slow, but the current implementation does a kernel call on every invocation of qemu_event_* and won't cut it. So, wrap a Win32 manual-reset event with a fast userspace path. The states and transitions are the same as for the futex and mutex/condvar implementations, but the slow path is different of course. The idea is to reset the Win32 event lazily, as part of a test-reset-test-wait sequence. Such a sequence is, indeed, how QemuEvents are used by RCU and other subsystems! The patch includes a formal model of the algorithm. Tested-by: Stefan Weil <sw@weilnetz.de> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Signed-off-by: Stefan Weil <sw@weilnetz.de>
99 lines
3.7 KiB
Promela
99 lines
3.7 KiB
Promela
/*
|
|
* This model describes the implementation of QemuEvent in
|
|
* util/qemu-thread-win32.c.
|
|
*
|
|
* Author: Paolo Bonzini <pbonzini@redhat.com>
|
|
*
|
|
* This file is in the public domain. If you really want a license,
|
|
* the WTFPL will do.
|
|
*
|
|
* To verify it:
|
|
* spin -a docs/event.promela
|
|
* gcc -O2 pan.c -DSAFETY
|
|
* ./a.out
|
|
*/
|
|
|
|
bool event;
|
|
int value;
|
|
|
|
/* Primitives for a Win32 event */
|
|
#define RAW_RESET event = false
|
|
#define RAW_SET event = true
|
|
#define RAW_WAIT do :: event -> break; od
|
|
|
|
#if 0
|
|
/* Basic sanity checking: test the Win32 event primitives */
|
|
#define RESET RAW_RESET
|
|
#define SET RAW_SET
|
|
#define WAIT RAW_WAIT
|
|
#else
|
|
/* Full model: layer a userspace-only fast path on top of the RAW_*
|
|
* primitives. SET/RESET/WAIT have exactly the same semantics as
|
|
* RAW_SET/RAW_RESET/RAW_WAIT, but try to avoid invoking them.
|
|
*/
|
|
#define EV_SET 0
|
|
#define EV_FREE 1
|
|
#define EV_BUSY -1
|
|
|
|
int state = EV_FREE;
|
|
|
|
int xchg_result;
|
|
#define SET if :: state != EV_SET -> \
|
|
atomic { /* xchg_result=xchg(state, EV_SET) */ \
|
|
xchg_result = state; \
|
|
state = EV_SET; \
|
|
} \
|
|
if :: xchg_result == EV_BUSY -> RAW_SET; \
|
|
:: else -> skip; \
|
|
fi; \
|
|
:: else -> skip; \
|
|
fi
|
|
|
|
#define RESET if :: state == EV_SET -> atomic { state = state | EV_FREE; } \
|
|
:: else -> skip; \
|
|
fi
|
|
|
|
int tmp1, tmp2;
|
|
#define WAIT tmp1 = state; \
|
|
if :: tmp1 != EV_SET -> \
|
|
if :: tmp1 == EV_FREE -> \
|
|
RAW_RESET; \
|
|
atomic { /* tmp2=cas(state, EV_FREE, EV_BUSY) */ \
|
|
tmp2 = state; \
|
|
if :: tmp2 == EV_FREE -> state = EV_BUSY; \
|
|
:: else -> skip; \
|
|
fi; \
|
|
} \
|
|
if :: tmp2 == EV_SET -> tmp1 = EV_SET; \
|
|
:: else -> tmp1 = EV_BUSY; \
|
|
fi; \
|
|
:: else -> skip; \
|
|
fi; \
|
|
assert(tmp1 != EV_FREE); \
|
|
if :: tmp1 == EV_BUSY -> RAW_WAIT; \
|
|
:: else -> skip; \
|
|
fi; \
|
|
:: else -> skip; \
|
|
fi
|
|
#endif
|
|
|
|
active proctype waiter()
|
|
{
|
|
if
|
|
:: !value ->
|
|
RESET;
|
|
if
|
|
:: !value -> WAIT;
|
|
:: else -> skip;
|
|
fi;
|
|
:: else -> skip;
|
|
fi;
|
|
assert(value);
|
|
}
|
|
|
|
active proctype notifier()
|
|
{
|
|
value = true;
|
|
SET;
|
|
}
|