blob: d92b478b9b214ec2ad22b8729cab27e84637baf2 [file] [log] [blame]
Jiayang Liubef8d2d2015-03-26 14:38:46 -07001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "rtc_base/criticalsection.h"
Jiayang Liubef8d2d2015-03-26 14:38:46 -070012
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020013#include "rtc_base/checks.h"
14#include "rtc_base/platform_thread.h"
Jiayang Liubef8d2d2015-03-26 14:38:46 -070015
tommied281e92016-01-21 23:47:25 -080016// TODO(tommi): Split this file up to per-platform implementation files.
17
Jiayang Liubef8d2d2015-03-26 14:38:46 -070018namespace rtc {
19
Tommi494f2092015-04-27 17:39:23 +020020CriticalSection::CriticalSection() {
21#if defined(WEBRTC_WIN)
22 InitializeCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -070023#elif defined(WEBRTC_POSIX)
24# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommied281e92016-01-21 23:47:25 -080025 lock_queue_ = 0;
26 owning_thread_ = 0;
27 recursion_ = 0;
28 semaphore_ = dispatch_semaphore_create(0);
eladalond6e94662017-06-28 07:31:30 -070029# else
Tommi494f2092015-04-27 17:39:23 +020030 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);
eladalond6e94662017-06-28 07:31:30 -070035# endif
Tommi494f2092015-04-27 17:39:23 +020036 CS_DEBUG_CODE(thread_ = 0);
37 CS_DEBUG_CODE(recursion_count_ = 0);
eladalond6e94662017-06-28 07:31:30 -070038 RTC_UNUSED(thread_);
39 RTC_UNUSED(recursion_count_);
40#else
41# error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +020042#endif
43}
44
45CriticalSection::~CriticalSection() {
46#if defined(WEBRTC_WIN)
47 DeleteCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -070048#elif defined(WEBRTC_POSIX)
49# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommied281e92016-01-21 23:47:25 -080050 dispatch_release(semaphore_);
eladalond6e94662017-06-28 07:31:30 -070051# else
Tommi494f2092015-04-27 17:39:23 +020052 pthread_mutex_destroy(&mutex_);
eladalond6e94662017-06-28 07:31:30 -070053# endif
54#else
55# error Unsupported platform.
tommied281e92016-01-21 23:47:25 -080056#endif
Tommi494f2092015-04-27 17:39:23 +020057}
58
danilchap3c6abd22017-09-06 05:46:29 -070059void CriticalSection::Enter() const RTC_EXCLUSIVE_LOCK_FUNCTION() {
Tommi494f2092015-04-27 17:39:23 +020060#if defined(WEBRTC_WIN)
61 EnterCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -070062#elif defined(WEBRTC_POSIX)
63# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommied281e92016-01-21 23:47:25 -080064 int spin = 3000;
tommi7406b962016-01-22 05:13:33 -080065 PlatformThreadRef self = CurrentThreadRef();
tommied281e92016-01-21 23:47:25 -080066 bool have_lock = false;
67 do {
68 // Instead of calling TryEnter() in this loop, we do two interlocked
69 // operations, first a read-only one in order to avoid affecting the lock
70 // cache-line while spinning, in case another thread is using the lock.
tommi7406b962016-01-22 05:13:33 -080071 if (!IsThreadRefEqual(owning_thread_, self)) {
tommied281e92016-01-21 23:47:25 -080072 if (AtomicOps::AcquireLoad(&lock_queue_) == 0) {
73 if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) == 0) {
74 have_lock = true;
75 break;
76 }
77 }
78 } else {
79 AtomicOps::Increment(&lock_queue_);
80 have_lock = true;
81 break;
82 }
83
84 sched_yield();
85 } while (--spin);
86
87 if (!have_lock && AtomicOps::Increment(&lock_queue_) > 1) {
88 // Owning thread cannot be the current thread since TryEnter() would
89 // have succeeded.
tommi7406b962016-01-22 05:13:33 -080090 RTC_DCHECK(!IsThreadRefEqual(owning_thread_, self));
tommied281e92016-01-21 23:47:25 -080091 // Wait for the lock to become available.
92 dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
93 RTC_DCHECK(owning_thread_ == 0);
94 RTC_DCHECK(!recursion_);
95 }
96
97 owning_thread_ = self;
98 ++recursion_;
99
eladalond6e94662017-06-28 07:31:30 -0700100# else
Tommi494f2092015-04-27 17:39:23 +0200101 pthread_mutex_lock(&mutex_);
eladalond6e94662017-06-28 07:31:30 -0700102# endif
tommied281e92016-01-21 23:47:25 -0800103
eladalond6e94662017-06-28 07:31:30 -0700104# if CS_DEBUG_CHECKS
Tommi494f2092015-04-27 17:39:23 +0200105 if (!recursion_count_) {
henrikg91d6ede2015-09-17 00:24:34 -0700106 RTC_DCHECK(!thread_);
tommi7406b962016-01-22 05:13:33 -0800107 thread_ = CurrentThreadRef();
Tommi494f2092015-04-27 17:39:23 +0200108 } else {
henrikg91d6ede2015-09-17 00:24:34 -0700109 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200110 }
111 ++recursion_count_;
eladalond6e94662017-06-28 07:31:30 -0700112# endif
113#else
114# error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +0200115#endif
116}
117
danilchap3c6abd22017-09-06 05:46:29 -0700118bool CriticalSection::TryEnter() const RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
Tommi494f2092015-04-27 17:39:23 +0200119#if defined(WEBRTC_WIN)
120 return TryEnterCriticalSection(&crit_) != FALSE;
eladalond6e94662017-06-28 07:31:30 -0700121#elif defined(WEBRTC_POSIX)
122# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommi7406b962016-01-22 05:13:33 -0800123 if (!IsThreadRefEqual(owning_thread_, CurrentThreadRef())) {
tommied281e92016-01-21 23:47:25 -0800124 if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) != 0)
125 return false;
tommi7406b962016-01-22 05:13:33 -0800126 owning_thread_ = CurrentThreadRef();
tommied281e92016-01-21 23:47:25 -0800127 RTC_DCHECK(!recursion_);
128 } else {
129 AtomicOps::Increment(&lock_queue_);
130 }
131 ++recursion_;
eladalond6e94662017-06-28 07:31:30 -0700132# else
Tommi494f2092015-04-27 17:39:23 +0200133 if (pthread_mutex_trylock(&mutex_) != 0)
134 return false;
eladalond6e94662017-06-28 07:31:30 -0700135# endif
136# if CS_DEBUG_CHECKS
Tommi494f2092015-04-27 17:39:23 +0200137 if (!recursion_count_) {
henrikg91d6ede2015-09-17 00:24:34 -0700138 RTC_DCHECK(!thread_);
tommi7406b962016-01-22 05:13:33 -0800139 thread_ = CurrentThreadRef();
Tommi494f2092015-04-27 17:39:23 +0200140 } else {
henrikg91d6ede2015-09-17 00:24:34 -0700141 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200142 }
143 ++recursion_count_;
eladalond6e94662017-06-28 07:31:30 -0700144# endif
Tommi494f2092015-04-27 17:39:23 +0200145 return true;
eladalond6e94662017-06-28 07:31:30 -0700146#else
147# error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +0200148#endif
149}
eladalond6e94662017-06-28 07:31:30 -0700150
danilchap3c6abd22017-09-06 05:46:29 -0700151void CriticalSection::Leave() const RTC_UNLOCK_FUNCTION() {
henrikg91d6ede2015-09-17 00:24:34 -0700152 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200153#if defined(WEBRTC_WIN)
154 LeaveCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -0700155#elif defined(WEBRTC_POSIX)
156# if CS_DEBUG_CHECKS
Tommi494f2092015-04-27 17:39:23 +0200157 --recursion_count_;
henrikg91d6ede2015-09-17 00:24:34 -0700158 RTC_DCHECK(recursion_count_ >= 0);
Tommi494f2092015-04-27 17:39:23 +0200159 if (!recursion_count_)
160 thread_ = 0;
eladalond6e94662017-06-28 07:31:30 -0700161# endif
162# if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommi7406b962016-01-22 05:13:33 -0800163 RTC_DCHECK(IsThreadRefEqual(owning_thread_, CurrentThreadRef()));
tommied281e92016-01-21 23:47:25 -0800164 RTC_DCHECK_GE(recursion_, 0);
165 --recursion_;
166 if (!recursion_)
167 owning_thread_ = 0;
168
169 if (AtomicOps::Decrement(&lock_queue_) > 0 && !recursion_)
170 dispatch_semaphore_signal(semaphore_);
eladalond6e94662017-06-28 07:31:30 -0700171# else
Tommi494f2092015-04-27 17:39:23 +0200172 pthread_mutex_unlock(&mutex_);
eladalond6e94662017-06-28 07:31:30 -0700173# endif
174#else
175# error Unsupported platform.
tommied281e92016-01-21 23:47:25 -0800176#endif
Tommi494f2092015-04-27 17:39:23 +0200177}
178
179bool CriticalSection::CurrentThreadIsOwner() const {
180#if defined(WEBRTC_WIN)
brucedawsona10492f2015-10-06 13:34:30 -0700181 // OwningThread has type HANDLE but actually contains the Thread ID:
182 // http://stackoverflow.com/questions/12675301/why-is-the-owningthread-member-of-critical-section-of-type-handle-when-it-is-de
183 // Converting through size_t avoids the VS 2015 warning C4312: conversion from
184 // 'type1' to 'type2' of greater size
185 return crit_.OwningThread ==
186 reinterpret_cast<HANDLE>(static_cast<size_t>(GetCurrentThreadId()));
eladalond6e94662017-06-28 07:31:30 -0700187#elif defined(WEBRTC_POSIX)
188# if CS_DEBUG_CHECKS
tommi7406b962016-01-22 05:13:33 -0800189 return IsThreadRefEqual(thread_, CurrentThreadRef());
eladalond6e94662017-06-28 07:31:30 -0700190# else
Tommi494f2092015-04-27 17:39:23 +0200191 return true;
eladalond6e94662017-06-28 07:31:30 -0700192# endif // CS_DEBUG_CHECKS
193#else
194# error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +0200195#endif
196}
197
Peter Boströmaf9e6632016-01-21 16:56:52 +0100198CritScope::CritScope(const CriticalSection* cs) : cs_(cs) { cs_->Enter(); }
Tommi494f2092015-04-27 17:39:23 +0200199CritScope::~CritScope() { cs_->Leave(); }
200
Peter Boströmaf9e6632016-01-21 16:56:52 +0100201TryCritScope::TryCritScope(const CriticalSection* cs)
Tommi494f2092015-04-27 17:39:23 +0200202 : cs_(cs), locked_(cs->TryEnter()) {
203 CS_DEBUG_CODE(lock_was_called_ = false);
eladalond6e94662017-06-28 07:31:30 -0700204 RTC_UNUSED(lock_was_called_);
Tommi494f2092015-04-27 17:39:23 +0200205}
206
207TryCritScope::~TryCritScope() {
henrikg91d6ede2015-09-17 00:24:34 -0700208 CS_DEBUG_CODE(RTC_DCHECK(lock_was_called_));
Tommi494f2092015-04-27 17:39:23 +0200209 if (locked_)
210 cs_->Leave();
211}
212
213bool TryCritScope::locked() const {
214 CS_DEBUG_CODE(lock_was_called_ = true);
215 return locked_;
216}
217
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700218void GlobalLockPod::Lock() {
tommied281e92016-01-21 23:47:25 -0800219#if !defined(WEBRTC_WIN) && (!defined(WEBRTC_MAC) || USE_NATIVE_MUTEX_ON_MAC)
Tommi494f2092015-04-27 17:39:23 +0200220 const struct timespec ts_null = {0};
221#endif
222
pbos46ad5422015-12-07 14:29:14 -0800223 while (AtomicOps::CompareAndSwap(&lock_acquired, 0, 1)) {
Tommi494f2092015-04-27 17:39:23 +0200224#if defined(WEBRTC_WIN)
225 ::Sleep(0);
tommied281e92016-01-21 23:47:25 -0800226#elif defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
227 sched_yield();
Tommi494f2092015-04-27 17:39:23 +0200228#else
229 nanosleep(&ts_null, nullptr);
230#endif
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700231 }
232}
233
234void GlobalLockPod::Unlock() {
pbos46ad5422015-12-07 14:29:14 -0800235 int old_value = AtomicOps::CompareAndSwap(&lock_acquired, 1, 0);
henrikg91d6ede2015-09-17 00:24:34 -0700236 RTC_DCHECK_EQ(1, old_value) << "Unlock called without calling Lock first";
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700237}
238
pbos46ad5422015-12-07 14:29:14 -0800239GlobalLock::GlobalLock() {
240 lock_acquired = 0;
241}
pbos3c12f4d2015-11-17 03:21:02 -0800242
pbos46ad5422015-12-07 14:29:14 -0800243GlobalLockScope::GlobalLockScope(GlobalLockPod* lock)
244 : lock_(lock) {
Joachim Bauchfec2c6d2015-05-27 23:41:43 +0200245 lock_->Lock();
246}
247
248GlobalLockScope::~GlobalLockScope() {
249 lock_->Unlock();
250}
251
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700252} // namespace rtc