1 /*---------------------------------------------------------------------------* 2 Project: Horizon 3 File: os_InterCoreCriticalSection.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: 31762 $ 11 *--------------------------------------------------------------------------- 12 13 14 */ 15 16 /* Please see man pages for details 17 18 19 20 21 */ 22 23 #ifndef NN_OS_OS_INTERCORE_CRITICALSECTION_H_ 24 #define NN_OS_OS_INTERCORE_CRITICALSECTION_H_ 25 26 #include <nn/os/os_MemoryBarrierSelect.h> 27 #include <nn/os/os_WaitableCounter.h> 28 #include <nn/assert.h> 29 #include <nn/WithInitialize.h> 30 #include <nn/util/detail/util_ScopedLockImpl.h> 31 #include <nn/hw/ARM/reg_access.h> 32 33 #ifdef __cplusplus 34 35 namespace nn { namespace os { 36 37 /* Please see man pages for details 38 39 40 41 */ 42 43 class InterCoreCriticalSection : private nn::util::NonCopyable<InterCoreCriticalSection> 44 { 45 private: 46 struct ReverseIfPositiveUpdater 47 { operatorReverseIfPositiveUpdater48 bool operator()(s32& x) 49 { 50 if( x > 0 ) 51 { 52 x = -x; 53 return true; 54 } 55 else 56 { 57 return false; 58 } 59 } 60 }; 61 62 struct ReverseUpdater 63 { 64 s32 afterUpdate; operatorReverseUpdater65 bool operator()(s32& x) 66 { 67 x = -x; 68 afterUpdate = x; 69 return true; 70 } 71 }; 72 73 public: 74 75 static const bool CAN_LOCK_RECURSIVELY = true; 76 77 /* Please see man pages for details 78 79 80 81 82 83 */ InterCoreCriticalSection()84 InterCoreCriticalSection() : m_ThreadUniqueValue(GetInvalidThreadUniqueValue()), m_LockCount(-1) {} 85 86 /* Please see man pages for details 87 88 */ InterCoreCriticalSection(const nn::WithInitialize &)89 InterCoreCriticalSection(const nn::WithInitialize&) { Initialize(); } 90 91 /* Please see man pages for details 92 93 94 95 */ Initialize()96 void Initialize() 97 { 98 *m_Counter = 1; 99 m_ThreadUniqueValue = GetInvalidThreadUniqueValue(); 100 m_LockCount = 0; 101 } 102 103 /* Please see man pages for details 104 105 106 107 */ TryInitialize()108 nn::Result TryInitialize() 109 { 110 Initialize(); 111 return ResultSuccess(); 112 } 113 114 /* Please see man pages for details 115 116 117 118 119 120 */ Finalize()121 void Finalize() { m_LockCount = -1; } 122 123 /* Please see man pages for details 124 125 */ ~InterCoreCriticalSection()126 ~InterCoreCriticalSection() {} 127 128 /* Please see man pages for details 129 130 131 132 */ Enter()133 void Enter() 134 { 135 NN_TASSERT_(IsInitialized()); 136 // Attempt entry to critical section if this thread has not entered critical section. 137 // 138 if (!LockedByCurrentThread() && !TryEnterImpl()) 139 { 140 EnterImpl(); 141 } 142 ++this->m_LockCount; 143 } 144 145 /* Please see man pages for details 146 147 148 149 */ TryEnter()150 bool TryEnter() 151 { 152 NN_TASSERT_(IsInitialized()); 153 // Attempt entry to critical section if this thread has not entered critical section. 154 // 155 if (LockedByCurrentThread() || TryEnterImpl()) 156 { 157 ++this->m_LockCount; 158 return true; 159 } 160 else 161 { 162 return false; 163 } 164 } 165 166 /* Please see man pages for details 167 168 169 170 */ Leave()171 void Leave() 172 { 173 NN_TASSERT_(IsInitialized()); 174 NN_TASSERTMSG_(LockedByCurrentThread() && m_LockCount > 0, "InterCoreCriticalSection is not entered on the current thread."); 175 if (--this->m_LockCount == 0) 176 { 177 NN_TASSERTMSG_( *m_Counter < 0 , "InterCoreCriticalSection is not entered."); 178 179 // Clear the thread ID that currently has the critical section. 180 m_ThreadUniqueValue = GetInvalidThreadUniqueValue(); 181 182 // Unlock by setting the counter's sign to positive. 183 ReverseUpdater updater; 184 m_Counter->AtomicUpdateConditional(updater); 185 186 DataSynchronizationBarrier(); 187 188 // Wake the waiting thread that has the highest priority. 189 if( updater.afterUpdate > 1 ) 190 { 191 m_Counter.Signal(1); 192 } 193 } 194 } 195 196 /* Please see man pages for details 197 198 199 200 */ 201 class ScopedLock; 202 IsLocked()203 bool IsLocked() const 204 { 205 return (*m_Counter < 0); 206 } 207 208 private: 209 210 void EnterImpl(); 211 TryEnterImpl()212 bool TryEnterImpl() 213 { 214 ReverseIfPositiveUpdater updater; 215 bool ret = m_Counter->AtomicUpdateConditional(updater); 216 DataSynchronizationBarrier(); 217 if (ret) 218 { 219 // If critical section is successfully entered, the counter's sign changes from positive to negative. 220 // 221 222 NN_TASSERT_(m_LockCount == 0); 223 224 // Save the thread ID that currently has the critical section. 225 this->m_ThreadUniqueValue = GetThreadUniqueValue(); 226 return true; 227 } 228 else 229 { 230 return false; 231 } 232 } 233 234 // TODO: We recommend moving this to ARM. 235 #ifdef NN_PROCESSOR_ARM946ES GetThreadUniqueValue()236 static uptr GetThreadUniqueValue() 237 { 238 return nn::os::CTR::ARM946ES::GetThreadId(); 239 } GetInvalidThreadUniqueValue()240 static uptr GetInvalidThreadUniqueValue() 241 { 242 return static_cast<uptr>(-1); 243 } 244 #else GetThreadUniqueValue()245 static uptr GetThreadUniqueValue() 246 { 247 uptr v; 248 HW_GET_CP15_THREAD_ID_USER_READ_ONLY(v); 249 return v; 250 } GetInvalidThreadUniqueValue()251 static uptr GetInvalidThreadUniqueValue() 252 { 253 return 0; 254 } 255 #endif 256 LockedByCurrentThread()257 bool LockedByCurrentThread() const 258 { 259 return GetThreadUniqueValue() == m_ThreadUniqueValue; 260 } 261 262 private: 263 nn::os::WaitableCounter m_Counter; 264 uptr m_ThreadUniqueValue; 265 s32 m_LockCount; IsInitialized()266 bool IsInitialized() const { return m_LockCount >= 0; } 267 }; 268 269 NN_UTIL_DETAIL_DEFINE_SCOPED_LOCK(InterCoreCriticalSection, Enter(), Leave()); 270 271 }} // namespace nn::os 272 273 #endif // __cplusplus 274 275 // C declarations follow 276 277 #include <nn/util/detail/util_CLibImpl.h> 278 279 #define NN_OS_INTERCORE_CRITICALSECTION_SIZE 12 280 281 NN_UTIL_DETAIL_CLIBIMPL_DEFINE_BUFFER_CLASS(nnosInterCoreCriticalSection, nn::os::InterCoreCriticalSection, NN_OS_INTERCORE_CRITICALSECTION_SIZE, u32); 282 283 NN_EXTERN_C void nnosInterCoreCriticalSectionInitialize(nnosInterCoreCriticalSection* this_); 284 285 NN_EXTERN_C bool nnosInterCoreCriticalSectionTryInitialize(nnosInterCoreCriticalSection* this_); 286 287 NN_EXTERN_C void nnosInterCoreCriticalSectionEnter(nnosInterCoreCriticalSection* this_); 288 289 NN_EXTERN_C bool nnosInterCoreCriticalSectionTryEnter(nnosInterCoreCriticalSection* this_); 290 291 NN_EXTERN_C void nnosInterCoreCriticalSectionLeave(nnosInterCoreCriticalSection* this_); 292 293 NN_EXTERN_C void nnosInterCoreCriticalSectionFinalize(nnosInterCoreCriticalSection* this_); 294 295 #endif 296