blob: 2100fe9fb42927f61612dbef0cdd41601eba7273 [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
Steve Anton10542f22019-01-11 09:11:00 -080011#include "rtc_base/critical_section.h"
Jiayang Liubef8d2d2015-03-26 14:38:46 -070012
Yves Gerey988cc082018-10-23 12:03:01 +020013#include <time.h>
14
Steve Anton10542f22019-01-11 09:11:00 -080015#include "rtc_base/atomic_ops.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020016#include "rtc_base/checks.h"
Tommi1f3f3c22018-02-17 11:46:14 +010017#include "rtc_base/platform_thread_types.h"
Niels Möllera12c42a2018-07-25 16:05:48 +020018#include "rtc_base/system/unused.h"
Jiayang Liubef8d2d2015-03-26 14:38:46 -070019
tommied281e92016-01-21 23:47:25 -080020// TODO(tommi): Split this file up to per-platform implementation files.
21
Jiayang Liubef8d2d2015-03-26 14:38:46 -070022namespace rtc {
23
Tommi494f2092015-04-27 17:39:23 +020024CriticalSection::CriticalSection() {
25#if defined(WEBRTC_WIN)
26 InitializeCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -070027#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +020028#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommied281e92016-01-21 23:47:25 -080029 lock_queue_ = 0;
30 owning_thread_ = 0;
31 recursion_ = 0;
32 semaphore_ = dispatch_semaphore_create(0);
Yves Gerey665174f2018-06-19 15:03:05 +020033#else
Tommi494f2092015-04-27 17:39:23 +020034 pthread_mutexattr_t mutex_attribute;
35 pthread_mutexattr_init(&mutex_attribute);
36 pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
Oskar Sundbom13471a42019-03-01 16:11:49 +010037#if defined(WEBRTC_MAC)
38 pthread_mutexattr_setpolicy_np(&mutex_attribute,
39 _PTHREAD_MUTEX_POLICY_FAIRSHARE);
40#endif
Tommi494f2092015-04-27 17:39:23 +020041 pthread_mutex_init(&mutex_, &mutex_attribute);
42 pthread_mutexattr_destroy(&mutex_attribute);
Yves Gerey665174f2018-06-19 15:03:05 +020043#endif
Tommi494f2092015-04-27 17:39:23 +020044 CS_DEBUG_CODE(thread_ = 0);
45 CS_DEBUG_CODE(recursion_count_ = 0);
eladalond6e94662017-06-28 07:31:30 -070046 RTC_UNUSED(thread_);
47 RTC_UNUSED(recursion_count_);
48#else
Yves Gerey665174f2018-06-19 15:03:05 +020049#error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +020050#endif
51}
52
53CriticalSection::~CriticalSection() {
54#if defined(WEBRTC_WIN)
55 DeleteCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -070056#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +020057#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommied281e92016-01-21 23:47:25 -080058 dispatch_release(semaphore_);
eladalond6e94662017-06-28 07:31:30 -070059#else
Yves Gerey665174f2018-06-19 15:03:05 +020060 pthread_mutex_destroy(&mutex_);
61#endif
62#else
63#error Unsupported platform.
tommied281e92016-01-21 23:47:25 -080064#endif
Tommi494f2092015-04-27 17:39:23 +020065}
66
danilchap3c6abd22017-09-06 05:46:29 -070067void CriticalSection::Enter() const RTC_EXCLUSIVE_LOCK_FUNCTION() {
Tommi494f2092015-04-27 17:39:23 +020068#if defined(WEBRTC_WIN)
69 EnterCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -070070#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +020071#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommied281e92016-01-21 23:47:25 -080072 int spin = 3000;
tommi7406b962016-01-22 05:13:33 -080073 PlatformThreadRef self = CurrentThreadRef();
tommied281e92016-01-21 23:47:25 -080074 bool have_lock = false;
75 do {
76 // Instead of calling TryEnter() in this loop, we do two interlocked
77 // operations, first a read-only one in order to avoid affecting the lock
78 // cache-line while spinning, in case another thread is using the lock.
tommi7406b962016-01-22 05:13:33 -080079 if (!IsThreadRefEqual(owning_thread_, self)) {
tommied281e92016-01-21 23:47:25 -080080 if (AtomicOps::AcquireLoad(&lock_queue_) == 0) {
81 if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) == 0) {
82 have_lock = true;
83 break;
84 }
85 }
86 } else {
87 AtomicOps::Increment(&lock_queue_);
88 have_lock = true;
89 break;
90 }
91
92 sched_yield();
93 } while (--spin);
94
95 if (!have_lock && AtomicOps::Increment(&lock_queue_) > 1) {
96 // Owning thread cannot be the current thread since TryEnter() would
97 // have succeeded.
tommi7406b962016-01-22 05:13:33 -080098 RTC_DCHECK(!IsThreadRefEqual(owning_thread_, self));
tommied281e92016-01-21 23:47:25 -080099 // Wait for the lock to become available.
100 dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
101 RTC_DCHECK(owning_thread_ == 0);
102 RTC_DCHECK(!recursion_);
103 }
104
105 owning_thread_ = self;
106 ++recursion_;
107
Yves Gerey665174f2018-06-19 15:03:05 +0200108#else
Tommi494f2092015-04-27 17:39:23 +0200109 pthread_mutex_lock(&mutex_);
Yves Gerey665174f2018-06-19 15:03:05 +0200110#endif
tommied281e92016-01-21 23:47:25 -0800111
Yves Gerey665174f2018-06-19 15:03:05 +0200112#if CS_DEBUG_CHECKS
Tommi494f2092015-04-27 17:39:23 +0200113 if (!recursion_count_) {
henrikg91d6ede2015-09-17 00:24:34 -0700114 RTC_DCHECK(!thread_);
tommi7406b962016-01-22 05:13:33 -0800115 thread_ = CurrentThreadRef();
Tommi494f2092015-04-27 17:39:23 +0200116 } else {
henrikg91d6ede2015-09-17 00:24:34 -0700117 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200118 }
119 ++recursion_count_;
Yves Gerey665174f2018-06-19 15:03:05 +0200120#endif
eladalond6e94662017-06-28 07:31:30 -0700121#else
Yves Gerey665174f2018-06-19 15:03:05 +0200122#error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +0200123#endif
124}
125
danilchap3c6abd22017-09-06 05:46:29 -0700126bool CriticalSection::TryEnter() const RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
Tommi494f2092015-04-27 17:39:23 +0200127#if defined(WEBRTC_WIN)
128 return TryEnterCriticalSection(&crit_) != FALSE;
eladalond6e94662017-06-28 07:31:30 -0700129#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +0200130#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommi7406b962016-01-22 05:13:33 -0800131 if (!IsThreadRefEqual(owning_thread_, CurrentThreadRef())) {
tommied281e92016-01-21 23:47:25 -0800132 if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) != 0)
133 return false;
tommi7406b962016-01-22 05:13:33 -0800134 owning_thread_ = CurrentThreadRef();
tommied281e92016-01-21 23:47:25 -0800135 RTC_DCHECK(!recursion_);
136 } else {
137 AtomicOps::Increment(&lock_queue_);
138 }
139 ++recursion_;
Yves Gerey665174f2018-06-19 15:03:05 +0200140#else
Tommi494f2092015-04-27 17:39:23 +0200141 if (pthread_mutex_trylock(&mutex_) != 0)
142 return false;
Yves Gerey665174f2018-06-19 15:03:05 +0200143#endif
144#if CS_DEBUG_CHECKS
Tommi494f2092015-04-27 17:39:23 +0200145 if (!recursion_count_) {
henrikg91d6ede2015-09-17 00:24:34 -0700146 RTC_DCHECK(!thread_);
tommi7406b962016-01-22 05:13:33 -0800147 thread_ = CurrentThreadRef();
Tommi494f2092015-04-27 17:39:23 +0200148 } else {
henrikg91d6ede2015-09-17 00:24:34 -0700149 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200150 }
151 ++recursion_count_;
Yves Gerey665174f2018-06-19 15:03:05 +0200152#endif
Tommi494f2092015-04-27 17:39:23 +0200153 return true;
eladalond6e94662017-06-28 07:31:30 -0700154#else
Yves Gerey665174f2018-06-19 15:03:05 +0200155#error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +0200156#endif
157}
eladalond6e94662017-06-28 07:31:30 -0700158
danilchap3c6abd22017-09-06 05:46:29 -0700159void CriticalSection::Leave() const RTC_UNLOCK_FUNCTION() {
henrikg91d6ede2015-09-17 00:24:34 -0700160 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200161#if defined(WEBRTC_WIN)
162 LeaveCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -0700163#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +0200164#if CS_DEBUG_CHECKS
Tommi494f2092015-04-27 17:39:23 +0200165 --recursion_count_;
henrikg91d6ede2015-09-17 00:24:34 -0700166 RTC_DCHECK(recursion_count_ >= 0);
Tommi494f2092015-04-27 17:39:23 +0200167 if (!recursion_count_)
168 thread_ = 0;
Yves Gerey665174f2018-06-19 15:03:05 +0200169#endif
170#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommi7406b962016-01-22 05:13:33 -0800171 RTC_DCHECK(IsThreadRefEqual(owning_thread_, CurrentThreadRef()));
tommied281e92016-01-21 23:47:25 -0800172 RTC_DCHECK_GE(recursion_, 0);
173 --recursion_;
174 if (!recursion_)
175 owning_thread_ = 0;
176
177 if (AtomicOps::Decrement(&lock_queue_) > 0 && !recursion_)
178 dispatch_semaphore_signal(semaphore_);
eladalond6e94662017-06-28 07:31:30 -0700179#else
Yves Gerey665174f2018-06-19 15:03:05 +0200180 pthread_mutex_unlock(&mutex_);
181#endif
182#else
183#error Unsupported platform.
tommied281e92016-01-21 23:47:25 -0800184#endif
Tommi494f2092015-04-27 17:39:23 +0200185}
186
187bool CriticalSection::CurrentThreadIsOwner() const {
188#if defined(WEBRTC_WIN)
brucedawsona10492f2015-10-06 13:34:30 -0700189 // OwningThread has type HANDLE but actually contains the Thread ID:
190 // http://stackoverflow.com/questions/12675301/why-is-the-owningthread-member-of-critical-section-of-type-handle-when-it-is-de
191 // Converting through size_t avoids the VS 2015 warning C4312: conversion from
192 // 'type1' to 'type2' of greater size
193 return crit_.OwningThread ==
194 reinterpret_cast<HANDLE>(static_cast<size_t>(GetCurrentThreadId()));
eladalond6e94662017-06-28 07:31:30 -0700195#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +0200196#if CS_DEBUG_CHECKS
tommi7406b962016-01-22 05:13:33 -0800197 return IsThreadRefEqual(thread_, CurrentThreadRef());
eladalond6e94662017-06-28 07:31:30 -0700198#else
Yves Gerey665174f2018-06-19 15:03:05 +0200199 return true;
200#endif // CS_DEBUG_CHECKS
201#else
202#error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +0200203#endif
204}
205
Yves Gerey665174f2018-06-19 15:03:05 +0200206CritScope::CritScope(const CriticalSection* cs) : cs_(cs) {
207 cs_->Enter();
208}
209CritScope::~CritScope() {
210 cs_->Leave();
211}
Tommi494f2092015-04-27 17:39:23 +0200212
Peter Boströmaf9e6632016-01-21 16:56:52 +0100213TryCritScope::TryCritScope(const CriticalSection* cs)
Tommi494f2092015-04-27 17:39:23 +0200214 : cs_(cs), locked_(cs->TryEnter()) {
215 CS_DEBUG_CODE(lock_was_called_ = false);
eladalond6e94662017-06-28 07:31:30 -0700216 RTC_UNUSED(lock_was_called_);
Tommi494f2092015-04-27 17:39:23 +0200217}
218
219TryCritScope::~TryCritScope() {
henrikg91d6ede2015-09-17 00:24:34 -0700220 CS_DEBUG_CODE(RTC_DCHECK(lock_was_called_));
Tommi494f2092015-04-27 17:39:23 +0200221 if (locked_)
222 cs_->Leave();
223}
224
225bool TryCritScope::locked() const {
226 CS_DEBUG_CODE(lock_was_called_ = true);
227 return locked_;
228}
229
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700230void GlobalLockPod::Lock() {
tommied281e92016-01-21 23:47:25 -0800231#if !defined(WEBRTC_WIN) && (!defined(WEBRTC_MAC) || USE_NATIVE_MUTEX_ON_MAC)
Tommi494f2092015-04-27 17:39:23 +0200232 const struct timespec ts_null = {0};
233#endif
234
pbos46ad5422015-12-07 14:29:14 -0800235 while (AtomicOps::CompareAndSwap(&lock_acquired, 0, 1)) {
Tommi494f2092015-04-27 17:39:23 +0200236#if defined(WEBRTC_WIN)
237 ::Sleep(0);
tommied281e92016-01-21 23:47:25 -0800238#elif defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
239 sched_yield();
Tommi494f2092015-04-27 17:39:23 +0200240#else
241 nanosleep(&ts_null, nullptr);
242#endif
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700243 }
244}
245
246void GlobalLockPod::Unlock() {
pbos46ad5422015-12-07 14:29:14 -0800247 int old_value = AtomicOps::CompareAndSwap(&lock_acquired, 1, 0);
henrikg91d6ede2015-09-17 00:24:34 -0700248 RTC_DCHECK_EQ(1, old_value) << "Unlock called without calling Lock first";
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700249}
250
pbos46ad5422015-12-07 14:29:14 -0800251GlobalLock::GlobalLock() {
252 lock_acquired = 0;
253}
pbos3c12f4d2015-11-17 03:21:02 -0800254
Yves Gerey665174f2018-06-19 15:03:05 +0200255GlobalLockScope::GlobalLockScope(GlobalLockPod* lock) : lock_(lock) {
Joachim Bauchfec2c6d2015-05-27 23:41:43 +0200256 lock_->Lock();
257}
258
259GlobalLockScope::~GlobalLockScope() {
260 lock_->Unlock();
261}
262
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700263} // namespace rtc