xref: /aosp_15_r20/external/webrtc/rtc_base/deprecated/recursive_critical_section.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
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 "rtc_base/deprecated/recursive_critical_section.h"
12 
13 #include <time.h>
14 
15 #include "rtc_base/checks.h"
16 #include "rtc_base/platform_thread_types.h"
17 #include "rtc_base/synchronization/yield.h"
18 #include "rtc_base/system/unused.h"
19 
20 #if RTC_DCHECK_IS_ON
21 #define RTC_CS_DEBUG_CODE(x) x
22 #else  // !RTC_DCHECK_IS_ON
23 #define RTC_CS_DEBUG_CODE(x)
24 #endif  // !RTC_DCHECK_IS_ON
25 
26 namespace rtc {
27 
RecursiveCriticalSection()28 RecursiveCriticalSection::RecursiveCriticalSection() {
29 #if defined(WEBRTC_WIN)
30   InitializeCriticalSection(&crit_);
31 #elif defined(WEBRTC_POSIX)
32 #if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
33   lock_queue_ = 0;
34   owning_thread_ = 0;
35   recursion_ = 0;
36   semaphore_ = dispatch_semaphore_create(0);
37 #else
38   pthread_mutexattr_t mutex_attribute;
39   pthread_mutexattr_init(&mutex_attribute);
40   pthread_mutexattr_settype(&mutex_attribute, PTHREAD_MUTEX_RECURSIVE);
41 #if defined(WEBRTC_MAC)
42   pthread_mutexattr_setpolicy_np(&mutex_attribute,
43                                  _PTHREAD_MUTEX_POLICY_FIRSTFIT);
44 #endif
45   pthread_mutex_init(&mutex_, &mutex_attribute);
46   pthread_mutexattr_destroy(&mutex_attribute);
47 #endif
48   RTC_CS_DEBUG_CODE(thread_ = 0);
49   RTC_CS_DEBUG_CODE(recursion_count_ = 0);
50   RTC_UNUSED(thread_);
51   RTC_UNUSED(recursion_count_);
52 #else
53 #error Unsupported platform.
54 #endif
55 }
56 
~RecursiveCriticalSection()57 RecursiveCriticalSection::~RecursiveCriticalSection() {
58 #if defined(WEBRTC_WIN)
59   DeleteCriticalSection(&crit_);
60 #elif defined(WEBRTC_POSIX)
61 #if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
62   dispatch_release(semaphore_);
63 #else
64   pthread_mutex_destroy(&mutex_);
65 #endif
66 #else
67 #error Unsupported platform.
68 #endif
69 }
70 
Enter() const71 void RecursiveCriticalSection::Enter() const RTC_EXCLUSIVE_LOCK_FUNCTION() {
72 #if defined(WEBRTC_WIN)
73   EnterCriticalSection(&crit_);
74 #elif defined(WEBRTC_POSIX)
75 #if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
76   int spin = 3000;
77   PlatformThreadRef self = CurrentThreadRef();
78   bool have_lock = false;
79   do {
80     // Instead of calling TryEnter() in this loop, we do two interlocked
81     // operations, first a read-only one in order to avoid affecting the lock
82     // cache-line while spinning, in case another thread is using the lock.
83     if (!IsThreadRefEqual(owning_thread_, self)) {
84       if (AtomicOps::AcquireLoad(&lock_queue_) == 0) {
85         if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) == 0) {
86           have_lock = true;
87           break;
88         }
89       }
90     } else {
91       AtomicOps::Increment(&lock_queue_);
92       have_lock = true;
93       break;
94     }
95 
96     sched_yield();
97   } while (--spin);
98 
99   if (!have_lock && AtomicOps::Increment(&lock_queue_) > 1) {
100     // Owning thread cannot be the current thread since TryEnter() would
101     // have succeeded.
102     RTC_DCHECK(!IsThreadRefEqual(owning_thread_, self));
103     // Wait for the lock to become available.
104     dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER);
105     RTC_DCHECK(owning_thread_ == 0);
106     RTC_DCHECK(!recursion_);
107   }
108 
109   owning_thread_ = self;
110   ++recursion_;
111 
112 #else
113   pthread_mutex_lock(&mutex_);
114 #endif
115 
116 #if RTC_DCHECK_IS_ON
117   if (!recursion_count_) {
118     RTC_DCHECK(!thread_);
119     thread_ = CurrentThreadRef();
120   } else {
121     RTC_DCHECK(CurrentThreadIsOwner());
122   }
123   ++recursion_count_;
124 #endif
125 #else
126 #error Unsupported platform.
127 #endif
128 }
129 
TryEnter() const130 bool RecursiveCriticalSection::TryEnter() const
131     RTC_EXCLUSIVE_TRYLOCK_FUNCTION(true) {
132 #if defined(WEBRTC_WIN)
133   return TryEnterCriticalSection(&crit_) != FALSE;
134 #elif defined(WEBRTC_POSIX)
135 #if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
136   if (!IsThreadRefEqual(owning_thread_, CurrentThreadRef())) {
137     if (AtomicOps::CompareAndSwap(&lock_queue_, 0, 1) != 0)
138       return false;
139     owning_thread_ = CurrentThreadRef();
140     RTC_DCHECK(!recursion_);
141   } else {
142     AtomicOps::Increment(&lock_queue_);
143   }
144   ++recursion_;
145 #else
146   if (pthread_mutex_trylock(&mutex_) != 0)
147     return false;
148 #endif
149 #if RTC_DCHECK_IS_ON
150   if (!recursion_count_) {
151     RTC_DCHECK(!thread_);
152     thread_ = CurrentThreadRef();
153   } else {
154     RTC_DCHECK(CurrentThreadIsOwner());
155   }
156   ++recursion_count_;
157 #endif
158   return true;
159 #else
160 #error Unsupported platform.
161 #endif
162 }
163 
Leave() const164 void RecursiveCriticalSection::Leave() const RTC_UNLOCK_FUNCTION() {
165   RTC_DCHECK(CurrentThreadIsOwner());
166 #if defined(WEBRTC_WIN)
167   LeaveCriticalSection(&crit_);
168 #elif defined(WEBRTC_POSIX)
169 #if RTC_DCHECK_IS_ON
170   --recursion_count_;
171   RTC_DCHECK(recursion_count_ >= 0);
172   if (!recursion_count_)
173     thread_ = 0;
174 #endif
175 #if defined(WEBRTC_MAC) && !RTC_USE_NATIVE_MUTEX_ON_MAC
176   RTC_DCHECK(IsThreadRefEqual(owning_thread_, CurrentThreadRef()));
177   RTC_DCHECK_GE(recursion_, 0);
178   --recursion_;
179   if (!recursion_)
180     owning_thread_ = 0;
181 
182   if (AtomicOps::Decrement(&lock_queue_) > 0 && !recursion_)
183     dispatch_semaphore_signal(semaphore_);
184 #else
185   pthread_mutex_unlock(&mutex_);
186 #endif
187 #else
188 #error Unsupported platform.
189 #endif
190 }
191 
CurrentThreadIsOwner() const192 bool RecursiveCriticalSection::CurrentThreadIsOwner() const {
193 #if defined(WEBRTC_WIN)
194   // OwningThread has type HANDLE but actually contains the Thread ID:
195   // http://stackoverflow.com/questions/12675301/why-is-the-owningthread-member-of-critical-section-of-type-handle-when-it-is-de
196   // Converting through size_t avoids the VS 2015 warning C4312: conversion from
197   // 'type1' to 'type2' of greater size
198   return crit_.OwningThread ==
199          reinterpret_cast<HANDLE>(static_cast<size_t>(GetCurrentThreadId()));
200 #elif defined(WEBRTC_POSIX)
201 #if RTC_DCHECK_IS_ON
202   return IsThreadRefEqual(thread_, CurrentThreadRef());
203 #else
204   return true;
205 #endif  // RTC_DCHECK_IS_ON
206 #else
207 #error Unsupported platform.
208 #endif
209 }
210 
CritScope(const RecursiveCriticalSection * cs)211 CritScope::CritScope(const RecursiveCriticalSection* cs) : cs_(cs) {
212   cs_->Enter();
213 }
~CritScope()214 CritScope::~CritScope() {
215   cs_->Leave();
216 }
217 
218 }  // namespace rtc
219