OLD | NEW |
| (Empty) |
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 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 pthread_mutexattr_init(&mutex_attribute); | |
32 pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE); | |
33 pthread_mutex_init(&mutex_, &mutex_attribute); | |
34 pthread_mutexattr_destroy(&mutex_attribute); | |
35 #endif | |
36 CS_DEBUG_CODE(thread_ = 0); | |
37 CS_DEBUG_CODE(recursion_count_ = 0); | |
38 #endif | |
39 } | |
40 | |
41 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 pthread_mutex_destroy(&mutex_); | |
49 #endif | |
50 #endif | |
51 } | |
52 | |
53 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 pthread_mutex_lock(&mutex_); | |
96 #endif | |
97 | |
98 #if CS_DEBUG_CHECKS | |
99 if (!recursion_count_) { | |
100 RTC_DCHECK(!thread_); | |
101 thread_ = CurrentThreadRef(); | |
102 } else { | |
103 RTC_DCHECK(CurrentThreadIsOwner()); | |
104 } | |
105 ++recursion_count_; | |
106 #endif | |
107 #endif | |
108 } | |
109 | |
110 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 if (pthread_mutex_trylock(&mutex_) != 0) | |
126 return false; | |
127 #endif | |
128 #if CS_DEBUG_CHECKS | |
129 if (!recursion_count_) { | |
130 RTC_DCHECK(!thread_); | |
131 thread_ = CurrentThreadRef(); | |
132 } else { | |
133 RTC_DCHECK(CurrentThreadIsOwner()); | |
134 } | |
135 ++recursion_count_; | |
136 #endif | |
137 return true; | |
138 #endif | |
139 } | |
140 void CriticalSection::Leave() const UNLOCK_FUNCTION() { | |
141 RTC_DCHECK(CurrentThreadIsOwner()); | |
142 #if defined(WEBRTC_WIN) | |
143 LeaveCriticalSection(&crit_); | |
144 #else | |
145 #if CS_DEBUG_CHECKS | |
146 --recursion_count_; | |
147 RTC_DCHECK(recursion_count_ >= 0); | |
148 if (!recursion_count_) | |
149 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 pthread_mutex_unlock(&mutex_); | |
162 #endif | |
163 #endif | |
164 } | |
165 | |
166 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 return IsThreadRefEqual(thread_, CurrentThreadRef()); | |
177 #else | |
178 return true; | |
179 #endif // CS_DEBUG_CHECKS | |
180 #endif | |
181 } | |
182 | |
183 CritScope::CritScope(const CriticalSection* cs) : cs_(cs) { cs_->Enter(); } | |
184 CritScope::~CritScope() { cs_->Leave(); } | |
185 | |
186 TryCritScope::TryCritScope(const CriticalSection* cs) | |
187 : cs_(cs), locked_(cs->TryEnter()) { | |
188 CS_DEBUG_CODE(lock_was_called_ = false); | |
189 } | |
190 | |
191 TryCritScope::~TryCritScope() { | |
192 CS_DEBUG_CODE(RTC_DCHECK(lock_was_called_)); | |
193 if (locked_) | |
194 cs_->Leave(); | |
195 } | |
196 | |
197 bool TryCritScope::locked() const { | |
198 CS_DEBUG_CODE(lock_was_called_ = true); | |
199 return locked_; | |
200 } | |
201 | |
202 void GlobalLockPod::Lock() { | |
203 #if !defined(WEBRTC_WIN) && (!defined(WEBRTC_MAC) || USE_NATIVE_MUTEX_ON_MAC) | |
204 const struct timespec ts_null = {0}; | |
205 #endif | |
206 | |
207 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 nanosleep(&ts_null, nullptr); | |
214 #endif | |
215 } | |
216 } | |
217 | |
218 void GlobalLockPod::Unlock() { | |
219 int old_value = AtomicOps::CompareAndSwap(&lock_acquired, 1, 0); | |
220 RTC_DCHECK_EQ(1, old_value) << "Unlock called without calling Lock first"; | |
221 } | |
222 | |
223 GlobalLock::GlobalLock() { | |
224 lock_acquired = 0; | |
225 } | |
226 | |
227 GlobalLockScope::GlobalLockScope(GlobalLockPod* lock) | |
228 : lock_(lock) { | |
229 lock_->Lock(); | |
230 } | |
231 | |
232 GlobalLockScope::~GlobalLockScope() { | |
233 lock_->Unlock(); | |
234 } | |
235 | |
236 } // namespace rtc | |
OLD | NEW |