Question

Is it possible to write this using the standard win32 CreateMutex style code. I am just wondering if I want to introduce a new library to our application or if I can find a way to write this myself. I just can't figure out how to to the wait inside a CriticalSection. This is my current working code with the pthread library.

T remove() {
    pthread_mutex_lock(&m_mutex);
    while (m_queue.size() == 0) {
        pthread_cond_wait(&m_condv, &m_mutex);
    }
    T item = m_queue.front();
    m_queue.pop_front();
    pthread_mutex_unlock(&m_mutex);
    return item;
}
Was it helpful?

Solution 2

Here's my attempt. This is not the best implementation of a conditional wait lock in win32, but I think it works. It could use careful code review scrutiny.

One caveat - it doesn't necessarily guarantee ordered fairness since all the waiting threads may be initially blocked waiting for the event. The scheduler will resume all the threads at this point to continue running (up to the subsequent blocking EnterCriticalSection call), but not necessarily in the same order in which the threads arrived into the remove() call to begin with. This likely isn't a big deal for most app's with only a handful of threads, but it's something most threading frameworks guarantee.

Other caveat - for brevity, I'm leaving out the important steps of checking the return value from all of these Win32 APIs.

CRITICAL_SECTION m_cs;
HANDLE m_event;

void Init()
{
   InitializeCriticalSection(&m_cs);
   m_event = CreateEvent(NULL, TRUE, FALSE, NULL); // manual reset event
}

void UnInit()
{
    DeleteCriticalSection(&m_cs);
    CloseHandle(m_event);
    m_event = NULL;
}

T remove()
{
    T item;
    bool fGotItem = false;
    while (fGotItem == false)
    {
        // wait for event to be signaled
        WaitForSingleObject(m_event, INFINITE);

        // wait for mutex to become available
        EnterCriticalSection(&m_cs);

        // inside critical section
        {
            // try to deque something - it’s possible that the queue is empty because another 
            // thread pre-empted us and got the last item in the queue before us

            size_t queue_size = m_queue.size();

            if (queue_size == 1)
            {
               // the queue is about to go empty
               ResetEvent(m_event);
            }

            if (queue_size > 0)
            {
                fGotItem = true;
                item = m_queue.front();

                m_queue.pop();        
            }
        }

        LeaveCriticalSection(&m_cs);

    }

    return item;
}

void Add(T& item)
{
    // wait for critical section to become available
    EnterCriticalSection(&m_cs);

    // inside critical section
    {
        m_queue.push_back(item);

        SetEvent(m_event); // signal other threads that something is available
    }

    LeaveCriticalSection(&m_cs);
}

OTHER TIPS

For pre-VC-2012 support, the best alternative is Boost.Thread that supports conditional variables.

Windows Vista introduced new native Win32 Conditional Variable and Slim Reader/Writer Lock primitives for exactly this type of scenario, for example:

Using a critical section:

CRITICAL_SECTION m_cs;
CONDITION_VARIABLE m_condv;

InitializeCriticalSection(&m_cs);
InitializeConditionVariable(&m_condv);

...

void add(T item)
{ 
    EnterCriticalSection(&m_cs);
    m_queue.push_back(item);
    LeaveCriticalSection(&m_cs);
    WakeConditionVariable(&m_condv);
}

T remove()
{ 
    EnterCriticalSection(&m_cs);
    while (m_queue.size() == 0)
        SleepConditionVariableCS(&m_condv, &m_cs, INFINITE);
    T item = m_queue.front();
    m_queue.pop_front();
    LeaveCriticalSection(&m_cs);
    return item;
}

Using a SRW lock:

SRWLOCK m_lock;
CONDITION_VARIABLE m_condv;

InitializeSRWLock(&m_lock);
InitializeConditionVariable(&m_condv);

...

void add(T item)
{ 
    AcquireSRWLockExclusive(&m_lock);
    m_queue.push_back(item);
    ReleaseSRWLockExclusive(&m_lock);
    WakeConditionVariable(&m_condv);
}

T remove()
{ 
    AcquireSRWLockExclusive(&m_lock);
    while (m_queue.size() == 0)
        SleepConditionVariableSRW(&m_condv, &m_lock, INFINITE, 0);
    T item = m_queue.front();
    m_queue.pop_front();
    ReleaseSRWLockExclusive(&m_lock);
    return item;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top