Home | History | Annotate | Download | only in src
      1 //===------------------------- mutex.cpp ----------------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is dual licensed under the MIT and the University of Illinois Open
      6 // Source Licenses. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #define _LIBCPP_BUILDING_MUTEX
     11 #include "mutex"
     12 #include "limits"
     13 #include "system_error"
     14 #include "include/atomic_support.h"
     15 #include "__undef_macros"
     16 
     17 _LIBCPP_BEGIN_NAMESPACE_STD
     18 #ifndef _LIBCPP_HAS_NO_THREADS
     19 
     20 const defer_lock_t  defer_lock = {};
     21 const try_to_lock_t try_to_lock = {};
     22 const adopt_lock_t  adopt_lock = {};
     23 
     24 mutex::~mutex()
     25 {
     26     __libcpp_mutex_destroy(&__m_);
     27 }
     28 
     29 void
     30 mutex::lock()
     31 {
     32     int ec = __libcpp_mutex_lock(&__m_);
     33     if (ec)
     34         __throw_system_error(ec, "mutex lock failed");
     35 }
     36 
     37 bool
     38 mutex::try_lock() _NOEXCEPT
     39 {
     40     return __libcpp_mutex_trylock(&__m_);
     41 }
     42 
     43 void
     44 mutex::unlock() _NOEXCEPT
     45 {
     46     int ec = __libcpp_mutex_unlock(&__m_);
     47     (void)ec;
     48     _LIBCPP_ASSERT(ec == 0, "call to mutex::unlock failed");
     49 }
     50 
     51 // recursive_mutex
     52 
     53 recursive_mutex::recursive_mutex()
     54 {
     55     int ec = __libcpp_recursive_mutex_init(&__m_);
     56     if (ec)
     57         __throw_system_error(ec, "recursive_mutex constructor failed");
     58 }
     59 
     60 recursive_mutex::~recursive_mutex()
     61 {
     62     int e = __libcpp_recursive_mutex_destroy(&__m_);
     63     (void)e;
     64     _LIBCPP_ASSERT(e == 0, "call to ~recursive_mutex() failed");
     65 }
     66 
     67 void
     68 recursive_mutex::lock()
     69 {
     70     int ec = __libcpp_recursive_mutex_lock(&__m_);
     71     if (ec)
     72         __throw_system_error(ec, "recursive_mutex lock failed");
     73 }
     74 
     75 void
     76 recursive_mutex::unlock() _NOEXCEPT
     77 {
     78     int e = __libcpp_recursive_mutex_unlock(&__m_);
     79     (void)e;
     80     _LIBCPP_ASSERT(e == 0, "call to recursive_mutex::unlock() failed");
     81 }
     82 
     83 bool
     84 recursive_mutex::try_lock() _NOEXCEPT
     85 {
     86     return __libcpp_recursive_mutex_trylock(&__m_);
     87 }
     88 
     89 // timed_mutex
     90 
     91 timed_mutex::timed_mutex()
     92     : __locked_(false)
     93 {
     94 }
     95 
     96 timed_mutex::~timed_mutex()
     97 {
     98     lock_guard<mutex> _(__m_);
     99 }
    100 
    101 void
    102 timed_mutex::lock()
    103 {
    104     unique_lock<mutex> lk(__m_);
    105     while (__locked_)
    106         __cv_.wait(lk);
    107     __locked_ = true;
    108 }
    109 
    110 bool
    111 timed_mutex::try_lock() _NOEXCEPT
    112 {
    113     unique_lock<mutex> lk(__m_, try_to_lock);
    114     if (lk.owns_lock() && !__locked_)
    115     {
    116         __locked_ = true;
    117         return true;
    118     }
    119     return false;
    120 }
    121 
    122 void
    123 timed_mutex::unlock() _NOEXCEPT
    124 {
    125     lock_guard<mutex> _(__m_);
    126     __locked_ = false;
    127     __cv_.notify_one();
    128 }
    129 
    130 // recursive_timed_mutex
    131 
    132 recursive_timed_mutex::recursive_timed_mutex()
    133     : __count_(0),
    134       __id_(0)
    135 {
    136 }
    137 
    138 recursive_timed_mutex::~recursive_timed_mutex()
    139 {
    140     lock_guard<mutex> _(__m_);
    141 }
    142 
    143 void
    144 recursive_timed_mutex::lock()
    145 {
    146     __libcpp_thread_id id = __libcpp_thread_get_current_id();
    147     unique_lock<mutex> lk(__m_);
    148     if (__libcpp_thread_id_equal(id, __id_))
    149     {
    150         if (__count_ == numeric_limits<size_t>::max())
    151             __throw_system_error(EAGAIN, "recursive_timed_mutex lock limit reached");
    152         ++__count_;
    153         return;
    154     }
    155     while (__count_ != 0)
    156         __cv_.wait(lk);
    157     __count_ = 1;
    158     __id_ = id;
    159 }
    160 
    161 bool
    162 recursive_timed_mutex::try_lock() _NOEXCEPT
    163 {
    164     __libcpp_thread_id id = __libcpp_thread_get_current_id();
    165     unique_lock<mutex> lk(__m_, try_to_lock);
    166     if (lk.owns_lock() && (__count_ == 0 || __libcpp_thread_id_equal(id, __id_)))
    167     {
    168         if (__count_ == numeric_limits<size_t>::max())
    169             return false;
    170         ++__count_;
    171         __id_ = id;
    172         return true;
    173     }
    174     return false;
    175 }
    176 
    177 void
    178 recursive_timed_mutex::unlock() _NOEXCEPT
    179 {
    180     unique_lock<mutex> lk(__m_);
    181     if (--__count_ == 0)
    182     {
    183         __id_ = 0;
    184         lk.unlock();
    185         __cv_.notify_one();
    186     }
    187 }
    188 
    189 #endif // !_LIBCPP_HAS_NO_THREADS
    190 
    191 // If dispatch_once_f ever handles C++ exceptions, and if one can get to it
    192 // without illegal macros (unexpected macros not beginning with _UpperCase or
    193 // __lowercase), and if it stops spinning waiting threads, then call_once should
    194 // call into dispatch_once_f instead of here. Relevant radar this code needs to
    195 // keep in sync with:  7741191.
    196 
    197 #ifndef _LIBCPP_HAS_NO_THREADS
    198 _LIBCPP_SAFE_STATIC static __libcpp_mutex_t mut = _LIBCPP_MUTEX_INITIALIZER;
    199 _LIBCPP_SAFE_STATIC static __libcpp_condvar_t cv = _LIBCPP_CONDVAR_INITIALIZER;
    200 #endif
    201 
    202 void
    203 __call_once(volatile unsigned long& flag, void* arg, void(*func)(void*))
    204 {
    205 #if defined(_LIBCPP_HAS_NO_THREADS)
    206     if (flag == 0)
    207     {
    208 #ifndef _LIBCPP_NO_EXCEPTIONS
    209         try
    210         {
    211 #endif  // _LIBCPP_NO_EXCEPTIONS
    212             flag = 1;
    213             func(arg);
    214             flag = ~0ul;
    215 #ifndef _LIBCPP_NO_EXCEPTIONS
    216         }
    217         catch (...)
    218         {
    219             flag = 0ul;
    220             throw;
    221         }
    222 #endif  // _LIBCPP_NO_EXCEPTIONS
    223     }
    224 #else // !_LIBCPP_HAS_NO_THREADS
    225     __libcpp_mutex_lock(&mut);
    226     while (flag == 1)
    227         __libcpp_condvar_wait(&cv, &mut);
    228     if (flag == 0)
    229     {
    230 #ifndef _LIBCPP_NO_EXCEPTIONS
    231         try
    232         {
    233 #endif  // _LIBCPP_NO_EXCEPTIONS
    234             __libcpp_relaxed_store(&flag, 1ul);
    235             __libcpp_mutex_unlock(&mut);
    236             func(arg);
    237             __libcpp_mutex_lock(&mut);
    238             __libcpp_atomic_store(&flag, ~0ul, _AO_Release);
    239             __libcpp_mutex_unlock(&mut);
    240             __libcpp_condvar_broadcast(&cv);
    241 #ifndef _LIBCPP_NO_EXCEPTIONS
    242         }
    243         catch (...)
    244         {
    245             __libcpp_mutex_lock(&mut);
    246             __libcpp_relaxed_store(&flag, 0ul);
    247             __libcpp_mutex_unlock(&mut);
    248             __libcpp_condvar_broadcast(&cv);
    249             throw;
    250         }
    251 #endif  // _LIBCPP_NO_EXCEPTIONS
    252     }
    253     else
    254         __libcpp_mutex_unlock(&mut);
    255 #endif // !_LIBCPP_HAS_NO_THREADS
    256 
    257 }
    258 
    259 _LIBCPP_END_NAMESPACE_STD
    260