Thursday, April 14, 2011

How to synchronize threads when polling for state changes with boost

In my application I want to be informed by events, that another application has been started or stopped. I have an existing API to the running application which cannot be changed to accomodate notification which would be the obvious solution.

What I have is a function call in the API (isRunning), so I decided to make a polling thread which polls the state through the API and informs my app.

My problem is now, the API call in the polling thread uses an object which is also used in my own application's main thread and I'd like to know how to be sure that I make it right :).

My idea would be to wrap each call to the API object (through an adapter e.g., see the second code block) with a mutex lock, so I'm sure the API is not called by my threads more than once.

Is this the right approach?

I'm using boost for threading/syncing (see code).

This is the polling thread:

void EventCreator::start()
{
    stop();
    m_bShouldRun = true;    
    m_spThread = BoostThreadPtr(new thread(bind(&EventCreator::run,this)));
}

void EventCreator::stop()
{
    {
        lock_guard<mutex> lock(m_mutex);
        m_bShouldRun = false;
        m_condition.notify_one();
    }

    if (m_spThread)
    {
        m_spThread->join();
        m_spThread.reset();
    }    
}

void EventCreator::run()
{
    bool isRTAppRunning = m_pDevice->isApplicationRunning();
    while (m_bShouldRun)
    {
        boost::unique_lock<mutex> lock(m_mutex);
        // 
        if(!m_condition.timed_wait(lock,boost::system_time(boost::get_system_time() + boost::posix_time::milliseconds(25))))
        {
            // here because of time out, so no sleep necessary
            bool isStillRunning = m_pDevice->isApplicationRunning();
            if (isRTAppRunning != isStillRunning)
            {   

                if (isStillRunning)
                {
                    // Using SendMessage to main thread => no problem here
                    notifyAppStarted();
                }
                else
                {   
                    notifyAppStopped(); 
                }
                isRTAppRunning = isStillRunning;
            }
            // in addition to wait above
            m_spThread->yield();
        }
    }
}

These are some API calls from the main thread:

void Device::getData(Int32 byteCnt)
{
    mutex::scoped_lock lock(m_monitor);
    m_pApi->fetchBytes(&m_buf,byteCnt);
}

bool Device::isApplicationRunning()
{
    mutex::scoped_lock lock(m_monitor);
    return m_pApi->getState() == DV_RUNNING;
}
From stackoverflow
  • This sounds like a good approach.

    In general, locks should be very fast to acquire when uncontended. Your polling thread should be polling infrequently, i.e., at most once every few seconds, so contention on the lock should be very small.

    If your two threads are constantly battling over the lock, you'll need to rethink your design.

0 comments:

Post a Comment