Re: PulsarEvent - позволяющий делать операции в процессе ожидания
От: Caracrist https://1pwd.org/
Дата: 28.09.12 22:03
Оценка:
В общем и целом, то что в первом сообщении — это нерабочий код. Там много проблем от голодания до дедлока.
Если кому интересно в медицинских целях можно всё это поискать.

А я предлагаю на рассмотрение исправленный вариант:
  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;
~~~~~
~lol~~
~~~ Single Password Solution
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.