On Mon, Aug 15, 2011 at 9:08 PM, Paolo Bonzini <pbonz...@redhat.com> wrote: > This emulates Win32 manual-reset events using futexes or conditional > variables. Typical ways to use them are with multi-producer, > single-consumer data structures, to test for a complex condition whose > elements come from different threads: > > for (;;) { > qemu_event_reset(ev); > ... test complex condition ... > if (condition is true) { > break; > } > qemu_event_wait(ev); > } > > Alternatively: > > ... compute condition ... > if (condition) { > do { > qemu_event_wait(ev); > qemu_event_reset(ev); > ... compute condition ... > } while(condition); > qemu_event_set(ev); > } > > QemuEvent provides a very fast userspace path in the common case when > no other thread is waiting, or the event is not changing state. It > is used to report RCU quiescent states to the thread calling > synchronize_rcu (the latter being the single consumer), and to report > call_rcu invocations to the thread that receives them. > > Signed-off-by: Paolo Bonzini <pbonz...@redhat.com> > --- > qemu-thread-posix.c | 124 > +++++++++++++++++++++++++++++++++++++++++++++++++++ > qemu-thread-posix.h | 8 +++ > qemu-thread-win32.c | 26 +++++++++++ > qemu-thread-win32.h | 4 ++ > qemu-thread.h | 8 +++ > 5 files changed, 170 insertions(+), 0 deletions(-) > > diff --git a/qemu-thread-posix.c b/qemu-thread-posix.c > index 2bd02ef..50e7421 100644 > --- a/qemu-thread-posix.c > +++ b/qemu-thread-posix.c > @@ -17,7 +17,10 @@ > #include <signal.h> > #include <stdint.h> > #include <string.h> > +#include <limits.h> > +#include <unistd.h> > #include "qemu-thread.h" > +#include "qemu-barrier.h" > > static void error_exit(int err, const char *msg) > { > @@ -115,6 +118,127 @@ void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex) > error_exit(err, __func__); > } > > +#ifdef __linux__ > +#include <sys/syscall.h>
futex manual page says #include <linux/futex.h> #include <sys/time.h> Maybe the compatibility stuff below belongs to linux-headers. > +#ifndef FUTEX_WAIT > +#define FUTEX_WAIT 0 > +#endif > +#ifndef FUTEX_WAKE > +#define FUTEX_WAKE 1 > +#endif > + > +#define futex(...) syscall(__NR_futex, __VA_ARGS__) > + > +static inline void futex_wake(QemuEvent *ev, int n) > +{ > + futex(ev, FUTEX_WAKE, n, NULL, NULL, 0); > +} > + > +static inline void futex_wait(QemuEvent *ev, unsigned val) > +{ > + futex(ev, FUTEX_WAIT, (int) val, NULL, NULL, 0); > +} > +#else > +static inline void futex_wake(QemuEvent *ev, int n) > +{ > + if (n == 1) Missing braces, please use checkpatch.pl. > + pthread_cond_signal(&ev->cond); > + else > + pthread_cond_broadcast(&ev->cond); > +} > + > +static inline void futex_wait(QemuEvent *ev, unsigned val) > +{ > + pthread_mutex_lock(&ev->lock); > + if (ev->value == val) > + pthread_cond_wait(&ev->cond, &ev->lock); > + pthread_mutex_unlock(&ev->lock); > +} > +#endif > + > +/* Bit 0 is 1 if there are no waiters. Bit 1 is 1 if the event is set. > + * The combination "event_set && event_has_waiters" is impossible. */ > +#define EV_FREE_BIT 1 > +#define EV_SET_BIT 2 > + > +#define EV_BUSY 0 > +#define EV_FREE 1 > +#define EV_SET 3 > + > +void qemu_event_init(QemuEvent *ev, bool init) > +{ > +#ifndef __linux__ > + pthread_mutex_init(&ev->lock, NULL); > + pthread_cond_init(&ev->cond, NULL); > +#endif > + > + ev->value = (init ? EV_SET : EV_FREE); > +} > + > +void qemu_event_destroy(QemuEvent *ev) > +{ > +#ifndef __linux__ > + pthread_mutex_destroy(&ev->lock); > + pthread_cond_destroy(&ev->cond); > +#endif > +} > + > +void qemu_event_set(QemuEvent *ev) > +{ > + unsigned value; > + > + smp_mb(); > + value = ev->value; > + if (value == EV_SET) { > + /* Exit on a pre-existing/concurrent set. */ > + smp_mb(); > + } else { > + if (__sync_fetch_and_or(&ev->value, EV_SET) == EV_BUSY) { > + /* There were waiters, wake them up. */ > + futex_wake(ev, INT_MAX); > + } > + } > +} > + > +void qemu_event_reset(QemuEvent *ev) > +{ > + unsigned value; > + > + smp_mb(); > + value = ev->value; > + if (value != EV_SET) { > + /* Exit on a pre-existing reset. */ > + smp_mb(); > + } else { > + /* If there was a concurrent reset (or even reset+wait), > + * do nothing. Otherwise change EV_SET->EV_FREE. */ > + __sync_fetch_and_and(&ev->value, ~EV_SET_BIT); > + } > +} > + > +void qemu_event_wait(QemuEvent *ev) > +{ > + unsigned value, old; > + > + smp_mb(); > + value = ev->value; > + if (value == EV_SET) { > + smp_mb(); > + } else { > + if (value == EV_FREE) { > + /* Leave the event reset and tell qemu_event_set that there > + * are waiters. No need to retry, because there cannot be > + * a concurent busy->free transition. After the CAS, the > + * event will be either set or busy. */ > + old = __sync_val_compare_and_swap(&ev->value, EV_FREE, EV_BUSY); > + if (old == EV_SET) { > + return; > + } > + } > + futex_wait(ev, EV_BUSY); > + } > +} > + > void qemu_thread_create(QemuThread *thread, > void *(*start_routine)(void*), > void *arg) > diff --git a/qemu-thread-posix.h b/qemu-thread-posix.h > index ee4618e..2f5b63d 100644 > --- a/qemu-thread-posix.h > +++ b/qemu-thread-posix.h > @@ -10,6 +10,14 @@ struct QemuCond { > pthread_cond_t cond; > }; > > +struct QemuEvent { > +#ifndef __linux__ > + pthread_mutex_t lock; > + pthread_cond_t cond; > +#endif > + unsigned value; > +}; > + > struct QemuThread { > pthread_t thread; > }; > diff --git a/qemu-thread-win32.c b/qemu-thread-win32.c > index 2d2d5ab..9bdbb48 100644 > --- a/qemu-thread-win32.c > +++ b/qemu-thread-win32.c > @@ -192,6 +192,32 @@ void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex) > qemu_mutex_lock(mutex); > } > > +void qemu_event_init(QemuEvent *ev, bool init) > +{ > + /* Manual reset. */ > + ev->event = CreateEvent(NULL, TRUE, init, NULL); > +} > + > +void qemu_event_destroy(QemuEvent *mutex) > +{ > + CloseHandle(ev->event); > +} > + > +void qemu_event_set(QemuEvent *ev) > +{ > + SetEvent(ev->event); > +} > + > +void qemu_event_reset(QemuEvent *ev) > +{ > + ResetEvent(ev->event); > +} > + > +void qemu_event_wait(QemuEvent *ev) > +{ > + WaitForSingleObject(ev->event, INFINITE); > +} > + > struct QemuThreadData { > QemuThread *thread; > void *(*start_routine)(void *); > diff --git a/qemu-thread-win32.h b/qemu-thread-win32.h > index 878f86a..ddd6d0f 100644 > --- a/qemu-thread-win32.h > +++ b/qemu-thread-win32.h > @@ -13,6 +13,10 @@ struct QemuCond { > HANDLE continue_event; > }; > > +struct QemuEvent { > + HANDLE event; > +}; > + > struct QemuThread { > HANDLE thread; > void *ret; > diff --git a/qemu-thread.h b/qemu-thread.h > index 0a73d50..8353e3d 100644 > --- a/qemu-thread.h > +++ b/qemu-thread.h > @@ -2,9 +2,11 @@ > #define __QEMU_THREAD_H 1 > > #include <inttypes.h> > +#include <stdbool.h> > > typedef struct QemuMutex QemuMutex; > typedef struct QemuCond QemuCond; > +typedef struct QemuEvent QemuEvent; > typedef struct QemuThread QemuThread; > > #ifdef _WIN32 > @@ -31,6 +33,12 @@ void qemu_cond_signal(QemuCond *cond); > void qemu_cond_broadcast(QemuCond *cond); > void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex); > > +void qemu_event_init(QemuEvent *ev, bool init); > +void qemu_event_set(QemuEvent *ev); > +void qemu_event_reset(QemuEvent *ev); > +void qemu_event_wait(QemuEvent *ev); > +void qemu_event_destroy(QemuEvent *ev); > + > void qemu_thread_create(QemuThread *thread, > void *(*start_routine)(void*), > void *arg); > -- > 1.7.6 > > > >