1 /*---------------------------------------------------------------------------* 2 Project: Horizon 3 File: os_CriticalSection.h 4 5 Copyright (C)2009 Nintendo Co., Ltd. All rights reserved. 6 7 These coded instructions, statements, and computer programs contain 8 proprietary information of Nintendo of America Inc. and/or Nintendo 9 Company Ltd., and are protected by Federal copyright law. They may 10 not be disclosed to third parties or copied or duplicated in any form, 11 in whole or in part, without the prior written consent of Nintendo. 12 13 $Rev: 35573 $ 14 *---------------------------------------------------------------------------*/ 15 16 /*! @file 17 @brief CriticalSection に関する API の宣言 18 19 :include nn/os.h 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 /*! 36 @brief クリティカルセクションを扱う為のクラスです。クリティカルセクションは排他制御を行うための同期オブジェクトです。 37 38 プログラムの特定の個所について複数のスレッドからの同時実行を抑制し、 39 リソースが同時に複数のスレッドからアクセスされないようにします。 40 41 ミューテックス(@ref nn::os::Mutex)と同じような役割を持ちますが、以下のような制限があります。 42 43 @li 一つのアプリケーション内で排他制御を行う場合にのみ使用できます。 44 @li 優先度継承は行いません。優先度逆転などの問題が生じる可能性があります。 45 @li ミューテックスよりクリティカルセクションの方がオブジェクトを保持するためのメモリ使用量は多くなります。 46 47 スレッド間でロックが衝突しない場合のパフォーマンスでは 48 @ref nn::os::CriticalSection の方が @ref nn::os::Mutex より 49 圧倒的に高速なため、 50 通常は @ref nn::os::Mutex ではなく、@ref nn::os::CriticalSection を使用すべきです。 51 52 @ref nn::os::CriticalSection::ScopedLock を用いるとオブジェクトの生成からスコープから出るまでの間、クリティカルセクションに入ることができます。 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 /*! 90 @brief オブジェクトを構築します。 91 92 初期化しないコンストラクタと、初期化するコンストラクタが用意されています。 93 94 初期化しない場合、使用する前に、@ref nn::os::CriticalSection::Initialize を呼んで明示的に初期化する必要があります。 95 */ CriticalSection()96 CriticalSection() : m_ThreadUniqueValue(GetInvalidThreadUniqueValue()), m_LockCount(-1) {} 97 98 /*! 99 @brief オブジェクトを構築し、初期化を行います。 100 */ CriticalSection(const nn::WithInitialize &)101 CriticalSection(const nn::WithInitialize&) { Initialize(); } 102 103 /*! 104 @brief オブジェクトを初期化します。 105 106 @return 無し。 107 */ Initialize()108 void Initialize() 109 { 110 *m_Counter = 1; 111 m_ThreadUniqueValue = GetInvalidThreadUniqueValue(); 112 m_LockCount = 0; 113 } 114 115 /*! 116 @brief オブジェクトの初期化を試みます。 117 118 @return 処理結果を返します。 119 */ TryInitialize()120 nn::Result TryInitialize() 121 { 122 Initialize(); 123 return ResultSuccess(); 124 } 125 126 /*! 127 @brief クリティカルセクションを破棄します。 128 129 デストラクタから自動的に呼び出されますが、明示的に呼ぶこともできます。 130 131 @return 無し。 132 */ Finalize()133 void Finalize() { m_LockCount = -1; } 134 135 /*! 136 @brief デストラクタです。 137 */ ~CriticalSection()138 ~CriticalSection() {} 139 140 /*! 141 @brief ロックして他のスレッドがクリティカルセクションに進入するのを防ぎます。ブロックします。 142 143 @return 無し。 144 */ Enter()145 void Enter() 146 { 147 NN_TASSERT_(IsInitialized()); 148 // 自スレッドがクリティカルセクションに進入していなければ 149 // クリティカルセクションに進入を試みます。 150 if (!LockedByCurrentThread() && !TryEnterImpl()) 151 { 152 EnterImpl(); 153 } 154 ++this->m_LockCount; 155 } 156 157 /*! 158 @brief ロックして他のスレッドがクリティカルセクションに進入するのを防ぎます。ブロックしません。 159 160 @return ロックに成功したかを返します。 161 */ TryEnter()162 bool TryEnter() 163 { 164 NN_TASSERT_(IsInitialized()); 165 // 自スレッドがクリティカルセクションに進入していなければ 166 // クリティカルセクションに進入を試みます。 167 if (LockedByCurrentThread() || TryEnterImpl()) 168 { 169 ++this->m_LockCount; 170 return true; 171 } 172 else 173 { 174 return false; 175 } 176 } 177 178 /*! 179 @brief アンロックして他のスレッドがクリティカルセクションに進入できるようにします。 180 181 @return 無し。 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 // クリティカルセクションを取得中のスレッド ID をクリアします。 192 m_ThreadUniqueValue = GetInvalidThreadUniqueValue(); 193 194 // カウンタの符号を正に設定することでロックを解除します。 195 ReverseUpdater updater; 196 m_Counter->AtomicUpdateConditional(updater); 197 198 // 待機している最も優先度の高いスレッドを起床します。 199 if( updater.afterUpdate > 1 ) 200 { 201 m_Counter.Signal(1); 202 } 203 } 204 } 205 206 /*! 207 @class nn::os::CriticalSection::ScopedLock 208 209 @brief オブジェクトの生成時からオブジェクトの存在するスコープを抜けるまでの間 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 // クリティカルセクションの進入に成功すれば 229 // カウンタの符号が正から負に変わります。 230 231 NN_TASSERT_(m_LockCount == 0); 232 233 // クリティカルセクションを取得したスレッド ID を保存します。 234 this->m_ThreadUniqueValue = GetThreadUniqueValue(); 235 return true; 236 } 237 else 238 { 239 return false; 240 } 241 } 242 243 // TODO: 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 用宣言 285 286 #include <nn/util/detail/util_CLibImpl.h> 287 288 #define NN_OS_CRITICALSECTION_SIZE 12 289 290 /*! 291 @addtogroup nn_os os 292 @{ 293 294 @defgroup nn_os_CriticalSection_c CriticalSection (C) 295 296 @brief @ref nn::os::CriticalSection の C インタフェースモジュールです。 297 298 @{ 299 */ 300 301 /*! 302 @struct nnosCriticalSection 303 @brief クリティカルセクションを表す C の構造体です。 304 305 @brief 対応するクラス @ref nn::os::CriticalSection を参照してください。 306 */ 307 NN_UTIL_DETAIL_CLIBIMPL_DEFINE_BUFFER_CLASS(nnosCriticalSection, nn::os::CriticalSection, NN_OS_CRITICALSECTION_SIZE, u32); 308 309 /*! 310 @brief 対応する C++ 関数を参照してください。@ref nn::os::CriticalSection::Initialize 311 */ 312 NN_EXTERN_C void nnosCriticalSectionInitialize(nnosCriticalSection* this_); 313 314 /*! 315 @brief 対応する C++ 関数を参照してください。@ref nn::os::CriticalSection::TryInitialize 316 */ 317 NN_EXTERN_C bool nnosCriticalSectionTryInitialize(nnosCriticalSection* this_); 318 319 /*! 320 @brief 対応する C++ 関数を参照してください。@ref nn::os::CriticalSection::Enter 321 */ 322 NN_EXTERN_C void nnosCriticalSectionEnter(nnosCriticalSection* this_); 323 324 /*! 325 @brief 対応する C++ 関数を参照してください。@ref nn::os::CriticalSection::TryEnter 326 */ 327 NN_EXTERN_C bool nnosCriticalSectionTryEnter(nnosCriticalSection* this_); 328 329 /*! 330 @brief 対応する C++ 関数を参照してください。@ref nn::os::CriticalSection::Leave 331 */ 332 NN_EXTERN_C void nnosCriticalSectionLeave(nnosCriticalSection* this_); 333 334 /*! 335 @brief 対応する C++ 関数を参照してください。@ref nn::os::CriticalSection::Finalize 336 */ 337 NN_EXTERN_C void nnosCriticalSectionFinalize(nnosCriticalSection* this_); 338 339 /*! 340 @} 341 342 @} 343 */ 344 345 #endif 346