blob: 1969edefa5005c5f74f1d598235a4214fc96baa4 [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
Mirko Bonadei84ce3c02019-07-03 14:45:54 +020022#if RTC_DCHECK_IS_ON
23#define RTC_CS_DEBUG_CODE(x) x
24#else // !RTC_DCHECK_IS_ON
25#define RTC_CS_DEBUG_CODE(x)
26#endif // !RTC_DCHECK_IS_ON
27
Jiayang Liubef8d2d2015-03-26 14:38:46 -070028namespace rtc {
29
Tommi494f2092015-04-27 17:39:23 +020030CriticalSection::CriticalSection() {
31#if defined(WEBRTC_WIN)
32 InitializeCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -070033#elif defined(WEBRTC_POSIX)
Mirko Bonadeia0eefc12019-07-08 14:11:02 +020034#if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
tommied281e92016-01-21 23:47:25 -080035 lock_queue_ = 0;
36 owning_thread_ = 0;
37 recursion_ = 0;
38 semaphore_ = dispatch_semaphore_create(0);
Yves Gerey665174f2018-06-19 15:03:05 +020039#else
Tommi494f2092015-04-27 17:39:23 +020040 pthread_mutexattr_t mutex_attribute;
41 pthread_mutexattr_init(&mutex_attribute);
42 pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
Oskar Sundbom13471a42019-03-01 16:11:49 +010043#if defined(WEBRTC_MAC)
44 pthread_mutexattr_setpolicy_np(&mutex_attribute,
45 _PTHREAD_MUTEX_POLICY_FAIRSHARE);
46#endif
Tommi494f2092015-04-27 17:39:23 +020047 pthread_mutex_init(&mutex_, &mutex_attribute);
48 pthread_mutexattr_destroy(&mutex_attribute);
Yves Gerey665174f2018-06-19 15:03:05 +020049#endif
Mirko Bonadei84ce3c02019-07-03 14:45:54 +020050 RTC_CS_DEBUG_CODE(thread_ = 0);
51 RTC_CS_DEBUG_CODE(recursion_count_ = 0);
eladalond6e94662017-06-28 07:31:30 -070052 RTC_UNUSED(thread_);
53 RTC_UNUSED(recursion_count_);
54#else
Yves Gerey665174f2018-06-19 15:03:05 +020055#error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +020056#endif
57}
58
59CriticalSection::~CriticalSection() {
60#if defined(WEBRTC_WIN)
61 DeleteCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -070062#elif defined(WEBRTC_POSIX)
Mirko Bonadeia0eefc12019-07-08 14:11:02 +020063#if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
tommied281e92016-01-21 23:47:25 -080064 dispatch_release(semaphore_);
eladalond6e94662017-06-28 07:31:30 -070065#else
Yves Gerey665174f2018-06-19 15:03:05 +020066 pthread_mutex_destroy(&mutex_);
67#endif
68#else
69#error Unsupported platform.
tommied281e92016-01-21 23:47:25 -080070#endif
Tommi494f2092015-04-27 17:39:23 +020071}
72
danilchap3c6abd22017-09-06 05:46:29 -070073void CriticalSection::Enter() const RTC_EXCLUSIVE_LOCK_FUNCTION() {
Tommi494f2092015-04-27 17:39:23 +020074#if defined(WEBRTC_WIN)
75 EnterCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -070076#elif defined(WEBRTC_POSIX)
Mirko Bonadeia0eefc12019-07-08 14:11:02 +020077#if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
tommied281e92016-01-21 23:47:25 -080078 int spin = 3000;
tommi7406b962016-01-22 05:13:33 -080079 PlatformThreadRef self = CurrentThreadRef();
tommied281e92016-01-21 23:47:25 -080080 bool have_lock = false;
81 do {
82 // Instead of calling TryEnter() in this loop, we do two interlocked
83 // operations, first a read-only one in order to avoid affecting the lock
84 // cache-line while spinning, in case another thread is using the lock.
tommi7406b962016-01-22 05:13:33 -080085 if (!IsThreadRefEqual(owning_thread_, self)) {
tommied281e92016-01-21 23:47:25 -080086 if (AtomicOps::AcquireLoad(&lock_queue_) == 0) {
87 if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) == 0) {
88 have_lock = true;
89 break;
90 }
91 }
92 } else {
93 AtomicOps::Increment(&lock_queue_);
94 have_lock = true;
95 break;
96 }
97
98 sched_yield();
99 } while (--spin);
100
101 if (!have_lock && AtomicOps::Increment(&lock_queue_) > 1) {
102 // Owning thread cannot be the current thread since TryEnter() would
103 // have succeeded.
tommi7406b962016-01-22 05:13:33 -0800104 RTC_DCHECK(!IsThreadRefEqual(owning_thread_, self));
tommied281e92016-01-21 23:47:25 -0800105 // Wait for the lock to become available.
106 dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
107 RTC_DCHECK(owning_thread_ == 0);
108 RTC_DCHECK(!recursion_);
109 }
110
111 owning_thread_ = self;
112 ++recursion_;
113
Yves Gerey665174f2018-06-19 15:03:05 +0200114#else
Tommi494f2092015-04-27 17:39:23 +0200115 pthread_mutex_lock(&mutex_);
Yves Gerey665174f2018-06-19 15:03:05 +0200116#endif
tommied281e92016-01-21 23:47:25 -0800117
Mirko Bonadei84ce3c02019-07-03 14:45:54 +0200118#if RTC_DCHECK_IS_ON
Tommi494f2092015-04-27 17:39:23 +0200119 if (!recursion_count_) {
henrikg91d6ede2015-09-17 00:24:34 -0700120 RTC_DCHECK(!thread_);
tommi7406b962016-01-22 05:13:33 -0800121 thread_ = CurrentThreadRef();
Tommi494f2092015-04-27 17:39:23 +0200122 } else {
henrikg91d6ede2015-09-17 00:24:34 -0700123 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200124 }
125 ++recursion_count_;
Yves Gerey665174f2018-06-19 15:03:05 +0200126#endif
eladalond6e94662017-06-28 07:31:30 -0700127#else
Yves Gerey665174f2018-06-19 15:03:05 +0200128#error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +0200129#endif
130}
131
danilchap3c6abd22017-09-06 05:46:29 -0700132bool CriticalSection::TryEnter() const RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
Tommi494f2092015-04-27 17:39:23 +0200133#if defined(WEBRTC_WIN)
134 return TryEnterCriticalSection(&crit_) != FALSE;
eladalond6e94662017-06-28 07:31:30 -0700135#elif defined(WEBRTC_POSIX)
Mirko Bonadeia0eefc12019-07-08 14:11:02 +0200136#if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
tommi7406b962016-01-22 05:13:33 -0800137 if (!IsThreadRefEqual(owning_thread_, CurrentThreadRef())) {
tommied281e92016-01-21 23:47:25 -0800138 if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) != 0)
139 return false;
tommi7406b962016-01-22 05:13:33 -0800140 owning_thread_ = CurrentThreadRef();
tommied281e92016-01-21 23:47:25 -0800141 RTC_DCHECK(!recursion_);
142 } else {
143 AtomicOps::Increment(&lock_queue_);
144 }
145 ++recursion_;
Yves Gerey665174f2018-06-19 15:03:05 +0200146#else
Tommi494f2092015-04-27 17:39:23 +0200147 if (pthread_mutex_trylock(&mutex_) != 0)
148 return false;
Yves Gerey665174f2018-06-19 15:03:05 +0200149#endif
Mirko Bonadei84ce3c02019-07-03 14:45:54 +0200150#if RTC_DCHECK_IS_ON
Tommi494f2092015-04-27 17:39:23 +0200151 if (!recursion_count_) {
henrikg91d6ede2015-09-17 00:24:34 -0700152 RTC_DCHECK(!thread_);
tommi7406b962016-01-22 05:13:33 -0800153 thread_ = CurrentThreadRef();
Tommi494f2092015-04-27 17:39:23 +0200154 } else {
henrikg91d6ede2015-09-17 00:24:34 -0700155 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200156 }
157 ++recursion_count_;
Yves Gerey665174f2018-06-19 15:03:05 +0200158#endif
Tommi494f2092015-04-27 17:39:23 +0200159 return true;
eladalond6e94662017-06-28 07:31:30 -0700160#else
Yves Gerey665174f2018-06-19 15:03:05 +0200161#error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +0200162#endif
163}
eladalond6e94662017-06-28 07:31:30 -0700164
danilchap3c6abd22017-09-06 05:46:29 -0700165void CriticalSection::Leave() const RTC_UNLOCK_FUNCTION() {
henrikg91d6ede2015-09-17 00:24:34 -0700166 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200167#if defined(WEBRTC_WIN)
168 LeaveCriticalSection(&crit_);
eladalond6e94662017-06-28 07:31:30 -0700169#elif defined(WEBRTC_POSIX)
Mirko Bonadei84ce3c02019-07-03 14:45:54 +0200170#if RTC_DCHECK_IS_ON
Tommi494f2092015-04-27 17:39:23 +0200171 --recursion_count_;
henrikg91d6ede2015-09-17 00:24:34 -0700172 RTC_DCHECK(recursion_count_ >= 0);
Tommi494f2092015-04-27 17:39:23 +0200173 if (!recursion_count_)
174 thread_ = 0;
Yves Gerey665174f2018-06-19 15:03:05 +0200175#endif
Mirko Bonadeia0eefc12019-07-08 14:11:02 +0200176#if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
tommi7406b962016-01-22 05:13:33 -0800177 RTC_DCHECK(IsThreadRefEqual(owning_thread_, CurrentThreadRef()));
tommied281e92016-01-21 23:47:25 -0800178 RTC_DCHECK_GE(recursion_, 0);
179 --recursion_;
180 if (!recursion_)
181 owning_thread_ = 0;
182
183 if (AtomicOps::Decrement(&lock_queue_) > 0 && !recursion_)
184 dispatch_semaphore_signal(semaphore_);
eladalond6e94662017-06-28 07:31:30 -0700185#else
Yves Gerey665174f2018-06-19 15:03:05 +0200186 pthread_mutex_unlock(&mutex_);
187#endif
188#else
189#error Unsupported platform.
tommied281e92016-01-21 23:47:25 -0800190#endif
Tommi494f2092015-04-27 17:39:23 +0200191}
192
193bool CriticalSection::CurrentThreadIsOwner() const {
194#if defined(WEBRTC_WIN)
brucedawsona10492f2015-10-06 13:34:30 -0700195 // OwningThread has type HANDLE but actually contains the Thread ID:
196 // http://stackoverflow.com/questions/12675301/why-is-the-owningthread-member-of-critical-section-of-type-handle-when-it-is-de
197 // Converting through size_t avoids the VS 2015 warning C4312: conversion from
198 // 'type1' to 'type2' of greater size
199 return crit_.OwningThread ==
200 reinterpret_cast<HANDLE>(static_cast<size_t>(GetCurrentThreadId()));
eladalond6e94662017-06-28 07:31:30 -0700201#elif defined(WEBRTC_POSIX)
Mirko Bonadei84ce3c02019-07-03 14:45:54 +0200202#if RTC_DCHECK_IS_ON
tommi7406b962016-01-22 05:13:33 -0800203 return IsThreadRefEqual(thread_, CurrentThreadRef());
eladalond6e94662017-06-28 07:31:30 -0700204#else
Yves Gerey665174f2018-06-19 15:03:05 +0200205 return true;
Mirko Bonadei84ce3c02019-07-03 14:45:54 +0200206#endif // RTC_DCHECK_IS_ON
Yves Gerey665174f2018-06-19 15:03:05 +0200207#else
208#error Unsupported platform.
Tommi494f2092015-04-27 17:39:23 +0200209#endif
210}
211
Yves Gerey665174f2018-06-19 15:03:05 +0200212CritScope::CritScope(const CriticalSection* cs) : cs_(cs) {
213 cs_->Enter();
214}
215CritScope::~CritScope() {
216 cs_->Leave();
217}
Tommi494f2092015-04-27 17:39:23 +0200218
Danil Chapovalov5740f3e2019-10-10 11:12:15 +0200219void GlobalLock::Lock() {
Mirko Bonadeia0eefc12019-07-08 14:11:02 +0200220#if !defined(WEBRTC_WIN) && \
221 (!defined(WEBRTC_MAC) || RTC_USE_NATIVE_MUTEX_ON_MAC)
Tommi494f2092015-04-27 17:39:23 +0200222 const struct timespec ts_null = {0};
223#endif
224
Danil Chapovalov5740f3e2019-10-10 11:12:15 +0200225 while (AtomicOps::CompareAndSwap(&lock_acquired_, 0, 1)) {
Tommi494f2092015-04-27 17:39:23 +0200226#if defined(WEBRTC_WIN)
227 ::Sleep(0);
Mirko Bonadeia0eefc12019-07-08 14:11:02 +0200228#elif defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
tommied281e92016-01-21 23:47:25 -0800229 sched_yield();
Tommi494f2092015-04-27 17:39:23 +0200230#else
231 nanosleep(&ts_null, nullptr);
232#endif
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700233 }
234}
235
Danil Chapovalov5740f3e2019-10-10 11:12:15 +0200236void GlobalLock::Unlock() {
237 int old_value = AtomicOps::CompareAndSwap(&lock_acquired_, 1, 0);
henrikg91d6ede2015-09-17 00:24:34 -0700238 RTC_DCHECK_EQ(1, old_value) << "Unlock called without calling Lock first";
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700239}
240
Danil Chapovalov5740f3e2019-10-10 11:12:15 +0200241GlobalLockScope::GlobalLockScope(GlobalLock* lock) : lock_(lock) {
Joachim Bauchfec2c6d2015-05-27 23:41:43 +0200242 lock_->Lock();
243}
244
245GlobalLockScope::~GlobalLockScope() {
246 lock_->Unlock();
247}
248
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700249} // namespace rtc