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