1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     os_LightBarrier.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: 16563 $
14  *---------------------------------------------------------------------------*/
15 
16 #ifndef NN_OS_OS_LIGHTBARRIER_H_
17 #define NN_OS_OS_LIGHTBARRIER_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 <cstdlib>
26 
27 namespace nn { namespace os {
28 
29 /*!
30     @file
31     @brief    LightBarrier に関するAPI の宣言
32 
33     :include nn/os.h
34 */
35 
36 /*!--------------------------------------------------------------------------*
37   @brief        複数のスレッドの到着を待つ同期機構です。
38 
39                 初期化時に指定された数のスレッドが到着するまで、
40                 早く到着したスレッドを待機させます。
41 
42                 このクラスを N 個を超えるスレッドのなかから N 個の到着を
43                 待つために使用することはできません。
44 
45                 @ref Await と初期化/終了を除くこのクラスのメンバ関数はスレッドセーフです。
46                 @ref Await はコンストラクタまたは @ref Initialize メンバ関数で
47                 指定する数のスレッド間でのみスレッドセーフです。
48 
49  *---------------------------------------------------------------------------*/
50 class LightBarrier
51 {
52 private:
53     struct IncrementIfLessThan_1
54     {
operatorIncrementIfLessThan_155         bool operator()(s32& x)
56         {
57             if( x < -1 )
58             {
59                 ++x;
60                 return true;
61             }
62             else
63             {
64                 return false;
65             }
66         }
67     };
68 
69 private:
70     WaitableCounter     m_CounterPlus;
71     WaitableCounter     m_CounterMinus;
72     s32                 m_ReleaseCountAndSign;
73 
74 public:
75     //----------------------------------------
76     //! @name 初期化/終了
77     //@{
78 
79     /*!--------------------------------------------------------------------------*
80       @brief        コンストラクタです。
81 
82                     初期化を行わないコンストラクタです。
83 
84                     別途 @ref Initialize を呼び出す必要があります。
85 
86       @return       なし。
87 
88      *---------------------------------------------------------------------------*/
LightBarrier()89     LightBarrier() {}
90 
91     /*!--------------------------------------------------------------------------*
92       @brief        コンストラクタです。
93 
94                     初期化を行うコンストラクタです。
95 
96                     別途 @ref Initialize を呼び出す必要はありません。
97 
98       @param[in]    numWait     待ち合わせるスレッドの数を指定します。
99 
100       @return       なし。
101 
102      *---------------------------------------------------------------------------*/
LightBarrier(s32 numWait)103     LightBarrier(s32 numWait) { Initialize(numWait); }
104 
105     /*!--------------------------------------------------------------------------*
106       @brief        初期化を行います。
107 
108       @param[in]    numWait     待ち合わせるスレッドの数を指定します。
109 
110       @return       なし。
111 
112      *---------------------------------------------------------------------------*/
Initialize(s32 numWait)113     void Initialize(s32 numWait)
114     {
115         NN_MIN_TASSERT_( numWait, 1 );
116 
117         *m_CounterPlus  = - numWait;
118         *m_CounterMinus = - numWait;
119         m_ReleaseCountAndSign = numWait;
120     }
121 
122     /*!--------------------------------------------------------------------------*
123       @brief        終了処理を行います。
124 
125       @return       なし。
126 
127      *---------------------------------------------------------------------------*/
Finalize()128     void Finalize() {}
129 
130     //@}
131 
132     //----------------------------------------
133     //! @name デバッグ用情報取得
134     //@{
135 
136     /*!--------------------------------------------------------------------------*
137       @brief        待ちわせるスレッドの数を取得します。
138 
139                     コンストラクタまたは @ref Initialize で指定した
140                     待ちわせるスレッドの数を取得します。
141 
142       @return       待ち合わせるスレッドの数を返します。
143 
144      *---------------------------------------------------------------------------*/
GetReleaseCount()145     s32 GetReleaseCount() const { return std::abs(m_ReleaseCountAndSign); }
146 
147     /*!--------------------------------------------------------------------------*
148       @brief        待機しているスレッドの数を取得します。
149 
150                     既に到着して他のスレッドが到着するのを待機している
151                     スレッドの数を取得します。
152 
153       @return       待機しているスレッドの数を返します。
154 
155      *---------------------------------------------------------------------------*/
GetNumOfWaiting()156     s32 GetNumOfWaiting() const
157     {
158         const s32 relCountAndSign = m_ReleaseCountAndSign;
159         s32 minusRemain;
160         s32 relCount;
161 
162         if( relCountAndSign > 0 )
163         {
164             minusRemain = *m_CounterPlus;
165             relCount    = relCountAndSign;
166         }
167         else
168         {
169             minusRemain = *m_CounterMinus;
170             relCount    = - relCountAndSign;
171         }
172 
173         return (minusRemain == 0) ? 0: relCount + minusRemain;
174     }
175 
176     //@}
177 
178     //----------------------------------------
179     //! @name スレッドの待ち合わせ
180     //@{
181 
182     /*!--------------------------------------------------------------------------*
183       @brief        他のスレッドの到着を待ちます。
184 
185                     コンストラクタまたは @ref Initialize で指定した数のスレッドが
186                     Await を呼ぶまで待機します。
187 
188       @return       なし。
189 
190      *---------------------------------------------------------------------------*/
Await()191     void Await()
192     {
193         const s32 relCountAndSign = m_ReleaseCountAndSign;
194 
195         if( relCountAndSign > 0 )
196         {
197             AwaitImpl(&m_CounterPlus, &m_CounterMinus, relCountAndSign);
198         }
199         else
200         {
201             AwaitImpl(&m_CounterMinus, &m_CounterPlus, - relCountAndSign);
202         }
203     }
204 
205     //@}
206 
207 private:
AwaitImpl(nn::os::WaitableCounter * pCur,nn::os::WaitableCounter * pNext,s32 relCount)208     void AwaitImpl(nn::os::WaitableCounter* pCur, nn::os::WaitableCounter* pNext, s32 relCount)
209     {
210         IncrementIfLessThan_1 updater;
211 
212         if( (*pCur)->AtomicUpdateConditional(updater) )
213         {
214             pCur->WaitIfLessThan(0);
215         }
216         else
217         {
218             m_ReleaseCountAndSign = - m_ReleaseCountAndSign;
219             **pNext = - relCount;
220             **pCur  = 0;
221             pCur->SignalAll();
222         }
223     }
224 };
225 
226 
227 }} // namespace nn::os
228 
229 #endif // __cplusplus
230 
231 // TODO: C 用 API が必要です。
232 
233 #endif  // ifndef NN_OS_OS_LIGHTBARRIER_H_
234