Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

wait-free fast-path win32 semaphore...

11 views
Skip to first unread message

Chris M. Thomasson

unread,
Mar 24, 2009, 9:40:18 AM3/24/09
to
This is Joe Seighs original algorithm with a minor tweak. One can now
increment the semaphore value by more than 1; here is example:
___________________________________________________________________
#define UNEXPECTED_ERROR(mp_exp) \
assert((mp_exp)), std::unexpected()

#define UNEXPECTED_ERROR_WIN() \
UNEXPECTED_ERROR(GetLastError() == ERROR_SUCCESS)


class semaphore {
LONG m_count;
HANDLE m_waitset;


public:
semaphore(LONG count = 0)
: m_count(count),
m_waitset(CreateSemaphore(NULL, 0, LONG_MAX, NULL)) {
if (m_count < 0 || ! m_waitset) {
if (m_waitset) CloseHandle(m_waitset);
assert(m_count > -1 && m_waitset);
throw std::exception();
}
}


~semaphore() throw() {
if (m_count < 0) {
UNEXPECTED_ERROR(m_count > -1);
} else if (! CloseHandle(m_waitset)) {
UNEXPECTED_ERROR_WIN();
}
}


public:
void post(LONG count = 1) throw() {
if (count < 1) {
UNEXPECTED_ERROR(count > 0);
}
LONG old_count = InterlockedExchangeAdd(&m_count, count);
if (old_count < 0) {
if (! ReleaseSemaphore(m_waitset,
(-old_count) > count ? count : -old_count, NULL)) {
UNEXPECTED_ERROR_WIN();
}
}
}


void wait() throw() {
if (InterlockedDecrement(&m_count) < 0) {
if (WaitForSingleObject(m_waitset, INFINITE) !=
WAIT_OBJECT_0) {
UNEXPECTED_ERROR_WIN();
}
}
}
};
___________________________________________________________________


I kept the timeout logic out for brevity. That part has not changed any, it
can be kept the same. Enjoy!


;^D

Chris M. Thomasson

unread,
Mar 24, 2009, 10:25:04 AM3/24/09
to

"Chris M. Thomasson" <n...@spam.invalid> wrote in message
news:Ro5yl.4222$3g7....@newsfe15.iad...

> This is Joe Seighs original algorithm with a minor tweak. One can now
> increment the semaphore value by more than 1; here is example:
> ___________________________________________________________________
>
> ___________________________________________________________________

> I kept the timeout logic out for brevity. That part has not changed any,
> it can be kept the same. Enjoy!


Joes semaphore hands off ownership on contention.

Here is one that does not:
____________________________________________________________
class semaphore {
LONG volatile m_state;
eventcount m_ecount;

public:
void post(LONG count = 1) {
InterlockedExchangeAdd(&m_state, count);
if (count == 1) {
m_ecount.signal_relaxed();
} else {
m_ecount.broadcast_relaxed();
}
}

void wait() {
LONG cmp;
while (! (cmp = m_state) ||
InterlockedCompareExchange(
&m_state, cmp, cmp - 1) != cmp) {
eventcount::key_type key = m_ecount.get();
if ((cmp = m_state) &&
InterlockedCompareExchange(
&m_state, cmp, cmp - 1) == cmp) {
break;
}
m_ecount.wait(key);
}
}
};
____________________________________________________________

Chris M. Thomasson

unread,
Apr 2, 2009, 7:36:39 AM4/2/09
to

"Chris M. Thomasson" <n...@spam.invalid> wrote in message
news:Q26yl.4230$3g7...@newsfe15.iad...


I think that is broken. This might fix it:


____________________________________________________________
class semaphore {
LONG volatile m_state;
eventcount m_ecount;


bool prv_wait_cas() {
LONG cmptmp, cmp = m_state;

do {
cmptmp = cmp;
cmp = InterlockedCompareExchange(&m_state,
(cmp) ? cmp - 1 : 0, cmp);
} while (cmp != cmptmp);

return (cmp);
}


public:
void post(LONG count = 1) {
InterlockedExchangeAdd(&m_state, count);
if (count == 1) {
m_ecount.signal_relaxed();
} else {
m_ecount.broadcast_relaxed();
}
}

void wait() {
while (! prv_wait_cas()) {


eventcount::key_type key = m_ecount.get();

if (prv_wait_cas()) break;
m_ecount.wait(key);
}
}
};
____________________________________________________________


0 new messages