blob: 63582ebae8c3e716047e2fd0dca869983b30a8c0 [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"
tommi7406b962016-01-22 05:13:33 -080014#include "webrtc/base/platform_thread.h"
Jiayang Liubef8d2d2015-03-26 14:38:46 -070015
tommied281e92016-01-21 23:47:25 -080016// TODO(tommi): Split this file up to per-platform implementation files.
17
Jiayang Liubef8d2d2015-03-26 14:38:46 -070018namespace rtc {
19
Tommi494f2092015-04-27 17:39:23 +020020CriticalSection::CriticalSection() {
21#if defined(WEBRTC_WIN)
22 InitializeCriticalSection(&crit_);
23#else
tommied281e92016-01-21 23:47:25 -080024#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
25 lock_queue_ = 0;
26 owning_thread_ = 0;
27 recursion_ = 0;
28 semaphore_ = dispatch_semaphore_create(0);
29#else
Tommi494f2092015-04-27 17:39:23 +020030 pthread_mutexattr_t mutex_attribute;
31 pthread_mutexattr_init(&mutex_attribute);
32 pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
33 pthread_mutex_init(&mutex_, &mutex_attribute);
34 pthread_mutexattr_destroy(&mutex_attribute);
tommied281e92016-01-21 23:47:25 -080035#endif
Tommi494f2092015-04-27 17:39:23 +020036 CS_DEBUG_CODE(thread_ = 0);
37 CS_DEBUG_CODE(recursion_count_ = 0);
38#endif
39}
40
41CriticalSection::~CriticalSection() {
42#if defined(WEBRTC_WIN)
43 DeleteCriticalSection(&crit_);
44#else
tommied281e92016-01-21 23:47:25 -080045#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
46 dispatch_release(semaphore_);
47#else
Tommi494f2092015-04-27 17:39:23 +020048 pthread_mutex_destroy(&mutex_);
49#endif
tommied281e92016-01-21 23:47:25 -080050#endif
Tommi494f2092015-04-27 17:39:23 +020051}
52
Peter Boströmaf9e6632016-01-21 16:56:52 +010053void CriticalSection::Enter() const EXCLUSIVE_LOCK_FUNCTION() {
Tommi494f2092015-04-27 17:39:23 +020054#if defined(WEBRTC_WIN)
55 EnterCriticalSection(&crit_);
56#else
tommied281e92016-01-21 23:47:25 -080057#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
58 int spin = 3000;
tommi7406b962016-01-22 05:13:33 -080059 PlatformThreadRef self = CurrentThreadRef();
tommied281e92016-01-21 23:47:25 -080060 bool have_lock = false;
61 do {
62 // Instead of calling TryEnter() in this loop, we do two interlocked
63 // operations, first a read-only one in order to avoid affecting the lock
64 // cache-line while spinning, in case another thread is using the lock.
tommi7406b962016-01-22 05:13:33 -080065 if (!IsThreadRefEqual(owning_thread_, self)) {
tommied281e92016-01-21 23:47:25 -080066 if (AtomicOps::AcquireLoad(&lock_queue_) == 0) {
67 if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) == 0) {
68 have_lock = true;
69 break;
70 }
71 }
72 } else {
73 AtomicOps::Increment(&lock_queue_);
74 have_lock = true;
75 break;
76 }
77
78 sched_yield();
79 } while (--spin);
80
81 if (!have_lock && AtomicOps::Increment(&lock_queue_) > 1) {
82 // Owning thread cannot be the current thread since TryEnter() would
83 // have succeeded.
tommi7406b962016-01-22 05:13:33 -080084 RTC_DCHECK(!IsThreadRefEqual(owning_thread_, self));
tommied281e92016-01-21 23:47:25 -080085 // Wait for the lock to become available.
86 dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
87 RTC_DCHECK(owning_thread_ == 0);
88 RTC_DCHECK(!recursion_);
89 }
90
91 owning_thread_ = self;
92 ++recursion_;
93
94#else
Tommi494f2092015-04-27 17:39:23 +020095 pthread_mutex_lock(&mutex_);
tommied281e92016-01-21 23:47:25 -080096#endif
97
Tommi494f2092015-04-27 17:39:23 +020098#if CS_DEBUG_CHECKS
99 if (!recursion_count_) {
henrikg91d6ede2015-09-17 00:24:34 -0700100 RTC_DCHECK(!thread_);
tommi7406b962016-01-22 05:13:33 -0800101 thread_ = CurrentThreadRef();
Tommi494f2092015-04-27 17:39:23 +0200102 } else {
henrikg91d6ede2015-09-17 00:24:34 -0700103 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200104 }
105 ++recursion_count_;
106#endif
107#endif
108}
109
Peter Boströmaf9e6632016-01-21 16:56:52 +0100110bool CriticalSection::TryEnter() const EXCLUSIVE_TRYLOCK_FUNCTION(true) {
Tommi494f2092015-04-27 17:39:23 +0200111#if defined(WEBRTC_WIN)
112 return TryEnterCriticalSection(&crit_) != FALSE;
113#else
tommied281e92016-01-21 23:47:25 -0800114#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommi7406b962016-01-22 05:13:33 -0800115 if (!IsThreadRefEqual(owning_thread_, CurrentThreadRef())) {
tommied281e92016-01-21 23:47:25 -0800116 if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) != 0)
117 return false;
tommi7406b962016-01-22 05:13:33 -0800118 owning_thread_ = CurrentThreadRef();
tommied281e92016-01-21 23:47:25 -0800119 RTC_DCHECK(!recursion_);
120 } else {
121 AtomicOps::Increment(&lock_queue_);
122 }
123 ++recursion_;
124#else
Tommi494f2092015-04-27 17:39:23 +0200125 if (pthread_mutex_trylock(&mutex_) != 0)
126 return false;
tommied281e92016-01-21 23:47:25 -0800127#endif
Tommi494f2092015-04-27 17:39:23 +0200128#if CS_DEBUG_CHECKS
129 if (!recursion_count_) {
henrikg91d6ede2015-09-17 00:24:34 -0700130 RTC_DCHECK(!thread_);
tommi7406b962016-01-22 05:13:33 -0800131 thread_ = CurrentThreadRef();
Tommi494f2092015-04-27 17:39:23 +0200132 } else {
henrikg91d6ede2015-09-17 00:24:34 -0700133 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200134 }
135 ++recursion_count_;
136#endif
137 return true;
138#endif
139}
Peter Boströmaf9e6632016-01-21 16:56:52 +0100140void CriticalSection::Leave() const UNLOCK_FUNCTION() {
henrikg91d6ede2015-09-17 00:24:34 -0700141 RTC_DCHECK(CurrentThreadIsOwner());
Tommi494f2092015-04-27 17:39:23 +0200142#if defined(WEBRTC_WIN)
143 LeaveCriticalSection(&crit_);
144#else
145#if CS_DEBUG_CHECKS
146 --recursion_count_;
henrikg91d6ede2015-09-17 00:24:34 -0700147 RTC_DCHECK(recursion_count_ >= 0);
Tommi494f2092015-04-27 17:39:23 +0200148 if (!recursion_count_)
149 thread_ = 0;
150#endif
tommied281e92016-01-21 23:47:25 -0800151#if defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
tommi7406b962016-01-22 05:13:33 -0800152 RTC_DCHECK(IsThreadRefEqual(owning_thread_, CurrentThreadRef()));
tommied281e92016-01-21 23:47:25 -0800153 RTC_DCHECK_GE(recursion_, 0);
154 --recursion_;
155 if (!recursion_)
156 owning_thread_ = 0;
157
158 if (AtomicOps::Decrement(&lock_queue_) > 0 && !recursion_)
159 dispatch_semaphore_signal(semaphore_);
160#else
Tommi494f2092015-04-27 17:39:23 +0200161 pthread_mutex_unlock(&mutex_);
162#endif
tommied281e92016-01-21 23:47:25 -0800163#endif
Tommi494f2092015-04-27 17:39:23 +0200164}
165
166bool CriticalSection::CurrentThreadIsOwner() const {
167#if defined(WEBRTC_WIN)
brucedawsona10492f2015-10-06 13:34:30 -0700168 // OwningThread has type HANDLE but actually contains the Thread ID:
169 // http://stackoverflow.com/questions/12675301/why-is-the-owningthread-member-of-critical-section-of-type-handle-when-it-is-de
170 // Converting through size_t avoids the VS 2015 warning C4312: conversion from
171 // 'type1' to 'type2' of greater size
172 return crit_.OwningThread ==
173 reinterpret_cast<HANDLE>(static_cast<size_t>(GetCurrentThreadId()));
Tommi494f2092015-04-27 17:39:23 +0200174#else
175#if CS_DEBUG_CHECKS
tommi7406b962016-01-22 05:13:33 -0800176 return IsThreadRefEqual(thread_, CurrentThreadRef());
Tommi494f2092015-04-27 17:39:23 +0200177#else
178 return true;
179#endif // CS_DEBUG_CHECKS
180#endif
181}
182
183bool CriticalSection::IsLocked() const {
184#if defined(WEBRTC_WIN)
185 return crit_.LockCount != -1;
186#else
187#if CS_DEBUG_CHECKS
188 return thread_ != 0;
189#else
190 return true;
191#endif
192#endif
193}
194
Peter Boströmaf9e6632016-01-21 16:56:52 +0100195CritScope::CritScope(const CriticalSection* cs) : cs_(cs) { cs_->Enter(); }
Tommi494f2092015-04-27 17:39:23 +0200196CritScope::~CritScope() { cs_->Leave(); }
197
Peter Boströmaf9e6632016-01-21 16:56:52 +0100198TryCritScope::TryCritScope(const CriticalSection* cs)
Tommi494f2092015-04-27 17:39:23 +0200199 : cs_(cs), locked_(cs->TryEnter()) {
200 CS_DEBUG_CODE(lock_was_called_ = false);
201}
202
203TryCritScope::~TryCritScope() {
henrikg91d6ede2015-09-17 00:24:34 -0700204 CS_DEBUG_CODE(RTC_DCHECK(lock_was_called_));
Tommi494f2092015-04-27 17:39:23 +0200205 if (locked_)
206 cs_->Leave();
207}
208
209bool TryCritScope::locked() const {
210 CS_DEBUG_CODE(lock_was_called_ = true);
211 return locked_;
212}
213
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700214void GlobalLockPod::Lock() {
tommied281e92016-01-21 23:47:25 -0800215#if !defined(WEBRTC_WIN) && (!defined(WEBRTC_MAC) || USE_NATIVE_MUTEX_ON_MAC)
Tommi494f2092015-04-27 17:39:23 +0200216 const struct timespec ts_null = {0};
217#endif
218
pbos46ad5422015-12-07 14:29:14 -0800219 while (AtomicOps::CompareAndSwap(&lock_acquired, 0, 1)) {
Tommi494f2092015-04-27 17:39:23 +0200220#if defined(WEBRTC_WIN)
221 ::Sleep(0);
tommied281e92016-01-21 23:47:25 -0800222#elif defined(WEBRTC_MAC) && !USE_NATIVE_MUTEX_ON_MAC
223 sched_yield();
Tommi494f2092015-04-27 17:39:23 +0200224#else
225 nanosleep(&ts_null, nullptr);
226#endif
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700227 }
228}
229
230void GlobalLockPod::Unlock() {
pbos46ad5422015-12-07 14:29:14 -0800231 int old_value = AtomicOps::CompareAndSwap(&lock_acquired, 1, 0);
henrikg91d6ede2015-09-17 00:24:34 -0700232 RTC_DCHECK_EQ(1, old_value) << "Unlock called without calling Lock first";
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700233}
234
pbos46ad5422015-12-07 14:29:14 -0800235GlobalLock::GlobalLock() {
236 lock_acquired = 0;
237}
pbos3c12f4d2015-11-17 03:21:02 -0800238
pbos46ad5422015-12-07 14:29:14 -0800239GlobalLockScope::GlobalLockScope(GlobalLockPod* lock)
240 : lock_(lock) {
Joachim Bauchfec2c6d2015-05-27 23:41:43 +0200241 lock_->Lock();
242}
243
244GlobalLockScope::~GlobalLockScope() {
245 lock_->Unlock();
246}
247
Jiayang Liubef8d2d2015-03-26 14:38:46 -0700248} // namespace rtc