В общем и целом, то что в первом сообщении — это нерабочий код. Там много проблем от голодания до дедлока.
Если кому интересно в медицинских целях можно всё это поискать.
А я предлагаю на рассмотрение исправленный вариант:
| | PulsarEvent.h |
| | #pragma once
#include <Windows.h>
#include <intrin.h>
class PulsarEvent
{
public:
PulsarEvent();
~PulsarEvent();
__int64 BeginWait();
DWORD PerformWait(__int64 index, DWORD timeout);
void Pulse();
private:
__int64 m_setIndex, m_waitIndex, m_waitingCount, m_settingCount;
long m_setLock;
HANDLE m_eventHandle;
};
|
| | |
| | PulsarEvent.cpp |
| | #include "stdafx.h"
#include "PulsarEvent.h"
PulsarEvent::PulsarEvent()
{
m_eventHandle = CreateEvent(NULL, TRUE, FALSE, NULL);
m_setIndex = 0;
m_waitIndex = 0;
m_waitingCount = 0;
m_settingCount = 0;
m_setLock = 0;
}
PulsarEvent::~PulsarEvent()
{
CloseHandle(m_eventHandle);
}
__int64 PulsarEvent::BeginWait()
{
return ::InterlockedIncrement64(&m_waitIndex);
}
DWORD PulsarEvent::PerformWait(__int64 index, DWORD timeout)
{
DWORD begin = GetTickCount();
DWORD res = STATUS_WAIT_0;
while (true)
{
::InterlockedIncrement64(&m_waitingCount);
__int64 prevIndex = m_setIndex;
if (prevIndex > index) {
::InterlockedDecrement64(&m_waitingCount);
break;
}
DWORD toWait = INFINITE;
if (timeout != INFINITE) {
DWORD now = GetTickCount();
if ((now - begin) > timeout)
{
::InterlockedDecrement64(&m_waitingCount);
SetLastError(WAIT_TIMEOUT);
res = WAIT_TIMEOUT;
break;
}
toWait = timeout - (now - begin);
}
res = WaitForSingleObject(m_eventHandle, toWait);
if (res != STATUS_WAIT_0)
{
::InterlockedDecrement64(&m_waitingCount);
break;
}
if(::InterlockedDecrement64(&m_waitingCount) == 0)
continue;
while (m_settingCount && m_waitingCount && (m_setIndex == prevIndex))
SwitchToThread();
}
return res;
}
void PulsarEvent::Pulse()
{
__int64 setIndex = BeginWait();
__int64 prevIndex = m_setIndex;
if (prevIndex > setIndex)
return;
if (::InterlockedCompareExchange64(&m_setIndex, setIndex, prevIndex) != prevIndex)
return;
while(::InterlockedCompareExchange(&m_setLock, 1, 0))
SwitchToThread();
SetEvent(m_eventHandle);
::InterlockedExchange(&m_setLock, 0);
::InterlockedIncrement64(&m_settingCount);
while (m_waitingCount && (m_setIndex == setIndex))
SwitchToThread();
while(::InterlockedCompareExchange(&m_setLock, 1, 0))
SwitchToThread();
if (m_setIndex == setIndex) {
ResetEvent(m_eventHandle);
}
::InterlockedExchange(&m_setLock, 0);
::InterlockedDecrement64(&m_settingCount);
}
|
| | |
Я немного изменил названия, так мне кажется правильней...
А вот пример использования:
| | SmartSpinMutex |
| | class SmartSpinMutex
{
public:
SmartSpinMutex() : pEvent(&g_Event), m_lock(0) {}
void Lock()
{
for (__int64 index = pEvent->BeginWait();
::InterlockedCompareExchange(&m_lock, 1, 0);
index = pEvent->BeginWait())
{
pEvent->PerformWait(index, INFINITE);
}
}
void Unlock()
{
::InterlockedExchange(&m_lock, 0);
pEvent->Pulse();
}
private:
static PulsarEvent g_Event;
PulsarEvent * pEvent;
long m_lock;
};
DECLSPEC_SELECTANY PulsarEvent SmartSpinMutex::g_Event;
|
| | |