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