/*---------------------------------------------------------------------------* Project: Horizon File: os_CriticalSection.h Copyright (C)2009 Nintendo Co., Ltd. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev: 22980 $ *---------------------------------------------------------------------------*/ /*! @file @brief CriticalSection に関する API の宣言 :include nn/os.h */ #ifndef NN_OS_OS_CRITICALSECTION_H_ #define NN_OS_OS_CRITICALSECTION_H_ #include #include #include #include #include #ifdef __cplusplus namespace nn { namespace os { /*! @brief クリティカルセクションを扱う為のクラスです。クリティカルセクションは排他制御を行うための同期オブジェクトです。 プログラムの特定の個所について複数のスレッドからの同時実行を抑制し、 リソースが同時に複数のスレッドからアクセスされないようにします。 ミューテックス(@ref nn::os::Mutex)と同じような役割を持ちますが、以下のような制限があります。 @li 一つのアプリケーション内で排他制御を行う場合にのみ使用できます。 @li 優先度継承は行いません。優先度逆転などの問題が生じる可能性があります。 @li ミューテックスよりクリティカルセクションの方がオブジェクトを保持するためのメモリ使用量は多くなります。 スレッド間でロックが衝突しない場合のパフォーマンスでは @ref nn::os::CriticalSection の方が @ref nn::os::Mutex より 圧倒的に高速なため、 通常は @ref nn::os::Mutex ではなく、@ref nn::os::CriticalSection を使用すべきです。 @ref nn::os::CriticalSection::ScopedLock を用いるとオブジェクトの生成からスコープから出るまでの間、クリティカルセクションに入ることができます。 */ class CriticalSection : private nn::util::NonCopyable { private: struct ReverseIfPositiveUpdater { bool operator()(s32& x) { if( x > 0 ) { x = -x; return true; } else { return false; } } }; struct ReverseUpdater { s32 afterUpdate; bool operator()(s32& x) { x = -x; afterUpdate = x; return true; } }; public: static const bool CAN_LOCK_RECURSIVELY = true; /*! @brief オブジェクトを構築します。 初期化しないコンストラクタと、初期化するコンストラクタが用意されています。 初期化しない場合、使用する前に、@ref nn::os::CriticalSection::Initialize を呼んで明示的に初期化する必要があります。 */ CriticalSection() : m_ThreadUniqueValue(GetInvalidThreadUniqueValue()), m_LockCount(-1) {} /*! @brief オブジェクトを構築し、初期化を行います。 */ CriticalSection(const nn::WithInitialize&) { Initialize(); } /*! @brief オブジェクトを初期化します。 @return 無し。 */ void Initialize() { *m_Counter = 1; m_ThreadUniqueValue = GetInvalidThreadUniqueValue(); m_LockCount = 0; } /*! @brief オブジェクトの初期化を試みます。 @return 処理結果を返します。 */ nn::Result TryInitialize() { Initialize(); return ResultSuccess(); } /*! @brief クリティカルセクションを破棄します。 デストラクタから自動的に呼び出されますが、明示的に呼ぶこともできます。 @return 無し。 */ void Finalize() { m_LockCount = -1; } /*! @brief デストラクタです。 */ ~CriticalSection() {} /*! @brief ロックして他のスレッドがクリティカルセクションに進入するのを防ぎます。ブロックします。 @return 無し。 */ void Enter() { NN_TASSERT_(IsInitialized()); // 自スレッドがクリティカルセクションに進入していなければ // クリティカルセクションに進入を試みます。 if (!LockedByCurrentThread() && !TryEnterImpl()) { EnterImpl(); } ++this->m_LockCount; } /*! @brief ロックして他のスレッドがクリティカルセクションに進入するのを防ぎます。ブロックしません。 @return ロックに成功したかを返します。 */ bool TryEnter() { NN_TASSERT_(IsInitialized()); // 自スレッドがクリティカルセクションに進入していなければ // クリティカルセクションに進入を試みます。 if (LockedByCurrentThread() || TryEnterImpl()) { ++this->m_LockCount; return true; } else { return false; } } /*! @brief アンロックして他のスレッドがクリティカルセクションに進入できるようにします。 @return 無し。 */ void Leave() { NN_TASSERT_(IsInitialized()); NN_TASSERTMSG_(LockedByCurrentThread() && m_LockCount > 0, "CriticalSection is not entered on the current thread."); if (--this->m_LockCount == 0) { NN_TASSERTMSG_( *m_Counter < 0 , "CriticalSection is not entered."); // クリティカルセクションを取得中のスレッド ID をクリアします。 m_ThreadUniqueValue = GetInvalidThreadUniqueValue(); // カウンタの符号を正に設定することでロックを解除します。 ReverseUpdater updater; m_Counter->AtomicUpdateConditional(updater); // 待機している最も優先度の高いスレッドを起床します。 if( updater.afterUpdate > 1 ) { m_Counter.Signal(1); } } } /*! @class nn::os::CriticalSection::ScopedLock @brief オブジェクトの生成時からオブジェクトの存在するスコープを抜けるまで間クリティカルセクションに入ります。 */ class ScopedLock; bool IsLocked() const { return (*m_Counter < 0); } private: void EnterImpl(); bool TryEnterImpl() { ReverseIfPositiveUpdater updater; if (m_Counter->AtomicUpdateConditional(updater)) { // クリティカルセクションの進入に成功すれば // カウンタの符号が正から負に変わります。 NN_TASSERT_(m_LockCount == 0); // クリティカルセクションを取得したスレッド ID を保存します。 this->m_ThreadUniqueValue = GetThreadUniqueValue(); return true; } else { return false; } } // TODO: ARM に移すことを推奨します。 #ifdef NN_PROCESSOR_ARM946ES static uptr GetThreadUniqueValue() { return nn::os::CTR::ARM946ES::GetThreadId(); } static uptr GetInvalidThreadUniqueValue() { return static_cast(-1); } #else static uptr GetThreadUniqueValue() { uptr v; HW_GET_CP15_THREAD_ID_USER_READ_ONLY(v); return v; } static uptr GetInvalidThreadUniqueValue() { return 0; } #endif bool LockedByCurrentThread() const { return GetThreadUniqueValue() == m_ThreadUniqueValue; } private: nn::os::WaitableCounter m_Counter; uptr m_ThreadUniqueValue; s32 m_LockCount; bool IsInitialized() const { return m_LockCount >= 0; } }; NN_UTIL_DETAIL_DEFINE_SCOPED_LOCK(CriticalSection, Enter(), Leave()); }} // namespace nn::os #endif // __cplusplus // 以下、C 用宣言 #include #define NN_OS_CRITICALSECTION_SIZE 12 /*! @addtogroup nn_os os @{ @defgroup nn_os_CriticalSection_c CriticalSection (C) @brief @ref nn::os::CriticalSection の C インタフェースモジュールです。 @{ */ /*! @struct nnosCriticalSection @brief クリティカルセクションを表す C の構造体です。 @brief 対応するクラス @ref nn::os::CriticalSection を参照してください。 */ NN_UTIL_DETAIL_CLIBIMPL_DEFINE_BUFFER_CLASS(nnosCriticalSection, nn::os::CriticalSection, NN_OS_CRITICALSECTION_SIZE, u32); /*! @brief 対応する C++ 関数を参照してください。@ref nn::os::CriticalSection::Initialize */ NN_EXTERN_C void nnosCriticalSectionInitialize(nnosCriticalSection* this_); /*! @brief 対応する C++ 関数を参照してください。@ref nn::os::CriticalSection::TryInitialize */ NN_EXTERN_C bool nnosCriticalSectionTryInitialize(nnosCriticalSection* this_); /*! @brief 対応する C++ 関数を参照してください。@ref nn::os::CriticalSection::Enter */ NN_EXTERN_C void nnosCriticalSectionEnter(nnosCriticalSection* this_); /*! @brief 対応する C++ 関数を参照してください。@ref nn::os::CriticalSection::TryEnter */ NN_EXTERN_C bool nnosCriticalSectionTryEnter(nnosCriticalSection* this_); /*! @brief 対応する C++ 関数を参照してください。@ref nn::os::CriticalSection::Leave */ NN_EXTERN_C void nnosCriticalSectionLeave(nnosCriticalSection* this_); /*! @brief 対応する C++ 関数を参照してください。@ref nn::os::CriticalSection::Finalize */ NN_EXTERN_C void nnosCriticalSectionFinalize(nnosCriticalSection* this_); /*! @} @} */ #endif