Ticket #5752: atomic_once.patch

File atomic_once.patch, 11.6 KB (added by Andrey Semashev, 10 years ago)

The patch ports POSIX implementation of call_once to Boost.Atomic.

  • libs/thread/src/pthread/once.cpp

     
    44//  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
    55
    66#define __STDC_CONSTANT_MACROS
     7#include <boost/thread/detail/config.hpp>
     8#include <boost/thread/once.hpp>
    79#include <boost/thread/pthread/pthread_mutex_scoped_lock.hpp>
    8 #include <boost/thread/once.hpp>
    910#include <boost/assert.hpp>
     11#include <boost/static_assert.hpp>
     12#include <boost/atomic.hpp>
     13#include <boost/memory_order.hpp>
    1014#include <pthread.h>
    11 #include <stdlib.h>
    12 #include <memory>
    1315
    1416namespace boost
    1517{
    16     namespace detail
    17     {
    18         BOOST_THREAD_DECL thread_detail::uintmax_atomic_t once_global_epoch=BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_MAX_C;
    19         BOOST_THREAD_DECL pthread_mutex_t once_epoch_mutex=PTHREAD_MUTEX_INITIALIZER;
    20         BOOST_THREAD_DECL pthread_cond_t once_epoch_cv = PTHREAD_COND_INITIALIZER;
     18namespace thread_detail
     19{
    2120
    22         namespace
    23         {
    24             pthread_key_t epoch_tss_key;
    25             pthread_once_t epoch_tss_key_flag=PTHREAD_ONCE_INIT;
     21enum flag_states
     22{
     23    uninitialized, in_progress, initialized
     24};
    2625
    27             extern "C"
    28             {
    29                 static void delete_epoch_tss_data(void* data)
    30                 {
    31                     free(data);
    32                 }
     26#if BOOST_ATOMIC_INT_LOCK_FREE == 2
     27typedef unsigned int atomic_int_type;
     28#elif BOOST_ATOMIC_SHORT_LOCK_FREE == 2
     29typedef unsigned short atomic_int_type;
     30#elif BOOST_ATOMIC_CHAR_LOCK_FREE == 2
     31typedef unsigned char atomic_int_type;
     32#elif BOOST_ATOMIC_LONG_LOCK_FREE == 2
     33typedef unsigned long atomic_int_type;
     34#elif defined(BOOST_HAS_LONG_LONG) && BOOST_ATOMIC_LLONG_LOCK_FREE == 2
     35typedef ulong_long_type atomic_int_type;
     36#else
     37// All tested integer types are not atomic, the spinlock pool will be used
     38typedef unsigned int atomic_int_type;
     39#endif
    3340
    34                 static void create_epoch_tss_key()
    35                 {
    36                     BOOST_VERIFY(!pthread_key_create(&epoch_tss_key,delete_epoch_tss_data));
    37                 }
    38             }
    39 
    40 #if defined BOOST_THREAD_PATCH
    41             const pthread_once_t pthread_once_init_value=PTHREAD_ONCE_INIT;
    42             struct BOOST_THREAD_DECL delete_epoch_tss_key_on_dlclose_t
    43             {
    44                 delete_epoch_tss_key_on_dlclose_t()
    45                 {
    46                 }
    47                 ~delete_epoch_tss_key_on_dlclose_t()
    48                 {
    49                     if(memcmp(&epoch_tss_key_flag, &pthread_once_init_value, sizeof(pthread_once_t)))
    50                     {
    51                         pthread_key_delete(epoch_tss_key);
    52                     }
    53                 }
    54             };
    55             delete_epoch_tss_key_on_dlclose_t delete_epoch_tss_key_on_dlclose;
     41typedef boost::atomic< atomic_int_type > atomic_type
     42#if defined(__GNUC__)
     43    __attribute__((may_alias))
    5644#endif
    57         }
     45;
     46BOOST_STATIC_ASSERT_MSG(sizeof(once_flag) >= sizeof(atomic_type), "Boost.Thread: unsupported platform");
    5847
    59         thread_detail::uintmax_atomic_t& get_once_per_thread_epoch()
     48static pthread_mutex_t once_mutex = PTHREAD_MUTEX_INITIALIZER;
     49static pthread_cond_t once_cv = PTHREAD_COND_INITIALIZER;
     50
     51BOOST_THREAD_DECL bool enter_once_region(once_flag& flag) BOOST_NOEXCEPT
     52{
     53    atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage);
     54    if (f.load(memory_order_acquire) != initialized)
     55    {
     56        while (true)
    6057        {
    61             BOOST_VERIFY(!pthread_once(&epoch_tss_key_flag,create_epoch_tss_key));
    62             void* data=pthread_getspecific(epoch_tss_key);
    63             if(!data)
     58            atomic_int_type expected = uninitialized;
     59            if (f.compare_exchange_strong(expected, in_progress, memory_order_acq_rel, memory_order_acquire))
    6460            {
    65                 data=malloc(sizeof(thread_detail::uintmax_atomic_t));
    66                 BOOST_VERIFY(!pthread_setspecific(epoch_tss_key,data));
    67                 *static_cast<thread_detail::uintmax_atomic_t*>(data)=BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_MAX_C;
     61                // We have set the flag to in_progress
     62                return true;
    6863            }
    69             return *static_cast<thread_detail::uintmax_atomic_t*>(data);
     64            else if (expected == initialized)
     65            {
     66                // Another thread managed to complete the initialization
     67                return false;
     68            }
     69            else
     70            {
     71                // Wait until the initialization is complete
     72                pthread::pthread_mutex_scoped_lock lk(&once_mutex);
     73                while (f.load(memory_order_acquire) == in_progress)
     74                {
     75                    BOOST_VERIFY(!pthread_cond_wait(&once_cv, &once_mutex));
     76                }
     77            }
    7078        }
    7179    }
     80    else
     81        return false;
     82}
    7283
     84BOOST_THREAD_DECL void commit_once_region(once_flag& flag) BOOST_NOEXCEPT
     85{
     86    atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage);
     87    pthread::pthread_mutex_scoped_lock lk(&once_mutex);
     88    f.store(initialized, memory_order_release);
     89    BOOST_VERIFY(!pthread_cond_broadcast(&once_cv));
    7390}
     91
     92BOOST_THREAD_DECL void rollback_once_region(once_flag& flag) BOOST_NOEXCEPT
     93{
     94    atomic_type& f = reinterpret_cast< atomic_type& >(flag.storage);
     95    pthread::pthread_mutex_scoped_lock lk(&once_mutex);
     96    f.store(uninitialized, memory_order_release);
     97    BOOST_VERIFY(!pthread_cond_broadcast(&once_cv));
     98}
     99
     100} // namespace thread_detail
     101
     102} // namespace boost
  • boost/thread/detail/config.hpp

     
    131131#define BOOST_THREAD_DONT_PROVIDE_ONCE_CXX11
    132132#endif
    133133
     134// For C++11 call_once interface the compiler MUST support constexpr.
     135// Otherwise once_flag would be initialized during dynamic initialization stage, which is not thread-safe.
     136#if defined(BOOST_THREAD_PROVIDES_ONCE_CXX11) && defined(BOOST_NO_CXX11_CONSTEXPR)
     137#undef BOOST_THREAD_PROVIDES_ONCE_CXX11
     138#endif
     139
    134140// THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE
    135141#if ! defined BOOST_THREAD_DONT_PROVIDE_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE \
    136142 && ! defined BOOST_THREAD_PROVIDES_THREAD_DESTRUCTOR_CALLS_TERMINATE_IF_JOINABLE
  • boost/thread/pthread/once.hpp

     
    1212
    1313#include <boost/thread/detail/config.hpp>
    1414
    15 #include <boost/thread/pthread/pthread_mutex_scoped_lock.hpp>
    16 #include <boost/thread/detail/delete.hpp>
     15#include <boost/cstdint.hpp>
    1716#include <boost/detail/no_exceptions_support.hpp>
    1817
    19 #include <boost/assert.hpp>
    2018#include <boost/config/abi_prefix.hpp>
    2119
    22 #include <boost/cstdint.hpp>
    23 #include <pthread.h>
    24 #include <csignal>
    25 
    2620namespace boost
    2721{
    2822
    29 #define BOOST_ONCE_INITIAL_FLAG_VALUE 0
     23struct once_flag;
    3024
    31   namespace thread_detail
    32   {
    33 //#ifdef SIG_ATOMIC_MAX
    34 //    typedef sig_atomic_t  uintmax_atomic_t;
    35 //    #define BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_MAX_C SIG_ATOMIC_MAX
    36 //#else
    37     typedef unsigned long  uintmax_atomic_t;
    38     #define BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_C2(value) value##ul
    39     #define BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_MAX_C BOOST_THREAD_DETAIL_UINTMAX_ATOMIC_C2(~0)
    40 //#endif
    41   }
     25namespace thread_detail
     26{
     27BOOST_THREAD_DECL bool enter_once_region(once_flag& flag) BOOST_NOEXCEPT;
     28BOOST_THREAD_DECL void commit_once_region(once_flag& flag) BOOST_NOEXCEPT;
     29BOOST_THREAD_DECL void rollback_once_region(once_flag& flag) BOOST_NOEXCEPT;
     30}
    4231
    4332#ifdef BOOST_THREAD_PROVIDES_ONCE_CXX11
    4433
    45   struct once_flag
    46   {
    47       BOOST_THREAD_NO_COPYABLE(once_flag)
    48       BOOST_CONSTEXPR once_flag() BOOST_NOEXCEPT
    49         : epoch(BOOST_ONCE_INITIAL_FLAG_VALUE)
    50       {}
    51   private:
    52       volatile thread_detail::uintmax_atomic_t epoch;
    53       template<typename Function>
    54       friend
    55       void call_once(once_flag& flag,Function f);
    56   };
     34struct once_flag
     35{
     36    BOOST_THREAD_NO_COPYABLE(once_flag)
     37    constexpr once_flag() BOOST_NOEXCEPT : storage(0)
     38    {
     39    }
    5740
     41private:
     42#if defined(__GNUC__)
     43    __attribute__((may_alias))
     44#endif
     45    uintmax_t storage;
     46
     47    friend bool thread_detail::enter_once_region(once_flag& flag) BOOST_NOEXCEPT;
     48    friend void thread_detail::commit_once_region(once_flag& flag) BOOST_NOEXCEPT;
     49    friend void thread_detail::rollback_once_region(once_flag& flag) BOOST_NOEXCEPT;
     50};
     51
     52#define BOOST_ONCE_INIT once_flag()
     53
    5854#else // BOOST_THREAD_PROVIDES_ONCE_CXX11
    5955
    60     struct once_flag
    61     {
    62       volatile thread_detail::uintmax_atomic_t epoch;
    63     };
     56struct once_flag
     57{
     58#if defined(__GNUC__)
     59    __attribute__((may_alias))
     60#endif
     61    uintmax_t storage;
     62};
    6463
    65 #define BOOST_ONCE_INIT {BOOST_ONCE_INITIAL_FLAG_VALUE}
     64#define BOOST_ONCE_INIT {0}
     65
    6666#endif // BOOST_THREAD_PROVIDES_ONCE_CXX11
    6767
    68     namespace detail
     68template<typename Function>
     69inline void call_once(once_flag& flag, Function f)
     70{
     71    if (thread_detail::enter_once_region(flag))
    6972    {
    70         BOOST_THREAD_DECL thread_detail::uintmax_atomic_t& get_once_per_thread_epoch();
    71         BOOST_THREAD_DECL extern thread_detail::uintmax_atomic_t once_global_epoch;
    72         BOOST_THREAD_DECL extern pthread_mutex_t once_epoch_mutex;
    73         BOOST_THREAD_DECL extern pthread_cond_t once_epoch_cv;
    74     }
    75 
    76     // Based on Mike Burrows fast_pthread_once algorithm as described in
    77     // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2444.html
    78     template<typename Function>
    79     void call_once(once_flag& flag,Function f)
    80     {
    81         static thread_detail::uintmax_atomic_t const uninitialized_flag=BOOST_ONCE_INITIAL_FLAG_VALUE;
    82         static thread_detail::uintmax_atomic_t const being_initialized=uninitialized_flag+1;
    83         thread_detail::uintmax_atomic_t const epoch=flag.epoch;
    84         thread_detail::uintmax_atomic_t& this_thread_epoch=detail::get_once_per_thread_epoch();
    85 
    86         if(epoch<this_thread_epoch)
     73        BOOST_TRY
    8774        {
    88             pthread::pthread_mutex_scoped_lock lk(&detail::once_epoch_mutex);
    89 
    90             while(flag.epoch<=being_initialized)
    91             {
    92                 if(flag.epoch==uninitialized_flag)
    93                 {
    94                     flag.epoch=being_initialized;
    95                     BOOST_TRY
    96                     {
    97                         pthread::pthread_mutex_scoped_unlock relocker(&detail::once_epoch_mutex);
    98                         f();
    99                     }
    100                     BOOST_CATCH (...)
    101                     {
    102                         flag.epoch=uninitialized_flag;
    103                         BOOST_VERIFY(!pthread_cond_broadcast(&detail::once_epoch_cv));
    104                         BOOST_RETHROW
    105                     }
    106                     BOOST_CATCH_END
    107                     flag.epoch=--detail::once_global_epoch;
    108                     BOOST_VERIFY(!pthread_cond_broadcast(&detail::once_epoch_cv));
    109                 }
    110                 else
    111                 {
    112                     while(flag.epoch==being_initialized)
    113                     {
    114                         BOOST_VERIFY(!pthread_cond_wait(&detail::once_epoch_cv,&detail::once_epoch_mutex));
    115                     }
    116                 }
    117             }
    118             this_thread_epoch=detail::once_global_epoch;
     75            f();
    11976        }
     77        BOOST_CATCH (...)
     78        {
     79            thread_detail::rollback_once_region(flag);
     80            BOOST_RETHROW
     81        }
     82        BOOST_CATCH_END
     83        thread_detail::commit_once_region(flag);
    12084    }
    12185}
    12286
     87}
     88
    12389#include <boost/config/abi_suffix.hpp>
    12490
    12591#endif