OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2015 The WebRTC Project Authors. All rights reserved. | 2 * Copyright 2015 The WebRTC Project Authors. All rights reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/base/criticalsection.h" | 11 #include "webrtc/base/criticalsection.h" |
12 | 12 |
13 #include "webrtc/base/checks.h" | 13 #include "webrtc/base/checks.h" |
14 | 14 |
15 // TODO(tommi): Split this file up to per-platform implementation files. | |
16 | |
15 namespace rtc { | 17 namespace rtc { |
16 | 18 |
17 CriticalSection::CriticalSection() { | 19 CriticalSection::CriticalSection() { |
18 #if defined(WEBRTC_WIN) | 20 #if defined(WEBRTC_WIN) |
19 InitializeCriticalSection(&crit_); | 21 InitializeCriticalSection(&crit_); |
20 #else | 22 #else |
23 #if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC | |
24 lock_queue_ = 0; | |
25 owning_thread_ = 0; | |
26 recursion_ = 0; | |
27 semaphore_ = dispatch_semaphore_create(0); | |
28 #else | |
21 pthread_mutexattr_t mutex_attribute; | 29 pthread_mutexattr_t mutex_attribute; |
22 pthread_mutexattr_init(&mutex_attribute); | 30 pthread_mutexattr_init(&mutex_attribute); |
23 pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE); | 31 pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE); |
24 pthread_mutex_init(&mutex_, &mutex_attribute); | 32 pthread_mutex_init(&mutex_, &mutex_attribute); |
25 pthread_mutexattr_destroy(&mutex_attribute); | 33 pthread_mutexattr_destroy(&mutex_attribute); |
34 #endif | |
26 CS_DEBUG_CODE(thread_ = 0); | 35 CS_DEBUG_CODE(thread_ = 0); |
27 CS_DEBUG_CODE(recursion_count_ = 0); | 36 CS_DEBUG_CODE(recursion_count_ = 0); |
28 #endif | 37 #endif |
29 } | 38 } |
30 | 39 |
31 CriticalSection::~CriticalSection() { | 40 CriticalSection::~CriticalSection() { |
32 #if defined(WEBRTC_WIN) | 41 #if defined(WEBRTC_WIN) |
33 DeleteCriticalSection(&crit_); | 42 DeleteCriticalSection(&crit_); |
34 #else | 43 #else |
44 #if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC | |
45 dispatch_release(semaphore_); | |
46 #else | |
35 pthread_mutex_destroy(&mutex_); | 47 pthread_mutex_destroy(&mutex_); |
36 #endif | 48 #endif |
49 #endif | |
37 } | 50 } |
38 | 51 |
39 void CriticalSection::Enter() const EXCLUSIVE_LOCK_FUNCTION() { | 52 void CriticalSection::Enter() const EXCLUSIVE_LOCK_FUNCTION() { |
40 #if defined(WEBRTC_WIN) | 53 #if defined(WEBRTC_WIN) |
41 EnterCriticalSection(&crit_); | 54 EnterCriticalSection(&crit_); |
42 #else | 55 #else |
56 #if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC | |
57 int spin = 3000; | |
58 pthread_t self = pthread_self(); | |
59 bool have_lock = false; | |
60 do { | |
61 // Instead of calling TryEnter() in this loop, we do two interlocked | |
62 // operations, first a read-only one in order to avoid affecting the lock | |
63 // cache-line while spinning, in case another thread is using the lock. | |
64 if (owning_thread_ != self) { | |
joachim
2016/01/22 08:35:38
Maybe change this to use "pthread_equal" to compar
tommi
2016/01/22 08:51:59
Ah, thanks. I had meant to use PlatformThreadRef a
| |
65 if (AtomicOps::AcquireLoad(&lock_queue_) == 0) { | |
66 if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) == 0) { | |
67 have_lock = true; | |
68 break; | |
69 } | |
70 } | |
71 } else { | |
72 AtomicOps::Increment(&lock_queue_); | |
73 have_lock = true; | |
74 break; | |
75 } | |
76 | |
77 sched_yield(); | |
78 } while (--spin); | |
79 | |
80 if (!have_lock && AtomicOps::Increment(&lock_queue_) > 1) { | |
81 // Owning thread cannot be the current thread since TryEnter() would | |
82 // have succeeded. | |
83 RTC_DCHECK(owning_thread_ != self); | |
joachim
2016/01/22 08:35:38
Same here.
| |
84 // Wait for the lock to become available. | |
85 dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER); | |
86 RTC_DCHECK(owning_thread_ == 0); | |
87 RTC_DCHECK(!recursion_); | |
88 } | |
89 | |
90 owning_thread_ = self; | |
91 ++recursion_; | |
92 | |
93 #else | |
43 pthread_mutex_lock(&mutex_); | 94 pthread_mutex_lock(&mutex_); |
95 #endif | |
96 | |
44 #if CS_DEBUG_CHECKS | 97 #if CS_DEBUG_CHECKS |
45 if (!recursion_count_) { | 98 if (!recursion_count_) { |
46 RTC_DCHECK(!thread_); | 99 RTC_DCHECK(!thread_); |
47 thread_ = pthread_self(); | 100 thread_ = pthread_self(); |
48 } else { | 101 } else { |
49 RTC_DCHECK(CurrentThreadIsOwner()); | 102 RTC_DCHECK(CurrentThreadIsOwner()); |
50 } | 103 } |
51 ++recursion_count_; | 104 ++recursion_count_; |
52 #endif | 105 #endif |
53 #endif | 106 #endif |
54 } | 107 } |
55 | 108 |
56 bool CriticalSection::TryEnter() const EXCLUSIVE_TRYLOCK_FUNCTION(true) { | 109 bool CriticalSection::TryEnter() const EXCLUSIVE_TRYLOCK_FUNCTION(true) { |
57 #if defined(WEBRTC_WIN) | 110 #if defined(WEBRTC_WIN) |
58 return TryEnterCriticalSection(&crit_) != FALSE; | 111 return TryEnterCriticalSection(&crit_) != FALSE; |
59 #else | 112 #else |
113 #if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC | |
114 if (owning_thread_ != pthread_self()) { | |
joachim
2016/01/22 08:35:38
And here.
| |
115 if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) != 0) | |
116 return false; | |
117 owning_thread_ = pthread_self(); | |
118 RTC_DCHECK(!recursion_); | |
119 } else { | |
120 AtomicOps::Increment(&lock_queue_); | |
121 } | |
122 ++recursion_; | |
123 #else | |
60 if (pthread_mutex_trylock(&mutex_) != 0) | 124 if (pthread_mutex_trylock(&mutex_) != 0) |
61 return false; | 125 return false; |
126 #endif | |
62 #if CS_DEBUG_CHECKS | 127 #if CS_DEBUG_CHECKS |
63 if (!recursion_count_) { | 128 if (!recursion_count_) { |
64 RTC_DCHECK(!thread_); | 129 RTC_DCHECK(!thread_); |
65 thread_ = pthread_self(); | 130 thread_ = pthread_self(); |
66 } else { | 131 } else { |
67 RTC_DCHECK(CurrentThreadIsOwner()); | 132 RTC_DCHECK(CurrentThreadIsOwner()); |
68 } | 133 } |
69 ++recursion_count_; | 134 ++recursion_count_; |
70 #endif | 135 #endif |
71 return true; | 136 return true; |
72 #endif | 137 #endif |
73 } | 138 } |
74 void CriticalSection::Leave() const UNLOCK_FUNCTION() { | 139 void CriticalSection::Leave() const UNLOCK_FUNCTION() { |
75 RTC_DCHECK(CurrentThreadIsOwner()); | 140 RTC_DCHECK(CurrentThreadIsOwner()); |
76 #if defined(WEBRTC_WIN) | 141 #if defined(WEBRTC_WIN) |
77 LeaveCriticalSection(&crit_); | 142 LeaveCriticalSection(&crit_); |
78 #else | 143 #else |
79 #if CS_DEBUG_CHECKS | 144 #if CS_DEBUG_CHECKS |
80 --recursion_count_; | 145 --recursion_count_; |
81 RTC_DCHECK(recursion_count_ >= 0); | 146 RTC_DCHECK(recursion_count_ >= 0); |
82 if (!recursion_count_) | 147 if (!recursion_count_) |
83 thread_ = 0; | 148 thread_ = 0; |
84 #endif | 149 #endif |
150 #if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC | |
151 RTC_DCHECK_EQ(owning_thread_, pthread_self()); | |
152 RTC_DCHECK_GE(recursion_, 0); | |
153 --recursion_; | |
154 if (!recursion_) | |
155 owning_thread_ = 0; | |
156 | |
157 if (AtomicOps::Decrement(&lock_queue_) > 0 && !recursion_) | |
158 dispatch_semaphore_signal(semaphore_); | |
159 #else | |
85 pthread_mutex_unlock(&mutex_); | 160 pthread_mutex_unlock(&mutex_); |
86 #endif | 161 #endif |
162 #endif | |
87 } | 163 } |
88 | 164 |
89 bool CriticalSection::CurrentThreadIsOwner() const { | 165 bool CriticalSection::CurrentThreadIsOwner() const { |
90 #if defined(WEBRTC_WIN) | 166 #if defined(WEBRTC_WIN) |
91 // OwningThread has type HANDLE but actually contains the Thread ID: | 167 // 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 | 168 // 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 | 169 // Converting through size_t avoids the VS 2015 warning C4312: conversion from |
94 // 'type1' to 'type2' of greater size | 170 // 'type1' to 'type2' of greater size |
95 return crit_.OwningThread == | 171 return crit_.OwningThread == |
96 reinterpret_cast<HANDLE>(static_cast<size_t>(GetCurrentThreadId())); | 172 reinterpret_cast<HANDLE>(static_cast<size_t>(GetCurrentThreadId())); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
128 if (locked_) | 204 if (locked_) |
129 cs_->Leave(); | 205 cs_->Leave(); |
130 } | 206 } |
131 | 207 |
132 bool TryCritScope::locked() const { | 208 bool TryCritScope::locked() const { |
133 CS_DEBUG_CODE(lock_was_called_ = true); | 209 CS_DEBUG_CODE(lock_was_called_ = true); |
134 return locked_; | 210 return locked_; |
135 } | 211 } |
136 | 212 |
137 void GlobalLockPod::Lock() { | 213 void GlobalLockPod::Lock() { |
138 #if !defined(WEBRTC_WIN) | 214 #if !defined(WEBRTC_WIN) && (!defined(WEBRTC_MAC) || USE_NATIVE_MUTEX_ON_MAC) |
139 const struct timespec ts_null = {0}; | 215 const struct timespec ts_null = {0}; |
140 #endif | 216 #endif |
141 | 217 |
142 while (AtomicOps::CompareAndSwap(&lock_acquired, 0, 1)) { | 218 while (AtomicOps::CompareAndSwap(&lock_acquired, 0, 1)) { |
143 #if defined(WEBRTC_WIN) | 219 #if defined(WEBRTC_WIN) |
144 ::Sleep(0); | 220 ::Sleep(0); |
221 #elif defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC | |
222 sched_yield(); | |
145 #else | 223 #else |
146 nanosleep(&ts_null, nullptr); | 224 nanosleep(&ts_null, nullptr); |
147 #endif | 225 #endif |
148 } | 226 } |
149 } | 227 } |
150 | 228 |
151 void GlobalLockPod::Unlock() { | 229 void GlobalLockPod::Unlock() { |
152 int old_value = AtomicOps::CompareAndSwap(&lock_acquired, 1, 0); | 230 int old_value = AtomicOps::CompareAndSwap(&lock_acquired, 1, 0); |
153 RTC_DCHECK_EQ(1, old_value) << "Unlock called without calling Lock first"; | 231 RTC_DCHECK_EQ(1, old_value) << "Unlock called without calling Lock first"; |
154 } | 232 } |
155 | 233 |
156 GlobalLock::GlobalLock() { | 234 GlobalLock::GlobalLock() { |
157 lock_acquired = 0; | 235 lock_acquired = 0; |
158 } | 236 } |
159 | 237 |
160 GlobalLockScope::GlobalLockScope(GlobalLockPod* lock) | 238 GlobalLockScope::GlobalLockScope(GlobalLockPod* lock) |
161 : lock_(lock) { | 239 : lock_(lock) { |
162 lock_->Lock(); | 240 lock_->Lock(); |
163 } | 241 } |
164 | 242 |
165 GlobalLockScope::~GlobalLockScope() { | 243 GlobalLockScope::~GlobalLockScope() { |
166 lock_->Unlock(); | 244 lock_->Unlock(); |
167 } | 245 } |
168 | 246 |
169 } // namespace rtc | 247 } // namespace rtc |
OLD | NEW |