1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     os_InterCoreCriticalSection.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: 31411 $
14  *---------------------------------------------------------------------------*/
15 
16 /*! :private
17     @file
18     @brief      InterCoreCriticalSection に関する API の宣言
19 
20     :include nn/os.h
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 /*! :private
38     @brief      マルチコア間での排他制御を行うためのクラスです。
39 
40                 基本的な使い方などについては @ref nn::os::CriticalSection を参照してください。
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     /*! :private
78       @brief        オブジェクトを構築します。
79 
80                     初期化しないコンストラクタと、初期化するコンストラクタが用意されています。
81 
82                     初期化しない場合、使用する前に、@ref nn::os::InterCoreCriticalSection::Initialize を呼んで明示的に初期化する必要があります。
83     */
InterCoreCriticalSection()84     InterCoreCriticalSection() : m_ThreadUniqueValue(GetInvalidThreadUniqueValue()), m_LockCount(-1) {}
85 
86     /*! :private
87       @brief        オブジェクトを構築し、初期化を行います。
88     */
InterCoreCriticalSection(const nn::WithInitialize &)89     InterCoreCriticalSection(const nn::WithInitialize&) { Initialize(); }
90 
91     /*! :private
92       @brief        オブジェクトを初期化します。
93 
94       @return       無し。
95     */
Initialize()96     void Initialize()
97     {
98         *m_Counter          = 1;
99         m_ThreadUniqueValue = GetInvalidThreadUniqueValue();
100         m_LockCount         = 0;
101     }
102 
103     /*! :private
104       @brief        オブジェクトの初期化を試みます。
105 
106       @return       処理結果を返します。
107     */
TryInitialize()108     nn::Result TryInitialize()
109     {
110         Initialize();
111         return ResultSuccess();
112     }
113 
114     /*! :private
115       @brief        クリティカルセクションを破棄します。
116 
117                     デストラクタから自動的に呼び出されますが、明示的に呼ぶこともできます。
118 
119       @return       無し。
120     */
Finalize()121     void Finalize() { m_LockCount = -1; }
122 
123     /*! :private
124       @brief        デストラクタです。
125     */
~InterCoreCriticalSection()126     ~InterCoreCriticalSection() {}
127 
128     /*! :private
129       @brief        ロックして他のスレッドがクリティカルセクションに進入するのを防ぎます。ブロックします。
130 
131       @return       無し。
132     */
Enter()133     void Enter()
134     {
135         NN_TASSERT_(IsInitialized());
136         // 自スレッドがクリティカルセクションに進入していなければ
137         // クリティカルセクションに進入を試みます。
138         if (!LockedByCurrentThread() && !TryEnterImpl())
139         {
140             EnterImpl();
141         }
142         ++this->m_LockCount;
143     }
144 
145     /*! :private
146       @brief        ロックして他のスレッドがクリティカルセクションに進入するのを防ぎます。ブロックしません。
147 
148       @return       ロックに成功したかを返します。
149     */
TryEnter()150     bool TryEnter()
151     {
152         NN_TASSERT_(IsInitialized());
153         // 自スレッドがクリティカルセクションに進入していなければ
154         // クリティカルセクションに進入を試みます。
155         if (LockedByCurrentThread() || TryEnterImpl())
156         {
157             ++this->m_LockCount;
158             return true;
159         }
160         else
161         {
162             return false;
163         }
164     }
165 
166     /*! :private
167       @brief        アンロックして他のスレッドがクリティカルセクションに進入できるようにします。
168 
169       @return       無し。
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             // クリティカルセクションを取得中のスレッド ID をクリアします。
180             m_ThreadUniqueValue = GetInvalidThreadUniqueValue();
181 
182             // カウンタの符号を正に設定することでロックを解除します。
183             ReverseUpdater updater;
184             m_Counter->AtomicUpdateConditional(updater);
185 
186             DataSynchronizationBarrier();
187 
188             // 待機している最も優先度の高いスレッドを起床します。
189             if( updater.afterUpdate > 1 )
190             {
191                 m_Counter.Signal(1);
192             }
193         }
194     }
195 
196     /*! :private
197         @class nn::os::InterCoreCriticalSection::ScopedLock
198 
199         @brief オブジェクトの生成時からオブジェクトの存在するスコープを抜けるまで間クリティカルセクションに入ります。
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             // クリティカルセクションの進入に成功すれば
220             // カウンタの符号が正から負に変わります。
221 
222             NN_TASSERT_(m_LockCount == 0);
223 
224             // クリティカルセクションを取得したスレッド ID を保存します。
225             this->m_ThreadUniqueValue = GetThreadUniqueValue();
226             return true;
227         }
228         else
229         {
230             return false;
231         }
232     }
233 
234     // TODO: 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 用宣言
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