Re: MT Design Question
"Balog Pal" <pasa@lib.hu> wrote in message
news:i5d2jb$1ob3$1@news.ett.com.ua...
"Joshua Maurice" <joshuamaurice@gmail.com>
[...]
Implementing the equivalent of
pthread_cond_wait with the windows XP and earlier API is surprisingly
difficult, and generally results in much less efficient code than if
they had properly implemented kernel and scheduler to directly
understand pthread_cond_wait or equivalent.
Implementing the perfect equivalent maybe. But covering the same
high-level
use case is not. The use of attached an auto-locking mutex in signaling
looks convenient, but is not needed more often than it is. And the rest
of
the cases can be probably covered better too, using
WaitFormultipleObjects.
(Btw if you just use the latter to wait on Event and a mutex with ALL
option, isn't the effect the same as you are after? )
FWIW, here are some of the issues:
http://www.cs.wustl.edu/~schmidt/win32-cv-1.html
Well, actually, it's not all _that_ hard to get a correct condvar impl on
Windows. Here is a sketch for a simple broadcast only condvar for Windows.
Keep in mind that POSIX allows for broadcast only impls as
`pthread_cond_signal()' is allowed to wake more than one thread.
<pseudo-code sketch>
_______________________________________________________
struct condvar
{
struct waitset
{
HANDLE m_event; // = manual reset event; set to false
LONG m_refs; // = 0
};
waitset* m_waitset; // = NULL
LONG m_refs; // = 0
CRITICAL_SECTION m_mutex;
void wait(LPCRITICAL_SECTION umutex)
{
// grab a reference to the current waitset
EnterCriticalSection(&m_mutex);
waitset* w = m_waitset;
if (! w) w = m_waitset = new waitset();
++m_refs;
LeaveCriticalSection(&m_mutex);
// unlock user mutex and wait on the waitset we obtained
LeaveCriticalSection(umutex);
WaitForSingleObject(w->m_waitset, INFINITE);
// decrement the waitsets refcount
if (! InterlockedDecrement(&w->m_refs))
{
delete w;
}
EnterCriticalSection(umutex);
// that's all folks! ;^)
}
void broadcast()
{
// swap a reference to the current/new waitset with NULL
EnterCriticalSection(&m_mutex);
waitset* w = m_waitset;
LONG refs = m_refs;
m_waitset = NULL;
m_refs = 0;
LeaveCriticalSection(&m_mutex);
if (w)
{
// signal all waiters
SetEvent(w->m_event);
// transfer the swapped reference count to the waitset reference
count
if (InterlockedExchangeAdd(&w->m_refs, refs) == -refs)
{
delete w;
}
}
}
void signal()
{
broadcast();
}
};
_______________________________________________________
Barring any damn typos, the algorithm works perfectly fine. BTW, there are
several enhancements that can be made to it... Perhaps a waitset cache?
;^)