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