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