qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [RFC PATCH 03/13] qemu-threads: add QemuEvent


From: Blue Swirl
Subject: Re: [Qemu-devel] [RFC PATCH 03/13] qemu-threads: add QemuEvent
Date: Wed, 17 Aug 2011 17:09:55 +0000

On Mon, Aug 15, 2011 at 9:08 PM, Paolo Bonzini <address@hidden> 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 <address@hidden>
> ---
>  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
>
>
>
>



reply via email to

[Prev in Thread] Current Thread [Next in Thread]