1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     os_InterCoreLightSemaphore.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: 33014 $
14  *---------------------------------------------------------------------------*/
15 
16 #ifndef NN_OS_OS_INTERCORE_LIGHTSEMAPHORE_H_
17 #define NN_OS_OS_INTERCORE_LIGHTSEMAPHORE_H_
18 
19 #ifdef __cplusplus
20 
21 #include <nn/os/os_MemoryBarrierSelect.h>
22 #include <nn/os/os_WaitableCounter.h>
23 #include <nn/assert.h>
24 #include <nn/WithInitialize.h>
25 #include <nn/util/detail/util_ScopedLockImpl.h>
26 #include <nn/config.h>
27 
28 namespace nn { namespace os {
29 
30 /*! :private
31     @file
32     @brief    InterCoreLightSemaphore に関するAPI の宣言
33 
34     :include nn/os.h
35 */
36 
37 /*! :private
38   @brief        スレッド間でリソース数の排他制御を行う同期機構です。
39 
40                 内部にカウンタ値を持ち、これを各スレッドで減算/加算する形で
41                 リソース数を管理します。
42                 カウンタ値が 0 の場合は 1 以上になるまで待つことができます。
43 
44                 複数の同期オブジェクトを同時に待つことができないという点を除いて
45                 nn::os::InterCoreLightSemaphore の方が nn::os::Semaphore より
46                 優れているため、通常は nn::os::InterCoreLightSemaphore を使用すべきです。
47 
48                 このクラスの初期化/終了以外のメンバ関数はスレッドセーフです。
49 
50  */
51 class InterCoreLightSemaphore
52 {
53 public:
54     static const s32 MAX_MAX_COUNT  = 0x7fff;
55 
56 private:
57     struct DecrementIfPositive
58     {
operatorDecrementIfPositive59         bool operator()(s32& x)
60         {
61             if( x > 0 )
62             {
63                 --x;
64                 return true;
65             }
66             else
67             {
68                 return false;
69             }
70         }
71     };
72     struct LimitedAdd
73     {
74         s32 max;
75         s32 value;
76         s32 beforeUpdate;
77 
operatorLimitedAdd78         bool operator()(s32& x)
79         {
80             beforeUpdate = x;
81 
82             if( x > max - value )
83             {
84                 x = max;
85             }
86             else
87             {
88                 x += value;
89             }
90 
91             return true;
92         }
93     };
94 
95 private:
96     WaitableCounter                 m_Counter;
97 #if NN_PLATFORM_HAS_16BIT_LL_SC
98     fnd::InterlockedVariable<s16>   m_NumWaiting;
99     s16                             m_Max;
100 #else
101     fnd::InterlockedVariable<s32>   m_NumWaiting;
102 #endif
103 
104 
105 public:
106     //----------------------------------------
107     //! @name 初期化/終了
108     //@{
109 
110     /*! :private
111       @brief        コンストラクタです。
112 
113                     初期化を行わないコンストラクタです。
114 
115                     別途 @ref Initialize を呼び出す必要があります。
116 
117      */
InterCoreLightSemaphore()118     InterCoreLightSemaphore() {}
119 
120     /*! :private
121       @brief        コンストラクタです。
122 
123                     初期化を行うコンストラクタです。
124 
125                     別途 @ref Initialize を呼び出す必要はありません。
126 
127       @param[in]    initialCount    初期カウンタ値を指定します。
128       @param[in]    maxCount        カウンタ値の最大値を指定します。
129                                     指定できる最大値は 32767 です。
130 
131      */
InterCoreLightSemaphore(s32 initialCount,s32 maxCount)132     InterCoreLightSemaphore(s32 initialCount, s32 maxCount) { Initialize(initialCount, maxCount); }
133 
InterCoreLightSemaphore(s32 initialCount)134     InterCoreLightSemaphore(s32 initialCount) { Initialize(initialCount); }
135 
136     /*! :private
137       @brief        初期化を行います。
138 
139       @param[in]    initialCount    初期カウンタ値を指定します。
140       @param[in]    maxCount        カウンタ値の最大値を指定します。
141                                     指定できる最大値は 32767 です。
142 
143       @return       なし。
144 
145      */
Initialize(s32 initialCount,s32 maxCount)146     void Initialize(s32 initialCount, s32 maxCount)
147     {
148         NN_MIN_TASSERT_( initialCount, 0 );
149         NN_MIN_TASSERT_( maxCount, 1 );
150         NN_MAX_TASSERT_( initialCount, maxCount );
151         NN_MAX_TASSERT_( maxCount, MAX_MAX_COUNT );
152 
153         *m_Counter      = initialCount;
154         m_NumWaiting    = 0;
155 #if NN_PLATFORM_HAS_16BIT_LL_SC
156         m_Max           = maxCount;
157 #else
158         NN_UNUSED_VAR(maxCount);
159 #endif
160     }
Initialize(s32 initialCount)161     void Initialize(s32 initialCount) { Initialize(initialCount, MAX_MAX_COUNT); }
162 
163     /*! :private
164       @brief        終了処理を行います。
165 
166       @return       なし。
167 
168      */
Finalize()169     void Finalize() {}
170 
171     //@}
172 
173     //----------------------------------------
174     //! @name デバッグ用情報取得
175     //@{
176 
177     /*! :private
178       @brief        コンストラクタまたは @ref Initialize で指定したカウンタ値の
179                     最大値を取得します。
180 
181       @return       設定されているカウンタ値の最大値を返します。
182 
183      */
184 #if NN_PLATFORM_HAS_16BIT_LL_SC
GetMax()185     s32  GetMax()   const { return m_Max; }
186 #endif
187 
188     /*! :private
189       @brief        現在のカウンタ値を取得します。
190 
191       @return       現在のカウンタ値を返します。
192 
193      */
GetCount()194     s32  GetCount() const { return *m_Counter; }
195 
196     //@}
197 
198     //----------------------------------------
199     //! @name カウンタの操作と待ち合わせ
200     //@{
201 
202     /*! :private
203       @brief        カウンタ値を 1 減算します。
204 
205                     カウンタ値が 0 であった場合は 1 以上になるまで待ったうえで
206                     減算を行います。
207 
208       @return       なし。
209 
210      */
Acquire()211     void Acquire()
212     {
213         while( ! TryAcquire() )
214         {
215             ++m_NumWaiting;
216             m_Counter.WaitIfLessThan(1);
217             --m_NumWaiting;
218         }
219     }
220 
221     /*! :private
222       @brief        カウンタ値を 1 減算しようとします。
223 
224                     カウンタ値が 0 であった場合は減算を行わずに返ります。
225 
226       @return       減算を行ったなら true、カウンタ値が 0 であったために
227                     減算を行えなかった場合は false を返します。
228 
229      */
TryAcquire()230     bool TryAcquire()
231     {
232         DecrementIfPositive updater;
233         bool ret = m_Counter->AtomicUpdateConditional(updater);
234         DataSynchronizationBarrier();
235         return ret;
236     }
237 
238     /*! :private
239       @brief        カウンタ値に加算します。
240 
241                     カウンタ値が 0 であり、かついずれかのスレッドが
242                     カウンタ値が 1 以上になるのを待機している場合
243                     カウンタ値が加算されるとそのスレッドの待機が解除されます。
244 
245                     複数のスレッドが待機している場合に 2 以上の値を
246                     加算すると複数のスレッドの待機が解除されます。
247                     加算した値より待機しているスレッドの方が多い場合は
248                     加算した値分のスレッドだけが起床し、
249                     残りは待機したままです。
250 
251                     起床するスレッドはスレッドの優先度に応じて優先度が高い方から起床します。
252 
253       @param[in]    releaseCount    加算する値を指定します。
254                                     1 以上でなければなりません。
255                                     指定しなかった場合は 1 加算します。
256 
257       @return       加算前のカウンタ値を返します。
258 
259      */
260     s32 Release(s32 releaseCount = 1);
261 
262     //@}
263 
264     class ScopedAcquire
265     {
266     private:
267         InterCoreLightSemaphore* m_Semaphore;
268     public:
269         ScopedAcquire(InterCoreLightSemaphore& semaphore, bool wait = true)
270             : m_Semaphore(wait ? (semaphore.Acquire(), &semaphore) : (semaphore.TryAcquire() ? &semaphore : 0)) {}
Aquired()271         bool Aquired() const { return m_Semaphore != 0; }
Detach()272         void Detach() { this->m_Semaphore = 0; }
~ScopedAcquire()273         ~ScopedAcquire() { if (m_Semaphore) { m_Semaphore->Release(); } }
274     };
275 };
276 
277 
278 }} // namespace nn::os
279 
280 #endif // __cplusplus
281 
282 // 以下、C 用宣言
283 
284 #include <nn/util/detail/util_CLibImpl.h>
285 
286 NN_UTIL_DETAIL_CLIBIMPL_DEFINE_BUFFER_CLASS(nnosInterCoreLightSemaphore, nn::os::InterCoreLightSemaphore, 8, u32);
287 
288 NN_EXTERN_C void nnosInterCoreLightSemaphoreInitialize(nnosInterCoreLightSemaphore* this_, s32 initialCount, s32 maxCount);
289 
290 #if NN_PLATFORM_HAS_16BIT_LL_SC
291 NN_EXTERN_C s32 nnosInterCoreLightSemaphoreGetMax(nnosInterCoreLightSemaphore* p);
292 #endif
293 
294 NN_EXTERN_C s32 nnosInterCoreLightSemaphoreGetCount(nnosInterCoreLightSemaphore* p);
295 
296 NN_EXTERN_C s32 nnosInterCoreLightSemaphoreRelease(nnosInterCoreLightSemaphore* this_, s32 releaseCount);
297 
298 NN_EXTERN_C void nnosInterCoreLightSemaphoreAcquire(nnosInterCoreLightSemaphore* this_);
299 
300 NN_EXTERN_C bool nnosInterCoreLightSemaphoreTryAcquire(nnosInterCoreLightSemaphore* this_);
301 
302 NN_EXTERN_C void nnosInterCoreLightSemaphoreFinalize(nnosInterCoreLightSemaphore* this_);
303 
304 #endif  // ifndef NN_OS_OS_INTERCORE_LIGHTSEMAPHORE_H_
305