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 "rtc_base/deprecated/recursive_critical_section.h"
12 
13 #include <time.h>
14 
15 #include "rtc_base/atomic_ops.h"
16 #include "rtc_base/checks.h"
17 #include "rtc_base/platform_thread_types.h"
18 #include "rtc_base/synchronization/yield.h"
19 #include "rtc_base/system/unused.h"
20 
21 #if RTC_DCHECK_IS_ON
22 #define RTC_CS_DEBUG_CODE(x) x
23 #else  // !RTC_DCHECK_IS_ON
24 #define RTC_CS_DEBUG_CODE(x)
25 #endif  // !RTC_DCHECK_IS_ON
26 
27 namespace rtc {
28 
RecursiveCriticalSection()29 RecursiveCriticalSection::RecursiveCriticalSection() {
30 #if defined(WEBRTC_WIN)
31   InitializeCriticalSection(&crit_);
32 #elif defined(WEBRTC_POSIX)
33 #if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
34   lock_queue_ = 0;
35   owning_thread_ = 0;
36   recursion_ = 0;
37   semaphore_ = dispatch_semaphore_create(0);
38 #else
39   pthread_mutexattr_t mutex_attribute;
40   pthread_mutexattr_init(&mutex_attribute);
41   pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
42 #if defined(WEBRTC_MAC)
43   pthread_mutexattr_setpolicy_np(&mutex_attribute,
44                                  _PTHREAD_MUTEX_POLICY_FIRSTFIT);
45 #endif
46   pthread_mutex_init(&mutex_, &mutex_attribute);
47   pthread_mutexattr_destroy(&mutex_attribute);
48 #endif
49   RTC_CS_DEBUG_CODE(thread_ = 0);
50   RTC_CS_DEBUG_CODE(recursion_count_ = 0);
51   RTC_UNUSED(thread_);
52   RTC_UNUSED(recursion_count_);
53 #else
54 #error Unsupported platform.
55 #endif
56 }
57 
~RecursiveCriticalSection()58 RecursiveCriticalSection::~RecursiveCriticalSection() {
59 #if defined(WEBRTC_WIN)
60   DeleteCriticalSection(&crit_);
61 #elif defined(WEBRTC_POSIX)
62 #if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
63   dispatch_release(semaphore_);
64 #else
65   pthread_mutex_destroy(&mutex_);
66 #endif
67 #else
68 #error Unsupported platform.
69 #endif
70 }
71 
Enter() const72 void RecursiveCriticalSection::Enter() const RTC_EXCLUSIVE_LOCK_FUNCTION() {
73 #if defined(WEBRTC_WIN)
74   EnterCriticalSection(&crit_);
75 #elif defined(WEBRTC_POSIX)
76 #if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
77   int spin = 3000;
78   PlatformThreadRef self = CurrentThreadRef();
79   bool have_lock = false;
80   do {
81     // Instead of calling TryEnter() in this loop, we do two interlocked
82     // operations, first a read-only one in order to avoid affecting the lock
83     // cache-line while spinning, in case another thread is using the lock.
84     if (!IsThreadRefEqual(owning_thread_, self)) {
85       if (AtomicOps::AcquireLoad(&lock_queue_) == 0) {
86         if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) == 0) {
87           have_lock = true;
88           break;
89         }
90       }
91     } else {
92       AtomicOps::Increment(&lock_queue_);
93       have_lock = true;
94       break;
95     }
96 
97     sched_yield();
98   } while (--spin);
99 
100   if (!have_lock && AtomicOps::Increment(&lock_queue_) > 1) {
101     // Owning thread cannot be the current thread since TryEnter() would
102     // have succeeded.
103     RTC_DCHECK(!IsThreadRefEqual(owning_thread_, self));
104     // Wait for the lock to become available.
105     dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
106     RTC_DCHECK(owning_thread_ == 0);
107     RTC_DCHECK(!recursion_);
108   }
109 
110   owning_thread_ = self;
111   ++recursion_;
112 
113 #else
114   pthread_mutex_lock(&mutex_);
115 #endif
116 
117 #if RTC_DCHECK_IS_ON
118   if (!recursion_count_) {
119     RTC_DCHECK(!thread_);
120     thread_ = CurrentThreadRef();
121   } else {
122     RTC_DCHECK(CurrentThreadIsOwner());
123   }
124   ++recursion_count_;
125 #endif
126 #else
127 #error Unsupported platform.
128 #endif
129 }
130 
TryEnter() const131 bool RecursiveCriticalSection::TryEnter() const
132     RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
133 #if defined(WEBRTC_WIN)
134   return TryEnterCriticalSection(&crit_) != FALSE;
135 #elif defined(WEBRTC_POSIX)
136 #if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
137   if (!IsThreadRefEqual(owning_thread_, CurrentThreadRef())) {
138     if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) != 0)
139       return false;
140     owning_thread_ = CurrentThreadRef();
141     RTC_DCHECK(!recursion_);
142   } else {
143     AtomicOps::Increment(&lock_queue_);
144   }
145   ++recursion_;
146 #else
147   if (pthread_mutex_trylock(&mutex_) != 0)
148     return false;
149 #endif
150 #if RTC_DCHECK_IS_ON
151   if (!recursion_count_) {
152     RTC_DCHECK(!thread_);
153     thread_ = CurrentThreadRef();
154   } else {
155     RTC_DCHECK(CurrentThreadIsOwner());
156   }
157   ++recursion_count_;
158 #endif
159   return true;
160 #else
161 #error Unsupported platform.
162 #endif
163 }
164 
Leave() const165 void RecursiveCriticalSection::Leave() const RTC_UNLOCK_FUNCTION() {
166   RTC_DCHECK(CurrentThreadIsOwner());
167 #if defined(WEBRTC_WIN)
168   LeaveCriticalSection(&crit_);
169 #elif defined(WEBRTC_POSIX)
170 #if RTC_DCHECK_IS_ON
171   --recursion_count_;
172   RTC_DCHECK(recursion_count_ >= 0);
173   if (!recursion_count_)
174     thread_ = 0;
175 #endif
176 #if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
177   RTC_DCHECK(IsThreadRefEqual(owning_thread_, CurrentThreadRef()));
178   RTC_DCHECK_GE(recursion_, 0);
179   --recursion_;
180   if (!recursion_)
181     owning_thread_ = 0;
182 
183   if (AtomicOps::Decrement(&lock_queue_) > 0 && !recursion_)
184     dispatch_semaphore_signal(semaphore_);
185 #else
186   pthread_mutex_unlock(&mutex_);
187 #endif
188 #else
189 #error Unsupported platform.
190 #endif
191 }
192 
CurrentThreadIsOwner() const193 bool RecursiveCriticalSection::CurrentThreadIsOwner() const {
194 #if defined(WEBRTC_WIN)
195   // OwningThread has type HANDLE but actually contains the Thread ID:
196   // http://stackoverflow.com/questions/12675301/why-is-the-owningthread-member-of-critical-section-of-type-handle-when-it-is-de
197   // Converting through size_t avoids the VS 2015 warning C4312: conversion from
198   // 'type1' to 'type2' of greater size
199   return crit_.OwningThread ==
200          reinterpret_cast<HANDLE>(static_cast<size_t>(GetCurrentThreadId()));
201 #elif defined(WEBRTC_POSIX)
202 #if RTC_DCHECK_IS_ON
203   return IsThreadRefEqual(thread_, CurrentThreadRef());
204 #else
205   return true;
206 #endif  // RTC_DCHECK_IS_ON
207 #else
208 #error Unsupported platform.
209 #endif
210 }
211 
CritScope(const RecursiveCriticalSection * cs)212 CritScope::CritScope(const RecursiveCriticalSection* cs) : cs_(cs) {
213   cs_->Enter();
214 }
~CritScope()215 CritScope::~CritScope() {
216   cs_->Leave();
217 }
218 
219 }  // namespace rtc
220