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