blob: d3f42f4a9fe90e532ef31e1523936bba12324a04 [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
11#include "webrtc/base/criticalsection.h"
12
13#include "webrtc/base/checks.h"
Jiayang Liubef8d2d2015-03-26 14:38:46 -070014
tommied281e92016-01-21 23:47:25 -080015// TODO(tommi): Split this file up to per-platform implementation files.
16
Jiayang Liubef8d2d2015-03-26 14:38:46 -070017namespace rtc {
18
Tommi494f2092015-04-27 17:39:23 +020019CriticalSection::CriticalSection() {
20#if defined(WEBRTC_WIN)
21 InitializeCriticalSection(&crit_);
22#else
tommied281e92016-01-21 23:47:25 -080023#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
24 lock_queue_ = 0;
25 owning_thread_ = 0;
26 recursion_ = 0;
27 semaphore_ = dispatch_semaphore_create(0);
28#else
Tommi494f2092015-04-27 17:39:23 +020029 pthread_mutexattr_t mutex_attribute;
30 pthread_mutexattr_init(&mutex_attribute);
31 pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
32 pthread_mutex_init(&mutex_, &mutex_attribute);
33 pthread_mutexattr_destroy(&mutex_attribute);
tommied281e92016-01-21 23:47:25 -080034#endif
Tommi494f2092015-04-27 17:39:23 +020035 CS_DEBUG_CODE(thread_ = 0);
36 CS_DEBUG_CODE(recursion_count_ = 0);
37#endif
38}
39
40CriticalSection::~CriticalSection() {
41#if defined(WEBRTC_WIN)
42 DeleteCriticalSection(&crit_);
43#else
tommied281e92016-01-21 23:47:25 -080044#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
45 dispatch_release(semaphore_);
46#else
Tommi494f2092015-04-27 17:39:23 +020047 pthread_mutex_destroy(&mutex_);
48#endif
tommied281e92016-01-21 23:47:25 -080049#endif
Tommi494f2092015-04-27 17:39:23 +020050}
51
Peter Boströmaf9e6632016-01-21 16:56:52 +010052void CriticalSection::Enter() const EXCLUSIVE_LOCK_FUNCTION() {
Tommi494f2092015-04-27 17:39:23 +020053#if defined(WEBRTC_WIN)
54 EnterCriticalSection(&crit_);
55#else
tommied281e92016-01-21 23:47:25 -080056#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
57 int spin = 3000;
58 pthread_t self = pthread_self();
59 bool have_lock = false;
60 do {
61 // Instead of calling TryEnter() in this loop, we do two interlocked
62 // operations, first a read-only one in order to avoid affecting the lock
63 // cache-line while spinning, in case another thread is using the lock.
64 if (owning_thread_ != self) {
65 if (AtomicOps::AcquireLoad(&lock_queue_) == 0) {
66 if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) == 0) {
67 have_lock = true;
68 break;
69 }
70 }
71 } else {
72 AtomicOps::Increment(&lock_queue_);
73 have_lock = true;
74 break;
75 }
76
77 sched_yield();
78 } while (--spin);
79
80 if (!have_lock && AtomicOps::Increment(&lock_queue_) > 1) {
81 // Owning thread cannot be the current thread since TryEnter() would
82 // have succeeded.
83 RTC_DCHECK(owning_thread_ != self);
84 // Wait for the lock to become available.
85 dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
86 RTC_DCHECK(owning_thread_ == 0);
87 RTC_DCHECK(!recursion_);
88 }
89
90 owning_thread_ = self;
91 ++recursion_;
92
93#else
Tommi494f2092015-04-27 17:39:23 +020094 pthread_mutex_lock(&mutex_);
tommied281e92016-01-21 23:47:25 -080095#endif
96
Tommi494f2092015-04-27 17:39:23 +020097#if CS_DEBUG_CHECKS
98 if (!recursion_count_) {
henrikg91d6ede2015-09-17 00:24:34 -070099 RTC_DCHECK(!thread_);
Tommi494f2092015-04-27 17:39:23 +0200100 thread_ = pthread_self();
101 } else {
henrikg91d6ede2015-09-17 00:24:34 -0700102 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200103 }
104 ++recursion_count_;
105#endif
106#endif
107}
108
Peter Boströmaf9e6632016-01-21 16:56:52 +0100109bool CriticalSection::TryEnter() const EXCLUSIVE_TRYLOCK_FUNCTION(true) {
Tommi494f2092015-04-27 17:39:23 +0200110#if defined(WEBRTC_WIN)
111 return TryEnterCriticalSection(&crit_) != FALSE;
112#else
tommied281e92016-01-21 23:47:25 -0800113#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
114 if (owning_thread_ != pthread_self()) {
115 if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) != 0)
116 return false;
117 owning_thread_ = pthread_self();
118 RTC_DCHECK(!recursion_);
119 } else {
120 AtomicOps::Increment(&lock_queue_);
121 }
122 ++recursion_;
123#else
Tommi494f2092015-04-27 17:39:23 +0200124 if (pthread_mutex_trylock(&mutex_) != 0)
125 return false;
tommied281e92016-01-21 23:47:25 -0800126#endif
Tommi494f2092015-04-27 17:39:23 +0200127#if CS_DEBUG_CHECKS
128 if (!recursion_count_) {
henrikg91d6ede2015-09-17 00:24:34 -0700129 RTC_DCHECK(!thread_);
Tommi494f2092015-04-27 17:39:23 +0200130 thread_ = pthread_self();
131 } else {
henrikg91d6ede2015-09-17 00:24:34 -0700132 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200133 }
134 ++recursion_count_;
135#endif
136 return true;
137#endif
138}
Peter Boströmaf9e6632016-01-21 16:56:52 +0100139void CriticalSection::Leave() const UNLOCK_FUNCTION() {
henrikg91d6ede2015-09-17 00:24:34 -0700140 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200141#if defined(WEBRTC_WIN)
142 LeaveCriticalSection(&crit_);
143#else
144#if CS_DEBUG_CHECKS
145 --recursion_count_;
henrikg91d6ede2015-09-17 00:24:34 -0700146 RTC_DCHECK(recursion_count_ >= 0);
Tommi494f2092015-04-27 17:39:23 +0200147 if (!recursion_count_)
148 thread_ = 0;
149#endif
tommied281e92016-01-21 23:47:25 -0800150#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
151 RTC_DCHECK_EQ(owning_thread_, pthread_self());
152 RTC_DCHECK_GE(recursion_, 0);
153 --recursion_;
154 if (!recursion_)
155 owning_thread_ = 0;
156
157 if (AtomicOps::Decrement(&lock_queue_) > 0 && !recursion_)
158 dispatch_semaphore_signal(semaphore_);
159#else
Tommi494f2092015-04-27 17:39:23 +0200160 pthread_mutex_unlock(&mutex_);
161#endif
tommied281e92016-01-21 23:47:25 -0800162#endif
Tommi494f2092015-04-27 17:39:23 +0200163}
164
165bool CriticalSection::CurrentThreadIsOwner() const {
166#if defined(WEBRTC_WIN)
brucedawsona10492f2015-10-06 13:34:30 -0700167 // OwningThread has type HANDLE but actually contains the Thread ID:
168 // http://stackoverflow.com/questions/12675301/why-is-the-owningthread-member-of-critical-section-of-type-handle-when-it-is-de
169 // Converting through size_t avoids the VS 2015 warning C4312: conversion from
170 // 'type1' to 'type2' of greater size
171 return crit_.OwningThread ==
172 reinterpret_cast<HANDLE>(static_cast<size_t>(GetCurrentThreadId()));
Tommi494f2092015-04-27 17:39:23 +0200173#else
174#if CS_DEBUG_CHECKS
175 return pthread_equal(thread_, pthread_self());
176#else
177 return true;
178#endif // CS_DEBUG_CHECKS
179#endif
180}
181
182bool CriticalSection::IsLocked() const {
183#if defined(WEBRTC_WIN)
184 return crit_.LockCount != -1;
185#else
186#if CS_DEBUG_CHECKS
187 return thread_ != 0;
188#else
189 return true;
190#endif
191#endif
192}
193
Peter Boströmaf9e6632016-01-21 16:56:52 +0100194CritScope::CritScope(const CriticalSection* cs) : cs_(cs) { cs_->Enter(); }
Tommi494f2092015-04-27 17:39:23 +0200195CritScope::~CritScope() { cs_->Leave(); }
196
Peter Boströmaf9e6632016-01-21 16:56:52 +0100197TryCritScope::TryCritScope(const CriticalSection* cs)
Tommi494f2092015-04-27 17:39:23 +0200198 : cs_(cs), locked_(cs->TryEnter()) {
199 CS_DEBUG_CODE(lock_was_called_ = false);
200}
201
202TryCritScope::~TryCritScope() {
henrikg91d6ede2015-09-17 00:24:34 -0700203 CS_DEBUG_CODE(RTC_DCHECK(lock_was_called_));
Tommi494f2092015-04-27 17:39:23 +0200204 if (locked_)
205 cs_->Leave();
206}
207
208bool TryCritScope::locked() const {
209 CS_DEBUG_CODE(lock_was_called_ = true);
210 return locked_;
211}
212
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700213void GlobalLockPod::Lock() {
tommied281e92016-01-21 23:47:25 -0800214#if !defined(WEBRTC_WIN) && (!defined(WEBRTC_MAC) || USE_NATIVE_MUTEX_ON_MAC)
Tommi494f2092015-04-27 17:39:23 +0200215 const struct timespec ts_null = {0};
216#endif
217
pbos46ad5422015-12-07 14:29:14 -0800218 while (AtomicOps::CompareAndSwap(&lock_acquired, 0, 1)) {
Tommi494f2092015-04-27 17:39:23 +0200219#if defined(WEBRTC_WIN)
220 ::Sleep(0);
tommied281e92016-01-21 23:47:25 -0800221#elif defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
222 sched_yield();
Tommi494f2092015-04-27 17:39:23 +0200223#else
224 nanosleep(&ts_null, nullptr);
225#endif
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700226 }
227}
228
229void GlobalLockPod::Unlock() {
pbos46ad5422015-12-07 14:29:14 -0800230 int old_value = AtomicOps::CompareAndSwap(&lock_acquired, 1, 0);
henrikg91d6ede2015-09-17 00:24:34 -0700231 RTC_DCHECK_EQ(1, old_value) << "Unlock called without calling Lock first";
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700232}
233
pbos46ad5422015-12-07 14:29:14 -0800234GlobalLock::GlobalLock() {
235 lock_acquired = 0;
236}
pbos3c12f4d2015-11-17 03:21:02 -0800237
pbos46ad5422015-12-07 14:29:14 -0800238GlobalLockScope::GlobalLockScope(GlobalLockPod* lock)
239 : lock_(lock) {
Joachim Bauchfec2c6d2015-05-27 23:41:43 +0200240 lock_->Lock();
241}
242
243GlobalLockScope::~GlobalLockScope() {
244 lock_->Unlock();
245}
246
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700247} // namespace rtc