0

boost::atomic has function store and load. However, there is a function that can return and current value and set the value a same time, which means the two operations are atomic.

Following code is the problem

void Initialize()
{
    ///if some other thread is initializing, skip this step
    if (m_initializing.load(boost::memory_order_relaxed))
        return S_FALSE;

    m_initializing.store(true, boost::memory_order_relaxed);
    return S_OK;
}

If two threads call this initialize function concurrently. thread A and B call load and returns false, they will call store at same time. So I want a atomic function. if A finds m_initializing is false, and set to true at same time. so that thread B knows some thread is working on it.

ernst
  • 5
  • 4

1 Answers1

0

Your approach is trickier than it seems at first glance.

As suggested in the comments section, a Read-Modify-Write (RMW) exchange() that replaces the load() can solve the issue of multiple threads accessing the same area, but what do you expect another thread will do while the initialization has not finished yet ? After all, your flag indicates that the initialization routine is running, not that it has finished. In addition, using memory_order_relaxed does not give any guarantees that the initialized data is properly synchronized with other threads calling Initialize(). At least you would have to use acquire/release semantics, but since the flag (with exchange()) is now set at the beginning, it does not 'release' anything yet.

It is much easier to use a mutex, which takes care of both thread serialization and inter-thread synchronization. In addition, use a flag that indicates status initialized.

bool m_initialized{false};
boost::mutex m_mut;

void Initialize()
{
    boost::lock_guard<boost::mutex> lck{m_mut};

    if (m_initialized)
        return; // already initialized

    // ... initializing part

    m_initialized = true;

    return;
}

Since the mutex synchronizes everything in the protected area, m_initialized can now be a regular bool (unless it is also used somewhere else).

I would suggest to replace the boost concurrency primitives (mutex, atomic) with std. They have been adopted by the standard from boost and are available since C++11

LWimsey
  • 6,189
  • 2
  • 25
  • 53
  • I am wondering, if initializing step take a long time, then other thread is blocked. And it is not safe to call function under the protection of mutex, which could cause deadlock unexpectedly. – ernst Jan 23 '17 at 01:43
  • Can you give more explanation about memory_order_relaxed, acquire/release semantics. I really cannot understand the documentation about memory order. – ernst Jan 23 '17 at 01:44
  • @ernst Initialization may take some time, but normally other threads cannot do anything until initialization has finished; although it is hard to tell since that part of the context is missing. Deadlock is not possible if you use the mutex as shown here as long as you don't call the `Initialize()` function recursively. – LWimsey Jan 23 '17 at 01:56
  • @ernst Explanation about memory ordering almost requires an entire chapter. Here is a related [question](http://stackoverflow.com/questions/12346487/what-do-each-memory-order-mean) – LWimsey Jan 23 '17 at 01:58
  • @ernst Btw, what makes you think that a mutex could cause deadlock unexpectedly ? A deadlock is always the result of a programming error, it does not happen for no reason. – LWimsey Jan 23 '17 at 23:20