1 /*---------------------------------------------------------------------------* 2 Project: Horizon 3 File: os_CriticalSection.h 4 Copyright (C)2009 Nintendo Co., Ltd. All rights reserved. 5 These coded instructions, statements, and computer programs contain 6 proprietary information of Nintendo of America Inc. and/or Nintendo 7 Company Ltd., and are protected by Federal copyright law. They may 8 not be disclosed to third parties or copied or duplicated in any form, 9 in whole or in part, without the prior written consent of Nintendo. 10 $Rev: 34502 $ 11 *--------------------------------------------------------------------------- 12 13 14 */ 15 16 /* Please see man pages for details 17 18 19 20 */ 21 22 #ifndef NN_OS_OS_CRITICALSECTION_H_ 23 #define NN_OS_OS_CRITICALSECTION_H_ 24 25 #include <nn/os/os_WaitableCounter.h> 26 #include <nn/assert.h> 27 #include <nn/WithInitialize.h> 28 #include <nn/util/detail/util_ScopedLockImpl.h> 29 #include <nn/hw/ARM/reg_access.h> 30 31 #ifdef __cplusplus 32 33 namespace nn { namespace os { 34 35 /* Please see man pages for details 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 */ 54 55 class CriticalSection : private nn::util::NonCopyable<CriticalSection> 56 { 57 private: 58 struct ReverseIfPositiveUpdater 59 { operatorReverseIfPositiveUpdater60 bool operator()(s32& x) 61 { 62 if( x > 0 ) 63 { 64 x = -x; 65 return true; 66 } 67 else 68 { 69 return false; 70 } 71 } 72 }; 73 74 struct ReverseUpdater 75 { 76 s32 afterUpdate; operatorReverseUpdater77 bool operator()(s32& x) 78 { 79 x = -x; 80 afterUpdate = x; 81 return true; 82 } 83 }; 84 85 public: 86 87 static const bool CAN_LOCK_RECURSIVELY = true; 88 89 /* Please see man pages for details 90 91 92 93 94 95 */ CriticalSection()96 CriticalSection() : m_ThreadUniqueValue(GetInvalidThreadUniqueValue()), m_LockCount(-1) {} 97 98 /* Please see man pages for details 99 100 */ CriticalSection(const nn::WithInitialize &)101 CriticalSection(const nn::WithInitialize&) { Initialize(); } 102 103 /* Please see man pages for details 104 105 106 107 */ Initialize()108 void Initialize() 109 { 110 *m_Counter = 1; 111 m_ThreadUniqueValue = GetInvalidThreadUniqueValue(); 112 m_LockCount = 0; 113 } 114 115 /* Please see man pages for details 116 117 118 119 */ TryInitialize()120 nn::Result TryInitialize() 121 { 122 Initialize(); 123 return ResultSuccess(); 124 } 125 126 /* Please see man pages for details 127 128 129 130 131 132 */ Finalize()133 void Finalize() { m_LockCount = -1; } 134 135 /* Please see man pages for details 136 137 */ ~CriticalSection()138 ~CriticalSection() {} 139 140 /* Please see man pages for details 141 142 143 144 */ Enter()145 void Enter() 146 { 147 NN_TASSERT_(IsInitialized()); 148 // Attempt entry to critical section if this thread has not entered critical section. 149 // 150 if (!LockedByCurrentThread() && !TryEnterImpl()) 151 { 152 EnterImpl(); 153 } 154 ++this->m_LockCount; 155 } 156 157 /* Please see man pages for details 158 159 160 161 */ TryEnter()162 bool TryEnter() 163 { 164 NN_TASSERT_(IsInitialized()); 165 // Attempt entry to critical section if this thread has not entered critical section. 166 // 167 if (LockedByCurrentThread() || TryEnterImpl()) 168 { 169 ++this->m_LockCount; 170 return true; 171 } 172 else 173 { 174 return false; 175 } 176 } 177 178 /* Please see man pages for details 179 180 181 182 */ Leave()183 void Leave() 184 { 185 NN_TASSERT_(IsInitialized()); 186 NN_TASSERTMSG_(LockedByCurrentThread() && m_LockCount > 0, "CriticalSection is not entered on the current thread."); 187 if (--this->m_LockCount == 0) 188 { 189 NN_TASSERTMSG_( *m_Counter < 0 , "CriticalSection is not entered."); 190 191 // Clear the thread ID that currently has the critical section. 192 m_ThreadUniqueValue = GetInvalidThreadUniqueValue(); 193 194 // Unlock by setting the counter's sign to positive. 195 ReverseUpdater updater; 196 m_Counter->AtomicUpdateConditional(updater); 197 198 // Wake the waiting thread that has the highest priority. 199 if( updater.afterUpdate > 1 ) 200 { 201 m_Counter.Signal(1); 202 } 203 } 204 } 205 206 /* Please see man pages for details 207 208 209 210 211 */ 212 class ScopedLock; 213 IsLocked()214 bool IsLocked() const 215 { 216 return (*m_Counter < 0); 217 } 218 219 private: 220 221 void EnterImpl(); 222 TryEnterImpl()223 bool TryEnterImpl() 224 { 225 ReverseIfPositiveUpdater updater; 226 if (m_Counter->AtomicUpdateConditional(updater)) 227 { 228 // If critical section is successfully entered, the counter's sign changes from positive to negative. 229 // 230 231 NN_TASSERT_(m_LockCount == 0); 232 233 // Save the thread ID that currently has the critical section. 234 this->m_ThreadUniqueValue = GetThreadUniqueValue(); 235 return true; 236 } 237 else 238 { 239 return false; 240 } 241 } 242 243 // TODO: We recommend moving this to ARM. 244 #ifdef NN_PROCESSOR_ARM946ES GetThreadUniqueValue()245 static uptr GetThreadUniqueValue() 246 { 247 return nn::os::CTR::ARM946ES::GetThreadId(); 248 } GetInvalidThreadUniqueValue()249 static uptr GetInvalidThreadUniqueValue() 250 { 251 return static_cast<uptr>(-1); 252 } 253 #else GetThreadUniqueValue()254 static uptr GetThreadUniqueValue() 255 { 256 uptr v; 257 HW_GET_CP15_THREAD_ID_USER_READ_ONLY(v); 258 return v; 259 } GetInvalidThreadUniqueValue()260 static uptr GetInvalidThreadUniqueValue() 261 { 262 return 0; 263 } 264 #endif 265 LockedByCurrentThread()266 bool LockedByCurrentThread() const 267 { 268 return GetThreadUniqueValue() == m_ThreadUniqueValue; 269 } 270 271 private: 272 nn::os::WaitableCounter m_Counter; 273 uptr m_ThreadUniqueValue; 274 s32 m_LockCount; IsInitialized()275 bool IsInitialized() const { return m_LockCount >= 0; } 276 }; 277 278 NN_UTIL_DETAIL_DEFINE_SCOPED_LOCK(CriticalSection, Enter(), Leave()); 279 280 }} // namespace nn::os 281 282 #endif // __cplusplus 283 284 // C declarations follow 285 286 #include <nn/util/detail/util_CLibImpl.h> 287 288 #define NN_OS_CRITICALSECTION_SIZE 12 289 290 /* Please see man pages for details 291 292 293 294 295 296 297 298 299 */ 300 301 /* Please see man pages for details 302 303 304 305 306 */ 307 NN_UTIL_DETAIL_CLIBIMPL_DEFINE_BUFFER_CLASS(nnosCriticalSection, nn::os::CriticalSection, NN_OS_CRITICALSECTION_SIZE, u32); 308 309 /* Please see man pages for details 310 311 */ 312 NN_EXTERN_C void nnosCriticalSectionInitialize(nnosCriticalSection* this_); 313 314 /* Please see man pages for details 315 316 */ 317 NN_EXTERN_C bool nnosCriticalSectionTryInitialize(nnosCriticalSection* this_); 318 319 /* Please see man pages for details 320 321 */ 322 NN_EXTERN_C void nnosCriticalSectionEnter(nnosCriticalSection* this_); 323 324 /* Please see man pages for details 325 326 */ 327 NN_EXTERN_C bool nnosCriticalSectionTryEnter(nnosCriticalSection* this_); 328 329 /* Please see man pages for details 330 331 */ 332 NN_EXTERN_C void nnosCriticalSectionLeave(nnosCriticalSection* this_); 333 334 /* Please see man pages for details 335 336 */ 337 NN_EXTERN_C void nnosCriticalSectionFinalize(nnosCriticalSection* this_); 338 339 /* 340 341 342 343 */ 344 345 #endif 346