Home | History | Annotate | Download | only in base
      1 /*
      2  *  Copyright 2015 The WebRTC Project Authors. All rights reserved.
      3  *
      4  *  Use of this source code is governed by a BSD-style license
      5  *  that can be found in the LICENSE file in the root of the source
      6  *  tree. An additional intellectual property rights grant can be found
      7  *  in the file PATENTS.  All contributing project authors may
      8  *  be found in the AUTHORS file in the root of the source tree.
      9  */
     10 
     11 #include "webrtc/base/criticalsection.h"
     12 
     13 #include "webrtc/base/checks.h"
     14 
     15 namespace rtc {
     16 
     17 CriticalSection::CriticalSection() {
     18 #if defined(WEBRTC_WIN)
     19   InitializeCriticalSection(&crit_);
     20 #else
     21   pthread_mutexattr_t mutex_attribute;
     22   pthread_mutexattr_init(&mutex_attribute);
     23   pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
     24   pthread_mutex_init(&mutex_, &mutex_attribute);
     25   pthread_mutexattr_destroy(&mutex_attribute);
     26   CS_DEBUG_CODE(thread_ = 0);
     27   CS_DEBUG_CODE(recursion_count_ = 0);
     28 #endif
     29 }
     30 
     31 CriticalSection::~CriticalSection() {
     32 #if defined(WEBRTC_WIN)
     33   DeleteCriticalSection(&crit_);
     34 #else
     35   pthread_mutex_destroy(&mutex_);
     36 #endif
     37 }
     38 
     39 void CriticalSection::Enter() EXCLUSIVE_LOCK_FUNCTION() {
     40 #if defined(WEBRTC_WIN)
     41   EnterCriticalSection(&crit_);
     42 #else
     43   pthread_mutex_lock(&mutex_);
     44 #if CS_DEBUG_CHECKS
     45   if (!recursion_count_) {
     46     RTC_DCHECK(!thread_);
     47     thread_ = pthread_self();
     48   } else {
     49     RTC_DCHECK(CurrentThreadIsOwner());
     50   }
     51   ++recursion_count_;
     52 #endif
     53 #endif
     54 }
     55 
     56 bool CriticalSection::TryEnter() EXCLUSIVE_TRYLOCK_FUNCTION(true) {
     57 #if defined(WEBRTC_WIN)
     58   return TryEnterCriticalSection(&crit_) != FALSE;
     59 #else
     60   if (pthread_mutex_trylock(&mutex_) != 0)
     61     return false;
     62 #if CS_DEBUG_CHECKS
     63   if (!recursion_count_) {
     64     RTC_DCHECK(!thread_);
     65     thread_ = pthread_self();
     66   } else {
     67     RTC_DCHECK(CurrentThreadIsOwner());
     68   }
     69   ++recursion_count_;
     70 #endif
     71   return true;
     72 #endif
     73 }
     74 void CriticalSection::Leave() UNLOCK_FUNCTION() {
     75   RTC_DCHECK(CurrentThreadIsOwner());
     76 #if defined(WEBRTC_WIN)
     77   LeaveCriticalSection(&crit_);
     78 #else
     79 #if CS_DEBUG_CHECKS
     80   --recursion_count_;
     81   RTC_DCHECK(recursion_count_ >= 0);
     82   if (!recursion_count_)
     83     thread_ = 0;
     84 #endif
     85   pthread_mutex_unlock(&mutex_);
     86 #endif
     87 }
     88 
     89 bool CriticalSection::CurrentThreadIsOwner() const {
     90 #if defined(WEBRTC_WIN)
     91   // OwningThread has type HANDLE but actually contains the Thread ID:
     92   // http://stackoverflow.com/questions/12675301/why-is-the-owningthread-member-of-critical-section-of-type-handle-when-it-is-de
     93   // Converting through size_t avoids the VS 2015 warning C4312: conversion from
     94   // 'type1' to 'type2' of greater size
     95   return crit_.OwningThread ==
     96          reinterpret_cast<HANDLE>(static_cast<size_t>(GetCurrentThreadId()));
     97 #else
     98 #if CS_DEBUG_CHECKS
     99   return pthread_equal(thread_, pthread_self());
    100 #else
    101   return true;
    102 #endif  // CS_DEBUG_CHECKS
    103 #endif
    104 }
    105 
    106 bool CriticalSection::IsLocked() const {
    107 #if defined(WEBRTC_WIN)
    108   return crit_.LockCount != -1;
    109 #else
    110 #if CS_DEBUG_CHECKS
    111   return thread_ != 0;
    112 #else
    113   return true;
    114 #endif
    115 #endif
    116 }
    117 
    118 CritScope::CritScope(CriticalSection* cs) : cs_(cs) { cs_->Enter(); }
    119 CritScope::~CritScope() { cs_->Leave(); }
    120 
    121 TryCritScope::TryCritScope(CriticalSection* cs)
    122     : cs_(cs), locked_(cs->TryEnter()) {
    123   CS_DEBUG_CODE(lock_was_called_ = false);
    124 }
    125 
    126 TryCritScope::~TryCritScope() {
    127   CS_DEBUG_CODE(RTC_DCHECK(lock_was_called_));
    128   if (locked_)
    129     cs_->Leave();
    130 }
    131 
    132 bool TryCritScope::locked() const {
    133   CS_DEBUG_CODE(lock_was_called_ = true);
    134   return locked_;
    135 }
    136 
    137 void GlobalLockPod::Lock() {
    138 #if !defined(WEBRTC_WIN)
    139   const struct timespec ts_null = {0, 0};
    140 #endif
    141 
    142   while (AtomicOps::CompareAndSwap(&lock_acquired, 0, 1)) {
    143 #if defined(WEBRTC_WIN)
    144     ::Sleep(0);
    145 #else
    146     nanosleep(&ts_null, nullptr);
    147 #endif
    148   }
    149 }
    150 
    151 void GlobalLockPod::Unlock() {
    152   int old_value = AtomicOps::CompareAndSwap(&lock_acquired, 1, 0);
    153   RTC_DCHECK_EQ(1, old_value) << "Unlock called without calling Lock first";
    154 }
    155 
    156 GlobalLock::GlobalLock() {
    157   lock_acquired = 0;
    158 }
    159 
    160 GlobalLockScope::GlobalLockScope(GlobalLockPod* lock)
    161     : lock_(lock) {
    162   lock_->Lock();
    163 }
    164 
    165 GlobalLockScope::~GlobalLockScope() {
    166   lock_->Unlock();
    167 }
    168 
    169 }  // namespace rtc
    170