RetroEvent - позволяющий делать операции в процессе ожидания
От: Caracrist http://1pwd.org/
Дата: 26.09.12 23:02
Оценка:
Идея проста: позволить двойную проверку(double check).


  RetroEvent.h
#pragma once

#include <Windows.h>
#include <intrin.h>

class RetroEvent
{
public:
    RetroEvent();
    ~RetroEvent();

    __int64 RequestWait();
    DWORD PerformWait(__int64 index, DWORD timeout);
    void Set();
private:
    __int64 m_setIndex, m_waitIndex, m_waitingCount;
    HANDLE m_eventHandle;
};


  RetroEvent.cpp
#include "stdafx.h"
#include "RetroEvent.h"

RetroEvent::RetroEvent()
{
    m_eventHandle = CreateEvent(NULL, TRUE, FALSE, NULL);
    m_setIndex = 0;
    m_waitIndex = 0;
    m_waitingCount = 0;
}
RetroEvent::~RetroEvent()
{
    CloseHandle(m_eventHandle);
}

__int64 RetroEvent::RequestWait()
{
    return ::InterlockedIncrement64(&m_waitIndex);
}
DWORD RetroEvent::PerformWait(__int64 index, DWORD timeout)
{
    DWORD begin = GetTickCount();
    DWORD res = STATUS_WAIT_0;
    while (true)
    {
        ::InterlockedIncrement64(&m_waitingCount);
        if (::InterlockedCompareExchange64(&m_setIndex,  0, 0) > index)
        {
            ::InterlockedDecrement64(&m_waitingCount);
            break;
        }

        DWORD now = GetTickCount();
        if ((now - begin) > timeout)
        {
            ::InterlockedDecrement64(&m_waitingCount);
            SetLastError(WAIT_TIMEOUT);
            res = WAIT_TIMEOUT;
            break;
        }
        DWORD 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 (::InterlockedCompareExchange64(&m_waitingCount, 0, 0)) SwitchToThread();
    } 
    return res;

}
void RetroEvent::Set()
{
    __int64 setIndex = RequestWait();
    __int64 prevIndex = m_setIndex;
    if (prevIndex > setIndex)
        return;
    if (::InterlockedCompareExchange64(&m_setIndex,  setIndex, prevIndex) != prevIndex)
        return;
    ::InterlockedIncrement64(&m_waitingCount);
    SetEvent(m_eventHandle);
    while (::InterlockedCompareExchange64(&m_waitingCount, 0, 0) > 1) 
    {
        if (::InterlockedCompareExchange64(&m_setIndex,  0, 0) != setIndex)
        {
            ::InterlockedDecrement64(&m_waitingCount);
            return;
        }        
        SwitchToThread();
    }
    ResetEvent(m_eventHandle);
    ::InterlockedDecrement64(&m_waitingCount);
}


Буду рад как критике, так и ссылкам на другие готовые реализации.
~~~~~
~lol~~
~~~ Cracked Minds (головоломки)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.