From patchwork Tue Aug 27 03:21:02 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: pingfan liu X-Patchwork-Id: 270013 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 8814E2C00A4 for ; Tue, 27 Aug 2013 13:24:38 +1000 (EST) Received: from localhost ([::1]:53870 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VE9tU-0003Wh-FV for incoming@patchwork.ozlabs.org; Mon, 26 Aug 2013 23:24:36 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:50464) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VE9sv-0003A2-QY for qemu-devel@nongnu.org; Mon, 26 Aug 2013 23:24:10 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1VE9sn-0006aF-Cg for qemu-devel@nongnu.org; Mon, 26 Aug 2013 23:24:01 -0400 Received: from mail-oa0-x22d.google.com ([2607:f8b0:4003:c02::22d]:52210) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VE9sm-0006a8-VH for qemu-devel@nongnu.org; Mon, 26 Aug 2013 23:23:53 -0400 Received: by mail-oa0-f45.google.com with SMTP id m1so1692572oag.18 for ; Mon, 26 Aug 2013 20:23:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=HUfOLjXexJ2zOtc3FiVavDwXvfgVVvJtwN3OqPF0U7o=; b=bypBHrA5e6FR6F7yKXr36nXB1P6ukMMHlkwb8Cw6FA0ijjt19Qr8Kch1yDozQkDiE1 sc9Q7CrpP/rhnadhvdie0L6XgFk6kecM8SjRhOkIl/BBZVyAi2qv+w75F5PWsli7gLDt yzxRmeLXfwBa73McOpWPYnADHUt0n+zPVukLXZ56gq3x1z0WgrHLV7d7rKmMYk1RRSQH fKC69mav5DrmEesQxhQyJJJkFPG6LEt4Awi6UHUMXMarCCbrqmyLuEtJz8wAO4Yh1Qgj GmnObz9wgRUm+W2ZbBhlo86cZP0Ag7ssGOyP5+PWcWEnmh/e769jqG76TN9Z5QU9bLWz UwfA== X-Received: by 10.182.226.199 with SMTP id ru7mr180928obc.12.1377573832469; Mon, 26 Aug 2013 20:23:52 -0700 (PDT) Received: from localhost ([202.108.130.138]) by mx.google.com with ESMTPSA id ya5sm17905745obc.1.1969.12.31.16.00.00 (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Mon, 26 Aug 2013 20:23:51 -0700 (PDT) From: Liu Ping Fan To: qemu-devel@nongnu.org Date: Tue, 27 Aug 2013 11:21:02 +0800 Message-Id: <1377573663-16727-4-git-send-email-pingfank@linux.vnet.ibm.com> X-Mailer: git-send-email 1.8.1.4 In-Reply-To: <1377573663-16727-1-git-send-email-pingfank@linux.vnet.ibm.com> References: <1377573663-16727-1-git-send-email-pingfank@linux.vnet.ibm.com> X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2607:f8b0:4003:c02::22d Cc: Kevin Wolf , Paolo Bonzini , Alex Bligh , Stefan Hajnoczi , Jan Kiszka Subject: [Qemu-devel] [PATCH v3 3/4] qemu-thread: add QemuEvent X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Paolo Bonzini 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); } Or more efficiently (but with some duplication): ... evaluate condition ... while (!condition) { qemu_event_reset(ev); ... evaluate condition ... if (!condition) { qemu_event_wait(ev); ... evaluate condition ... } } 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 --- include/qemu/thread-posix.h | 8 +++ include/qemu/thread-win32.h | 4 ++ include/qemu/thread.h | 7 +++ util/qemu-thread-posix.c | 116 ++++++++++++++++++++++++++++++++++++++++++++ util/qemu-thread-win32.c | 26 ++++++++++ 5 files changed, 161 insertions(+) diff --git a/include/qemu/thread-posix.h b/include/qemu/thread-posix.h index 361566a..eb5c7a1 100644 --- a/include/qemu/thread-posix.h +++ b/include/qemu/thread-posix.h @@ -21,6 +21,14 @@ struct QemuSemaphore { #endif }; +struct QemuEvent { +#ifndef __linux__ + pthread_mutex_t lock; + pthread_cond_t cond; +#endif + unsigned value; +}; + struct QemuThread { pthread_t thread; }; diff --git a/include/qemu/thread-win32.h b/include/qemu/thread-win32.h index 13adb95..3d58081 100644 --- a/include/qemu/thread-win32.h +++ b/include/qemu/thread-win32.h @@ -17,6 +17,10 @@ struct QemuSemaphore { HANDLE sema; }; +struct QemuEvent { + HANDLE event; +}; + typedef struct QemuThreadData QemuThreadData; struct QemuThread { QemuThreadData *data; diff --git a/include/qemu/thread.h b/include/qemu/thread.h index c02404b..3e32c65 100644 --- a/include/qemu/thread.h +++ b/include/qemu/thread.h @@ -7,6 +7,7 @@ typedef struct QemuMutex QemuMutex; typedef struct QemuCond QemuCond; typedef struct QemuSemaphore QemuSemaphore; +typedef struct QemuEvent QemuEvent; typedef struct QemuThread QemuThread; #ifdef _WIN32 @@ -45,6 +46,12 @@ void qemu_sem_wait(QemuSemaphore *sem); int qemu_sem_timedwait(QemuSemaphore *sem, int ms); void qemu_sem_destroy(QemuSemaphore *sem); +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, int mode); diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index 4de133e..37dd298 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -20,7 +20,12 @@ #include #include #include +#ifdef __linux__ +#include +#include +#endif #include "qemu/thread.h" +#include "qemu/atomic.h" static void error_exit(int err, const char *msg) { @@ -272,6 +277,117 @@ void qemu_sem_wait(QemuSemaphore *sem) #endif } +#ifdef __linux__ +#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) { + 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 + +/* Valid transitions: + * - free->set, when setting the event + * - busy->set, when setting the event, followed by futex_wake + * - set->free, when resetting the event + * - free->busy, when waiting + * + * set->busy does not happen (it can be observed from the outside but + * it really is set->free->busy). + * + * busy->free provably cannot happen; to enforce it, the set->free transition + * is done with an OR, which becomes a no-op if the event has concurrently + * transitioned to free or busy. + */ + +#define EV_SET 0 +#define EV_FREE 1 +#define EV_BUSY -1 + +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) +{ + if (atomic_mb_read(&ev->value) != EV_SET) { + if (atomic_xchg(&ev->value, EV_SET) == EV_BUSY) { + /* There were waiters, wake them up. */ + futex_wake(ev, INT_MAX); + } + } +} + +void qemu_event_reset(QemuEvent *ev) +{ + if (atomic_mb_read(&ev->value) == EV_SET) { + /* + * If there was a concurrent reset (or even reset+wait), + * do nothing. Otherwise change EV_SET->EV_FREE. + */ + atomic_or(&ev->value, EV_FREE); + } +} + +void qemu_event_wait(QemuEvent *ev) +{ + unsigned value; + + value = atomic_mb_read(&ev->value); + if (value != EV_SET) { + 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. + */ + if (atomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) { + return; + } + } + futex_wait(ev, EV_BUSY); + } +} + + void qemu_thread_create(QemuThread *thread, void *(*start_routine)(void*), void *arg, int mode) diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c index 517878d..27a5217 100644 --- a/util/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -227,6 +227,32 @@ void qemu_sem_wait(QemuSemaphore *sem) } } +void qemu_event_init(QemuEvent *ev, bool init) +{ + /* Manual reset. */ + ev->event = CreateEvent(NULL, TRUE, init, NULL); +} + +void qemu_event_destroy(QemuEvent *ev) +{ + 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 { /* Passed to win32_start_routine. */ void *(*start_routine)(void *);