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