Boost logo

Boost :

Subject: Re: [boost] [thread] Can Boost.Thread use Boost.Atomic without falling on a compatibility issue?
From: Vicente J. Botet Escriba (vicente.botet_at_[hidden])
Date: 2013-01-13 17:24:10


Le 13/01/13 21:32, Andrey Semashev a écrit :
> On Mon, Jan 14, 2013 at 12:04 AM, Andrey Semashev
> <andrey.semashev_at_[hidden]> wrote:
>> On Sun, Jan 13, 2013 at 11:44 PM, Andrey Semashev
>> <andrey.semashev_at_[hidden]> wrote:
>>> On Sun, Jan 13, 2013 at 11:15 PM, Vicente J. Botet Escriba
>>> <vicente.botet_at_[hidden]> wrote:
>>>> I believed it also but the mutex shouldn't be locked while doing
>>>> notifications, only when you change the condition and when you wait for the
>>>> condition to be notified.
>>> Looking at the code now it seems there is a potential race condition.
>>> The commit or rollback can set the flag and issue the broadcast just
>>> before the enter method acquires the lock and blocks in the cv.
>>>
>>> I suppose there should be a second check for the flag value under the
>>> lock in the enter_once_region method. The locks in commit and rollback
>>> functions can be removed altogether then.
>> I attached the updated once.cpp.
> No, that code is not correct either. The lock in commit and rollback
> functions *is* needed and it *must* cover both the store and broadcast
> exactly to prevent missed notifications. Otherwise thread A can check
> for the flag value but not yet enter pthread_cond_wait and thread B
> can store the new flag value and issue pthread_cond_broadcast.
>
>
Wouldn't be useful to use double check here

     BOOST_THREAD_DECL bool enter_once_region(once_flag& flag)
BOOST_NOEXCEPT
     {
       atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage);
       if (f.load(memory_order_acquire) != initialized)
       {
         pthread::pthread_mutex_scoped_lock lk(&once_mutex);
         if (f.load(memory_order_acquire) != initialized)
         {
           while (true)
           {
             atomic_int_type expected = uninitialized;
             if (f.compare_exchange_strong(expected, in_progress,
memory_order_acq_rel, memory_order_acquire))
             {
               // We have set the flag to in_progress
               return true;
             }
             else if (expected == initialized)
             {
               // Another thread managed to complete the initialization
               return false;
             }
             else
             {
               // Wait until the initialization is complete
               BOOST_VERIFY(!pthread_cond_wait(&once_cv, &once_mutex));
             }
           }
         }
       }
       return false;
     }

     BOOST_THREAD_DECL void commit_once_region(once_flag& flag)
BOOST_NOEXCEPT
     {
       atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage);
       {
         pthread::pthread_mutex_scoped_lock lk(&once_mutex);
         f.store(initialized, memory_order_release);
       }
       BOOST_VERIFY(!pthread_cond_broadcast(&once_cv));
     }

     BOOST_THREAD_DECL void rollback_once_region(once_flag& flag)
BOOST_NOEXCEPT
     {
       atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage);
       {
         pthread::pthread_mutex_scoped_lock lk(&once_mutex);
         f.store(uninitialized, memory_order_release);
       }
       BOOST_VERIFY(!pthread_cond_broadcast(&once_cv));
     }

Vicente


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk