blob: c786a40b19c2caa4ceaa084deecd0a8bf35fb7ea [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
Tommi8d2c5a82018-03-19 11:12:48 +010013#include "rtc_base/atomicops.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020014#include "rtc_base/checks.h"
Tommi1f3f3c22018-02-17 11:46:14 +010015#include "rtc_base/platform_thread_types.h"
Jiayang Liubef8d2d2015-03-26 14:38:46 -070016
tommied281e92016-01-21 23:47:25 -080017// TODO(tommi): Split this file up to per-platform implementation files.
18
Jiayang Liubef8d2d2015-03-26 14:38:46 -070019namespace rtc {
20
Tommi494f2092015-04-27 17:39:23 +020021CriticalSection::CriticalSection() {
22#if defined(WEBRTC_WIN)
23 InitializeCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -070024#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +020025#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommied281e92016-01-21 23:47:25 -080026 lock_queue_ = 0;
27 owning_thread_ = 0;
28 recursion_ = 0;
29 semaphore_ = dispatch_semaphore_create(0);
Yves Gerey665174f2018-06-19 15:03:05 +020030#else
Tommi494f2092015-04-27 17:39:23 +020031 pthread_mutexattr_t mutex_attribute;
32 pthread_mutexattr_init(&mutex_attribute);
33 pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
34 pthread_mutex_init(&mutex_, &mutex_attribute);
35 pthread_mutexattr_destroy(&mutex_attribute);
Yves Gerey665174f2018-06-19 15:03:05 +020036#endif
Tommi494f2092015-04-27 17:39:23 +020037 CS_DEBUG_CODE(thread_ = 0);
38 CS_DEBUG_CODE(recursion_count_ = 0);
eladalond6e94662017-06-28 07:31:30 -070039 RTC_UNUSED(thread_);
40 RTC_UNUSED(recursion_count_);
41#else
Yves Gerey665174f2018-06-19 15:03:05 +020042#error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +020043#endif
44}
45
46CriticalSection::~CriticalSection() {
47#if defined(WEBRTC_WIN)
48 DeleteCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -070049#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +020050#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommied281e92016-01-21 23:47:25 -080051 dispatch_release(semaphore_);
eladalond6e94662017-06-28 07:31:30 -070052#else
Yves Gerey665174f2018-06-19 15:03:05 +020053 pthread_mutex_destroy(&mutex_);
54#endif
55#else
56#error Unsupported platform.
tommied281e92016-01-21 23:47:25 -080057#endif
Tommi494f2092015-04-27 17:39:23 +020058}
59
danilchap3c6abd22017-09-06 05:46:29 -070060void CriticalSection::Enter() const RTC_EXCLUSIVE_LOCK_FUNCTION() {
Tommi494f2092015-04-27 17:39:23 +020061#if defined(WEBRTC_WIN)
62 EnterCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -070063#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +020064#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommied281e92016-01-21 23:47:25 -080065 int spin = 3000;
tommi7406b962016-01-22 05:13:33 -080066 PlatformThreadRef self = CurrentThreadRef();
tommied281e92016-01-21 23:47:25 -080067 bool have_lock = false;
68 do {
69 // Instead of calling TryEnter() in this loop, we do two interlocked
70 // operations, first a read-only one in order to avoid affecting the lock
71 // cache-line while spinning, in case another thread is using the lock.
tommi7406b962016-01-22 05:13:33 -080072 if (!IsThreadRefEqual(owning_thread_, self)) {
tommied281e92016-01-21 23:47:25 -080073 if (AtomicOps::AcquireLoad(&lock_queue_) == 0) {
74 if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) == 0) {
75 have_lock = true;
76 break;
77 }
78 }
79 } else {
80 AtomicOps::Increment(&lock_queue_);
81 have_lock = true;
82 break;
83 }
84
85 sched_yield();
86 } while (--spin);
87
88 if (!have_lock && AtomicOps::Increment(&lock_queue_) > 1) {
89 // Owning thread cannot be the current thread since TryEnter() would
90 // have succeeded.
tommi7406b962016-01-22 05:13:33 -080091 RTC_DCHECK(!IsThreadRefEqual(owning_thread_, self));
tommied281e92016-01-21 23:47:25 -080092 // Wait for the lock to become available.
93 dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
94 RTC_DCHECK(owning_thread_ == 0);
95 RTC_DCHECK(!recursion_);
96 }
97
98 owning_thread_ = self;
99 ++recursion_;
100
Yves Gerey665174f2018-06-19 15:03:05 +0200101#else
Tommi494f2092015-04-27 17:39:23 +0200102 pthread_mutex_lock(&mutex_);
Yves Gerey665174f2018-06-19 15:03:05 +0200103#endif
tommied281e92016-01-21 23:47:25 -0800104
Yves Gerey665174f2018-06-19 15:03:05 +0200105#if CS_DEBUG_CHECKS
Tommi494f2092015-04-27 17:39:23 +0200106 if (!recursion_count_) {
henrikg91d6ede2015-09-17 00:24:34 -0700107 RTC_DCHECK(!thread_);
tommi7406b962016-01-22 05:13:33 -0800108 thread_ = CurrentThreadRef();
Tommi494f2092015-04-27 17:39:23 +0200109 } else {
henrikg91d6ede2015-09-17 00:24:34 -0700110 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200111 }
112 ++recursion_count_;
Yves Gerey665174f2018-06-19 15:03:05 +0200113#endif
eladalond6e94662017-06-28 07:31:30 -0700114#else
Yves Gerey665174f2018-06-19 15:03:05 +0200115#error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +0200116#endif
117}
118
danilchap3c6abd22017-09-06 05:46:29 -0700119bool CriticalSection::TryEnter() const RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
Tommi494f2092015-04-27 17:39:23 +0200120#if defined(WEBRTC_WIN)
121 return TryEnterCriticalSection(&crit_) != FALSE;
eladalond6e94662017-06-28 07:31:30 -0700122#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +0200123#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommi7406b962016-01-22 05:13:33 -0800124 if (!IsThreadRefEqual(owning_thread_, CurrentThreadRef())) {
tommied281e92016-01-21 23:47:25 -0800125 if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) != 0)
126 return false;
tommi7406b962016-01-22 05:13:33 -0800127 owning_thread_ = CurrentThreadRef();
tommied281e92016-01-21 23:47:25 -0800128 RTC_DCHECK(!recursion_);
129 } else {
130 AtomicOps::Increment(&lock_queue_);
131 }
132 ++recursion_;
Yves Gerey665174f2018-06-19 15:03:05 +0200133#else
Tommi494f2092015-04-27 17:39:23 +0200134 if (pthread_mutex_trylock(&mutex_) != 0)
135 return false;
Yves Gerey665174f2018-06-19 15:03:05 +0200136#endif
137#if CS_DEBUG_CHECKS
Tommi494f2092015-04-27 17:39:23 +0200138 if (!recursion_count_) {
henrikg91d6ede2015-09-17 00:24:34 -0700139 RTC_DCHECK(!thread_);
tommi7406b962016-01-22 05:13:33 -0800140 thread_ = CurrentThreadRef();
Tommi494f2092015-04-27 17:39:23 +0200141 } else {
henrikg91d6ede2015-09-17 00:24:34 -0700142 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200143 }
144 ++recursion_count_;
Yves Gerey665174f2018-06-19 15:03:05 +0200145#endif
Tommi494f2092015-04-27 17:39:23 +0200146 return true;
eladalond6e94662017-06-28 07:31:30 -0700147#else
Yves Gerey665174f2018-06-19 15:03:05 +0200148#error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +0200149#endif
150}
eladalond6e94662017-06-28 07:31:30 -0700151
danilchap3c6abd22017-09-06 05:46:29 -0700152void CriticalSection::Leave() const RTC_UNLOCK_FUNCTION() {
henrikg91d6ede2015-09-17 00:24:34 -0700153 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200154#if defined(WEBRTC_WIN)
155 LeaveCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -0700156#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +0200157#if CS_DEBUG_CHECKS
Tommi494f2092015-04-27 17:39:23 +0200158 --recursion_count_;
henrikg91d6ede2015-09-17 00:24:34 -0700159 RTC_DCHECK(recursion_count_ >= 0);
Tommi494f2092015-04-27 17:39:23 +0200160 if (!recursion_count_)
161 thread_ = 0;
Yves Gerey665174f2018-06-19 15:03:05 +0200162#endif
163#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommi7406b962016-01-22 05:13:33 -0800164 RTC_DCHECK(IsThreadRefEqual(owning_thread_, CurrentThreadRef()));
tommied281e92016-01-21 23:47:25 -0800165 RTC_DCHECK_GE(recursion_, 0);
166 --recursion_;
167 if (!recursion_)
168 owning_thread_ = 0;
169
170 if (AtomicOps::Decrement(&lock_queue_) > 0 && !recursion_)
171 dispatch_semaphore_signal(semaphore_);
eladalond6e94662017-06-28 07:31:30 -0700172#else
Yves Gerey665174f2018-06-19 15:03:05 +0200173 pthread_mutex_unlock(&mutex_);
174#endif
175#else
176#error Unsupported platform.
tommied281e92016-01-21 23:47:25 -0800177#endif
Tommi494f2092015-04-27 17:39:23 +0200178}
179
180bool CriticalSection::CurrentThreadIsOwner() const {
181#if defined(WEBRTC_WIN)
brucedawsona10492f2015-10-06 13:34:30 -0700182 // OwningThread has type HANDLE but actually contains the Thread ID:
183 // http://stackoverflow.com/questions/12675301/why-is-the-owningthread-member-of-critical-section-of-type-handle-when-it-is-de
184 // Converting through size_t avoids the VS 2015 warning C4312: conversion from
185 // 'type1' to 'type2' of greater size
186 return crit_.OwningThread ==
187 reinterpret_cast<HANDLE>(static_cast<size_t>(GetCurrentThreadId()));
eladalond6e94662017-06-28 07:31:30 -0700188#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +0200189#if CS_DEBUG_CHECKS
tommi7406b962016-01-22 05:13:33 -0800190 return IsThreadRefEqual(thread_, CurrentThreadRef());
eladalond6e94662017-06-28 07:31:30 -0700191#else
Yves Gerey665174f2018-06-19 15:03:05 +0200192 return true;
193#endif // CS_DEBUG_CHECKS
194#else
195#error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +0200196#endif
197}
198
Yves Gerey665174f2018-06-19 15:03:05 +0200199CritScope::CritScope(const CriticalSection* cs) : cs_(cs) {
200 cs_->Enter();
201}
202CritScope::~CritScope() {
203 cs_->Leave();
204}
Tommi494f2092015-04-27 17:39:23 +0200205
Peter Boströmaf9e6632016-01-21 16:56:52 +0100206TryCritScope::TryCritScope(const CriticalSection* cs)
Tommi494f2092015-04-27 17:39:23 +0200207 : cs_(cs), locked_(cs->TryEnter()) {
208 CS_DEBUG_CODE(lock_was_called_ = false);
eladalond6e94662017-06-28 07:31:30 -0700209 RTC_UNUSED(lock_was_called_);
Tommi494f2092015-04-27 17:39:23 +0200210}
211
212TryCritScope::~TryCritScope() {
henrikg91d6ede2015-09-17 00:24:34 -0700213 CS_DEBUG_CODE(RTC_DCHECK(lock_was_called_));
Tommi494f2092015-04-27 17:39:23 +0200214 if (locked_)
215 cs_->Leave();
216}
217
218bool TryCritScope::locked() const {
219 CS_DEBUG_CODE(lock_was_called_ = true);
220 return locked_;
221}
222
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700223void GlobalLockPod::Lock() {
tommied281e92016-01-21 23:47:25 -0800224#if !defined(WEBRTC_WIN) && (!defined(WEBRTC_MAC) || USE_NATIVE_MUTEX_ON_MAC)
Tommi494f2092015-04-27 17:39:23 +0200225 const struct timespec ts_null = {0};
226#endif
227
pbos46ad5422015-12-07 14:29:14 -0800228 while (AtomicOps::CompareAndSwap(&lock_acquired, 0, 1)) {
Tommi494f2092015-04-27 17:39:23 +0200229#if defined(WEBRTC_WIN)
230 ::Sleep(0);
tommied281e92016-01-21 23:47:25 -0800231#elif defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
232 sched_yield();
Tommi494f2092015-04-27 17:39:23 +0200233#else
234 nanosleep(&ts_null, nullptr);
235#endif
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700236 }
237}
238
239void GlobalLockPod::Unlock() {
pbos46ad5422015-12-07 14:29:14 -0800240 int old_value = AtomicOps::CompareAndSwap(&lock_acquired, 1, 0);
henrikg91d6ede2015-09-17 00:24:34 -0700241 RTC_DCHECK_EQ(1, old_value) << "Unlock called without calling Lock first";
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700242}
243
pbos46ad5422015-12-07 14:29:14 -0800244GlobalLock::GlobalLock() {
245 lock_acquired = 0;
246}
pbos3c12f4d2015-11-17 03:21:02 -0800247
Yves Gerey665174f2018-06-19 15:03:05 +0200248GlobalLockScope::GlobalLockScope(GlobalLockPod* lock) : lock_(lock) {
Joachim Bauchfec2c6d2015-05-27 23:41:43 +0200249 lock_->Lock();
250}
251
252GlobalLockScope::~GlobalLockScope() {
253 lock_->Unlock();
254}
255
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700256} // namespace rtc