Garmaine Staff asked 2 years ago

I'm writing a multithreaded program that can execute some tasks in separate threads.

Some operations require waiting for them at the end of execution of my program. I've written simple guard for such "important" operations:

class CPendingOperationGuard final
{
public: 
    CPendingOperationGuard()
    {
        InterlockedIncrementAcquire( &m_ullCounter );
    }

    ~CPendingOperationGuard()
    {
        InterlockedDecrementAcquire( &m_ullCounter );
    }

    static bool WaitForAll( DWORD dwTimeOut )
    {
        // Here is a topic of my question
        // Return false on timeout
        // Return true if wait was successful
    }

private:
    static volatile ULONGLONG m_ullCounter;
};

Usage is simple:

void ImportantTask()
{
    CPendingOperationGuard guard;
    // Do work
}

// ...

void StopExecution()
{
    if(!CPendingOperationGuard::WaitForAll( 30000 )) {
        // Handle error
    }
}

The question is: how to effectively wait until a m_ullCounter becames zero or until timeout.

I have two ideas:

  1. To launch this function in another separate thread and write WaitForSingleObject( hThread, dwTimeout ):

    DWORD WINAPI WaitWorker( LPVOID )
    {
        while(InterlockedCompareExchangeRelease( &m_ullCounter, 0, 0 ))
            ;
    }
    

    But it will "eat" almost 100% of CPU time – bad idea.

  2. Second idea is to allow other threads to start:

    DWORD WINAPI WaitWorker( LPVOID )
    {
        while(InterlockedCompareExchangeRelease( &m_ullCounter, 0, 0 ))
            Sleep( 0 );
    }
    

    But it'll switch execution context into kernel mode and back – too expensive in may task. Bad idea too

The question is:
How to perform almost-zero-overhead waiting until my variable becames zero? Maybe without separate thread… The main condition is to support stopping of waiting by timeout.

Maybe someone can suggest completely another idea for my task – to wait for all registered operations (like in WinAPI's ThreadPools – its API has, for instance, WaitForThreadpoolWaitCallbacks to perform waiting for ALL registered tasks).

PS: it is not possible to rewrite my code with ThreadPool API 🙁