From patchwork Sun Sep 22 08:11:18 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: pingfan liu X-Patchwork-Id: 276949 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 4E39D2C0079 for ; Sun, 22 Sep 2013 18:13:56 +1000 (EST) Received: from localhost ([::1]:34330 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VNeni-0004E8-4P for incoming@patchwork.ozlabs.org; Sun, 22 Sep 2013 04:13:54 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:50344) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VNels-0001Bp-AN for qemu-devel@nongnu.org; Sun, 22 Sep 2013 04:12:08 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1VNeli-0006r4-Tf for qemu-devel@nongnu.org; Sun, 22 Sep 2013 04:12:00 -0400 Received: from mail-pd0-x231.google.com ([2607:f8b0:400e:c02::231]:62399) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VNeli-0006qx-I1 for qemu-devel@nongnu.org; Sun, 22 Sep 2013 04:11:50 -0400 Received: by mail-pd0-f177.google.com with SMTP id y10so2033949pdj.22 for ; Sun, 22 Sep 2013 01:11:49 -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=f8CBE9VtC4hc4RoLrDkck1rtAmtLjxq798IA/mQfJDE=; b=bKdq13rNW5Wk4HyggHKy3HlRihNS3+KUOfGAYbO1PQuFPCv4b72Lfv87/03xl4CY71 oBciwch0M8xnNu1hczovZXu9b8kfBoMbW4E6uhxU7oC0PxbHlBgG7m14Ibr87EkZD+qQ /79Po3C/AUqaE2oGf+BgPqZqXn8ftigdBWn/GvO2Yg2c/MdSZPypNEzmQA3W/1T0o3su hwreQOxDl2CaUDT0ZC62s8dzNkchnbYTIJfWw3b/Sp6hQ+Hxv+WyZ/OP3EnQCNsWluVB B6WYmxttVonAtgRUBLiRWag4vCNe2xJAi/z7WFnrC4oW2IvuL34LgDiP8LeIU9mP+97H DfrA== X-Received: by 10.66.25.70 with SMTP id a6mr18072490pag.68.1379837509682; Sun, 22 Sep 2013 01:11:49 -0700 (PDT) Received: from localhost ([202.108.130.138]) by mx.google.com with ESMTPSA id xv2sm26407376pbb.39.1969.12.31.16.00.00 (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Sun, 22 Sep 2013 01:11:48 -0700 (PDT) From: Liu Ping Fan To: qemu-devel@nongnu.org Date: Sun, 22 Sep 2013 16:11:18 +0800 Message-Id: <1379837479-8419-5-git-send-email-pingfank@linux.vnet.ibm.com> X-Mailer: git-send-email 1.8.1.4 In-Reply-To: <1379837479-8419-1-git-send-email-pingfank@linux.vnet.ibm.com> References: <1379837479-8419-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:400e:c02::231 Cc: Kevin Wolf , Paolo Bonzini , Alex Bligh , Stefan Hajnoczi , Jan Kiszka Subject: [Qemu-devel] [PATCH v4 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 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 *);