blob: d8a5b48c0513902587ba21954f60847c516a395a [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"
Niels Möllera12c42a2018-07-25 16:05:48 +020016#include "rtc_base/system/unused.h"
Jiayang Liubef8d2d2015-03-26 14:38:46 -070017
tommied281e92016-01-21 23:47:25 -080018// TODO(tommi): Split this file up to per-platform implementation files.
19
Jiayang Liubef8d2d2015-03-26 14:38:46 -070020namespace rtc {
21
Tommi494f2092015-04-27 17:39:23 +020022CriticalSection::CriticalSection() {
23#if defined(WEBRTC_WIN)
24 InitializeCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -070025#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +020026#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommied281e92016-01-21 23:47:25 -080027 lock_queue_ = 0;
28 owning_thread_ = 0;
29 recursion_ = 0;
30 semaphore_ = dispatch_semaphore_create(0);
Yves Gerey665174f2018-06-19 15:03:05 +020031#else
Tommi494f2092015-04-27 17:39:23 +020032 pthread_mutexattr_t mutex_attribute;
33 pthread_mutexattr_init(&mutex_attribute);
34 pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
35 pthread_mutex_init(&mutex_, &mutex_attribute);
36 pthread_mutexattr_destroy(&mutex_attribute);
Yves Gerey665174f2018-06-19 15:03:05 +020037#endif
Tommi494f2092015-04-27 17:39:23 +020038 CS_DEBUG_CODE(thread_ = 0);
39 CS_DEBUG_CODE(recursion_count_ = 0);
eladalond6e94662017-06-28 07:31:30 -070040 RTC_UNUSED(thread_);
41 RTC_UNUSED(recursion_count_);
42#else
Yves Gerey665174f2018-06-19 15:03:05 +020043#error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +020044#endif
45}
46
47CriticalSection::~CriticalSection() {
48#if defined(WEBRTC_WIN)
49 DeleteCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -070050#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +020051#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommied281e92016-01-21 23:47:25 -080052 dispatch_release(semaphore_);
eladalond6e94662017-06-28 07:31:30 -070053#else
Yves Gerey665174f2018-06-19 15:03:05 +020054 pthread_mutex_destroy(&mutex_);
55#endif
56#else
57#error Unsupported platform.
tommied281e92016-01-21 23:47:25 -080058#endif
Tommi494f2092015-04-27 17:39:23 +020059}
60
danilchap3c6abd22017-09-06 05:46:29 -070061void CriticalSection::Enter() const RTC_EXCLUSIVE_LOCK_FUNCTION() {
Tommi494f2092015-04-27 17:39:23 +020062#if defined(WEBRTC_WIN)
63 EnterCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -070064#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +020065#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommied281e92016-01-21 23:47:25 -080066 int spin = 3000;
tommi7406b962016-01-22 05:13:33 -080067 PlatformThreadRef self = CurrentThreadRef();
tommied281e92016-01-21 23:47:25 -080068 bool have_lock = false;
69 do {
70 // Instead of calling TryEnter() in this loop, we do two interlocked
71 // operations, first a read-only one in order to avoid affecting the lock
72 // cache-line while spinning, in case another thread is using the lock.
tommi7406b962016-01-22 05:13:33 -080073 if (!IsThreadRefEqual(owning_thread_, self)) {
tommied281e92016-01-21 23:47:25 -080074 if (AtomicOps::AcquireLoad(&lock_queue_) == 0) {
75 if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) == 0) {
76 have_lock = true;
77 break;
78 }
79 }
80 } else {
81 AtomicOps::Increment(&lock_queue_);
82 have_lock = true;
83 break;
84 }
85
86 sched_yield();
87 } while (--spin);
88
89 if (!have_lock && AtomicOps::Increment(&lock_queue_) > 1) {
90 // Owning thread cannot be the current thread since TryEnter() would
91 // have succeeded.
tommi7406b962016-01-22 05:13:33 -080092 RTC_DCHECK(!IsThreadRefEqual(owning_thread_, self));
tommied281e92016-01-21 23:47:25 -080093 // Wait for the lock to become available.
94 dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
95 RTC_DCHECK(owning_thread_ == 0);
96 RTC_DCHECK(!recursion_);
97 }
98
99 owning_thread_ = self;
100 ++recursion_;
101
Yves Gerey665174f2018-06-19 15:03:05 +0200102#else
Tommi494f2092015-04-27 17:39:23 +0200103 pthread_mutex_lock(&mutex_);
Yves Gerey665174f2018-06-19 15:03:05 +0200104#endif
tommied281e92016-01-21 23:47:25 -0800105
Yves Gerey665174f2018-06-19 15:03:05 +0200106#if CS_DEBUG_CHECKS
Tommi494f2092015-04-27 17:39:23 +0200107 if (!recursion_count_) {
henrikg91d6ede2015-09-17 00:24:34 -0700108 RTC_DCHECK(!thread_);
tommi7406b962016-01-22 05:13:33 -0800109 thread_ = CurrentThreadRef();
Tommi494f2092015-04-27 17:39:23 +0200110 } else {
henrikg91d6ede2015-09-17 00:24:34 -0700111 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200112 }
113 ++recursion_count_;
Yves Gerey665174f2018-06-19 15:03:05 +0200114#endif
eladalond6e94662017-06-28 07:31:30 -0700115#else
Yves Gerey665174f2018-06-19 15:03:05 +0200116#error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +0200117#endif
118}
119
danilchap3c6abd22017-09-06 05:46:29 -0700120bool CriticalSection::TryEnter() const RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
Tommi494f2092015-04-27 17:39:23 +0200121#if defined(WEBRTC_WIN)
122 return TryEnterCriticalSection(&crit_) != FALSE;
eladalond6e94662017-06-28 07:31:30 -0700123#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +0200124#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommi7406b962016-01-22 05:13:33 -0800125 if (!IsThreadRefEqual(owning_thread_, CurrentThreadRef())) {
tommied281e92016-01-21 23:47:25 -0800126 if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) != 0)
127 return false;
tommi7406b962016-01-22 05:13:33 -0800128 owning_thread_ = CurrentThreadRef();
tommied281e92016-01-21 23:47:25 -0800129 RTC_DCHECK(!recursion_);
130 } else {
131 AtomicOps::Increment(&lock_queue_);
132 }
133 ++recursion_;
Yves Gerey665174f2018-06-19 15:03:05 +0200134#else
Tommi494f2092015-04-27 17:39:23 +0200135 if (pthread_mutex_trylock(&mutex_) != 0)
136 return false;
Yves Gerey665174f2018-06-19 15:03:05 +0200137#endif
138#if CS_DEBUG_CHECKS
Tommi494f2092015-04-27 17:39:23 +0200139 if (!recursion_count_) {
henrikg91d6ede2015-09-17 00:24:34 -0700140 RTC_DCHECK(!thread_);
tommi7406b962016-01-22 05:13:33 -0800141 thread_ = CurrentThreadRef();
Tommi494f2092015-04-27 17:39:23 +0200142 } else {
henrikg91d6ede2015-09-17 00:24:34 -0700143 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200144 }
145 ++recursion_count_;
Yves Gerey665174f2018-06-19 15:03:05 +0200146#endif
Tommi494f2092015-04-27 17:39:23 +0200147 return true;
eladalond6e94662017-06-28 07:31:30 -0700148#else
Yves Gerey665174f2018-06-19 15:03:05 +0200149#error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +0200150#endif
151}
eladalond6e94662017-06-28 07:31:30 -0700152
danilchap3c6abd22017-09-06 05:46:29 -0700153void CriticalSection::Leave() const RTC_UNLOCK_FUNCTION() {
henrikg91d6ede2015-09-17 00:24:34 -0700154 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200155#if defined(WEBRTC_WIN)
156 LeaveCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -0700157#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +0200158#if CS_DEBUG_CHECKS
Tommi494f2092015-04-27 17:39:23 +0200159 --recursion_count_;
henrikg91d6ede2015-09-17 00:24:34 -0700160 RTC_DCHECK(recursion_count_ >= 0);
Tommi494f2092015-04-27 17:39:23 +0200161 if (!recursion_count_)
162 thread_ = 0;
Yves Gerey665174f2018-06-19 15:03:05 +0200163#endif
164#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommi7406b962016-01-22 05:13:33 -0800165 RTC_DCHECK(IsThreadRefEqual(owning_thread_, CurrentThreadRef()));
tommied281e92016-01-21 23:47:25 -0800166 RTC_DCHECK_GE(recursion_, 0);
167 --recursion_;
168 if (!recursion_)
169 owning_thread_ = 0;
170
171 if (AtomicOps::Decrement(&lock_queue_) > 0 && !recursion_)
172 dispatch_semaphore_signal(semaphore_);
eladalond6e94662017-06-28 07:31:30 -0700173#else
Yves Gerey665174f2018-06-19 15:03:05 +0200174 pthread_mutex_unlock(&mutex_);
175#endif
176#else
177#error Unsupported platform.
tommied281e92016-01-21 23:47:25 -0800178#endif
Tommi494f2092015-04-27 17:39:23 +0200179}
180
181bool CriticalSection::CurrentThreadIsOwner() const {
182#if defined(WEBRTC_WIN)
brucedawsona10492f2015-10-06 13:34:30 -0700183 // OwningThread has type HANDLE but actually contains the Thread ID:
184 // http://stackoverflow.com/questions/12675301/why-is-the-owningthread-member-of-critical-section-of-type-handle-when-it-is-de
185 // Converting through size_t avoids the VS 2015 warning C4312: conversion from
186 // 'type1' to 'type2' of greater size
187 return crit_.OwningThread ==
188 reinterpret_cast<HANDLE>(static_cast<size_t>(GetCurrentThreadId()));
eladalond6e94662017-06-28 07:31:30 -0700189#elif defined(WEBRTC_POSIX)
Yves Gerey665174f2018-06-19 15:03:05 +0200190#if CS_DEBUG_CHECKS
tommi7406b962016-01-22 05:13:33 -0800191 return IsThreadRefEqual(thread_, CurrentThreadRef());
eladalond6e94662017-06-28 07:31:30 -0700192#else
Yves Gerey665174f2018-06-19 15:03:05 +0200193 return true;
194#endif // CS_DEBUG_CHECKS
195#else
196#error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +0200197#endif
198}
199
Yves Gerey665174f2018-06-19 15:03:05 +0200200CritScope::CritScope(const CriticalSection* cs) : cs_(cs) {
201 cs_->Enter();
202}
203CritScope::~CritScope() {
204 cs_->Leave();
205}
Tommi494f2092015-04-27 17:39:23 +0200206
Peter Boströmaf9e6632016-01-21 16:56:52 +0100207TryCritScope::TryCritScope(const CriticalSection* cs)
Tommi494f2092015-04-27 17:39:23 +0200208 : cs_(cs), locked_(cs->TryEnter()) {
209 CS_DEBUG_CODE(lock_was_called_ = false);
eladalond6e94662017-06-28 07:31:30 -0700210 RTC_UNUSED(lock_was_called_);
Tommi494f2092015-04-27 17:39:23 +0200211}
212
213TryCritScope::~TryCritScope() {
henrikg91d6ede2015-09-17 00:24:34 -0700214 CS_DEBUG_CODE(RTC_DCHECK(lock_was_called_));
Tommi494f2092015-04-27 17:39:23 +0200215 if (locked_)
216 cs_->Leave();
217}
218
219bool TryCritScope::locked() const {
220 CS_DEBUG_CODE(lock_was_called_ = true);
221 return locked_;
222}
223
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700224void GlobalLockPod::Lock() {
tommied281e92016-01-21 23:47:25 -0800225#if !defined(WEBRTC_WIN) && (!defined(WEBRTC_MAC) || USE_NATIVE_MUTEX_ON_MAC)
Tommi494f2092015-04-27 17:39:23 +0200226 const struct timespec ts_null = {0};
227#endif
228
pbos46ad5422015-12-07 14:29:14 -0800229 while (AtomicOps::CompareAndSwap(&lock_acquired, 0, 1)) {
Tommi494f2092015-04-27 17:39:23 +0200230#if defined(WEBRTC_WIN)
231 ::Sleep(0);
tommied281e92016-01-21 23:47:25 -0800232#elif defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
233 sched_yield();
Tommi494f2092015-04-27 17:39:23 +0200234#else
235 nanosleep(&ts_null, nullptr);
236#endif
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700237 }
238}
239
240void GlobalLockPod::Unlock() {
pbos46ad5422015-12-07 14:29:14 -0800241 int old_value = AtomicOps::CompareAndSwap(&lock_acquired, 1, 0);
henrikg91d6ede2015-09-17 00:24:34 -0700242 RTC_DCHECK_EQ(1, old_value) << "Unlock called without calling Lock first";
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700243}
244
pbos46ad5422015-12-07 14:29:14 -0800245GlobalLock::GlobalLock() {
246 lock_acquired = 0;
247}
pbos3c12f4d2015-11-17 03:21:02 -0800248
Yves Gerey665174f2018-06-19 15:03:05 +0200249GlobalLockScope::GlobalLockScope(GlobalLockPod* lock) : lock_(lock) {
Joachim Bauchfec2c6d2015-05-27 23:41:43 +0200250 lock_->Lock();
251}
252
253GlobalLockScope::~GlobalLockScope() {
254 lock_->Unlock();
255}
256
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700257} // namespace rtc