|
Hi all,
I would like if possible a review of this approach to win32 condvars.
It is based on the ACE implementation (http://www.cs.wustl.edu/
~schmidt/win32-cv-1.html) but I tried to get rid of most concurrent
accesses to the "number of waiters" variable and of the critical
section protecting it. To achieve this I made the following changes:
* I force the mutex to be held during signal and broadcast.
* I make signal synchronous (so, more expensive) as a tradeoff for
making broadcast cheaper.
Together, this two conditions ensure that the thread with the mutex
protects the signaled thread(s) against other threads.
Any observation is welcome!
#include <windows.h>
#include <limits.h>
typedef CRITICAL_SECTION QemuMutex;
static inline void qemu_mutex_lock(QemuMutex *mutex) {
EnterCriticalSection(mutex);
}
static inline void qemu_mutex_unlock(QemuMutex *mutex) {
LeaveCriticalSection(mutex);
}
typedef struct QemuCond QemuCond;
void qemu_cond_init(QemuCond *cond);
void qemu_cond_destroy(QemuCond *cond);
void qemu_cond_signal(QemuCond *cond);
void qemu_cond_broadcast(QemuCond *cond);
void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex);
struct QemuCond {
LONG waiters, target;
HANDLE sema;
HANDLE continue_event;
};
void qemu_cond_init(QemuCond *cond)
{
memset(cond, 0, sizeof(*cond));
cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
cond->continue_event = CreateEvent(NULL, /* security */
FALSE, /* auto-reset */
FALSE, /* not signaled */
NULL); /* name */
}
void qemu_cond_signal(QemuCond *cond)
{
DWORD result;
/*
* Signal only when there are waiters. cond->waiters is
* incremented by pthread_cond_wait under the external lock,
* so we are safe about that.
*/
if (cond->waiters == 0) {
return;
}
/*
* Waiting threads decrement it outside the external lock,
* but only if another thread is signaling/broadcasting and
* has the mutex. So, it also cannot be decremented
* concurrently with this particular access.
*/
cond->target = cond->waiters - 1;
result = SignalObjectAndWait(cond->sema, cond->continue_event,
INFINITE, FALSE);
}
void qemu_cond_broadcast(QemuCond *cond)
{
BOOLEAN result;
if (cond->waiters == 0) {
return;
}
cond->target = 0;
result = ReleaseSemaphore(cond->sema, cond->waiters, NULL);
WaitForSingleObject(cond->continue_event, INFINITE);
}
void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex)
{
/*
* This access is protected under the mutex.
*/
cond->waiters++;
qemu_mutex_unlock(mutex);
WaitForSingleObject(cond->sema, INFINITE);
/*
* Decrease waiters count. The mutex is not taken, so we have
* to do this atomically.
*
* All waiters contend for the mutex at the end of this function
* until the signaling thread relinquishes it. To ensure
* each waiter consumes exactly one slice of the semaphore,
* the signaling thread stops until it is told by the last
* waiter that it can go on.
*/
if (InterlockedDecrement(&cond->waiters) == cond->target) {
SetEvent(cond->continue_event);
}
qemu_mutex_lock(mutex);
}
|
|
0
|
|
|
Reply
|
bonzini
|
2/15/2011 1:29:55 PM
|
Header
|
Report
as Spam
|
|
Paolo Bonzini <bonzini@gnu.org> writes:
> I would like if possible a review of this approach to win32 condvars.
> void qemu_cond_signal(QemuCond *cond)
> {
> DWORD result;
> if (cond->waiters == 0) {
> return;
> }
> cond->target = cond->waiters - 1;
> result = SignalObjectAndWait(cond->sema, cond->continue_event,
> INFINITE, FALSE);
> }
>
> void qemu_cond_broadcast(QemuCond *cond)
> {
> BOOLEAN result;
> if (cond->waiters == 0) {
> return;
> }
>
> cond->target = 0;
> result = ReleaseSemaphore(cond->sema, cond->waiters, NULL);
> WaitForSingleObject(cond->continue_event, INFINITE);
> }
>
> void qemu_cond_wait(QemuCond *cond, QemuMutex *mutex)
> {
> cond->waiters++;
> qemu_mutex_unlock(mutex);
> WaitForSingleObject(cond->sema, INFINITE);
>
> if (InterlockedDecrement(&cond->waiters) == cond->target) {
> SetEvent(cond->continue_event);
> }
>
> qemu_mutex_lock(mutex);
> }
Consider:
1. Thread A calls qemu_cond_wait, sets cond->waiters==1 and blocks in
WaitForSingleObject
2. Thread B calls qemu_cond_signal, sees cond->waiters==1, sets
cond->target==0, triggers the semaphore and waits for
cond->continue_event
3. Thread C calls qemu_cond_wait, sets cond->waiters==2 and blocks in
WaitForSingleObject
Now we have a problem. There are two possible outcomes here, both of
which are problematic.
4a. Thread C is now woken, since the semaphore is signalled [Stolen
wakeup]
4b.Thread C stays sleeping, but thread B is woken as expected. Thread B
decrements cond->waiters to 1, but sees cond->target==0, so does not
signal cond->continue_event [Signalling thread does not return]
Why not just use the implementation from boost?
Anthony
--
Author of C++ Concurrency in Action http://www.stdthread.co.uk/book/
just::thread C++0x thread library http://www.stdthread.co.uk
Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk
15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976
|
|
0
|
|
|
Reply
|
Anthony
|
2/15/2011 3:51:19 PM
|
Header
|
Report
as Spam
|
|
Anthony Williams <anthony.ajw@gmail.com> writes:
> Consider:
>
> 1. Thread A calls qemu_cond_wait, sets cond->waiters==1 and blocks in
> WaitForSingleObject
>
> 2. Thread B calls qemu_cond_signal, sees cond->waiters==1, sets
> cond->target==0, triggers the semaphore and waits for
> cond->continue_event
>
> 3. Thread C calls qemu_cond_wait, sets cond->waiters==2 and blocks in
> WaitForSingleObject
This can't happen if the mutex is held across the signal call, as per
Paolo's original post.
Anthony
--
Author of C++ Concurrency in Action http://www.stdthread.co.uk/book/
just::thread C++0x thread library http://www.stdthread.co.uk
Just Software Solutions Ltd http://www.justsoftwaresolutions.co.uk
15 Carrallack Mews, St Just, Cornwall, TR19 7UL, UK. Company No. 5478976
|
|
0
|
|
|
Reply
|
Anthony
|
2/16/2011 12:43:59 PM
|
Header
|
Report
as Spam
|
|
2 Replies
305 Views
Similiar Articles: Thread-safe Queue on Win32 - comp.programming.threadsYet another is to wake a thread if the count of waiting threads ... Thread-safe Queue on Win32 - comp.programming.threads Is this implementation of thread-safe queue correct? ... Set implementation in STL - comp.lang.c++.moderatedYet another binary search tree library - comp.lang.c > >> of the ... Thread-safe Queue on Win32 - comp.programming.threads Is this implementation of thread-safe queue correct? ... A container library for C - comp.compilers.lcchttp://www.cs.virginia.edu/~lcc-win32/container.html ... (2) The source code of the sample implementation ... Yet another binary search tree library - comp.lang.c ... Poco VS Boost - comp.os.ms-windows.programmer.win32Yet, the Poco library offers a good and easy way to ... So if you don't need another library, stick to boost. ... one have any experience with using Poco library implementation ... boost threads: notify_one vs. notify_all - comp.lang.c++.moderated ...... the mutex (if only to wait on the condvar again) what allows another ... can't speak for POSIX condvars but the implementation ... IIRC, both boost.thread and pthreads-win32 ... Is there a GUI Magazine anywhere? - comp.lang.java.guilicensing fonts and graphics, coding tips like implementing ... user interface - comp.os.ms-windows.programmer.win32 ... ... Binary Tree Interface - comp.lang.java.gui Yet another ... x86-64 and calling conventions - comp.compilers... it is probably my due punishment for writing yet another C ... Win32 is my primary target, with Linux (x86 and x86 ... it is my eventual hope to use it for implementing something ... Can drivers lie? - comp.graphics.api.openglI think it is especially true on win32 since all ... > That's not necessarily a poor implementation, since drivers ... Yet another example of having to spend more time on the What is the max length for a string sent through WM_COPYDATA ...... small application to monitor another ... COPYDATA message was received, yet ... performance, depending on the implementation it could be a heavy load on the stack. Another ... shared memory: open + unlink + sendfd + mmap - comp.unix ...the best implementation (falling back to mmap'd files ... systems were designed around IPC, yet they have several brain-dead versions of shared memory. MsgWaitFor...()-compatible condvar implementation that can be ...C++/VB - MsgWaitFor...()-compatible condvar implementation that can ... I need to implement a condition variable in win32 ... I send continuously data to another window using its ... cbloom rants: 08-09-11 - Threading LinksDerevyago - C++ multithreading yet another Win32 condvar implementation - comp.programming.threads Google Groups Dekker's algorithm - Wikipedia, the free ... Walkthrough: Hosting a Win32 Control in WPF... when you have a substantial investment in Win32 ... but you may only need to provide a minimal implementation. ... This use of SendMessage requires yet another PInvoke ... Hosting Win32 Content in WPFSee WPF and Win32 Interoperation Overview. ... way of putting those controls inside another HWND. ... You should now override the TranslateAccelerator implementation ... CIPE-Win32 by Damion K. WilsonCIPE-Win32 - Crypto IP ... It does not run (yet) on DOS ... be decided with the Linux implementation and then followed by this (and any other) port to another ... 5/18/2012 1:28:23 AM
|