1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     fnd_Interlocked.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: 24961 $
14  *---------------------------------------------------------------------------*/
15 
16 /* @file
17   @brief 排他制御された変数を扱うクラスの宣言
18 */
19 
20 #ifndef NN_FND_FND_INTERLOCKED_H_
21 #define NN_FND_FND_INTERLOCKED_H_
22 
23 #ifdef __cplusplus
24 
25 #include <nn/types.h>
26 
27 #if     defined(NN_PROCESSOR_ARM_V4) || defined(NN_PROCESSOR_ARM_V5)
28     #include <nn/fnd/ARMv4/fnd_Interlocked.h>
29     namespace nn {
30         namespace fnd {
31             using ARMv4::Interlocked;
32         }
33     }
34 #elif   defined(NN_PROCESSOR_ARM_V6)
35     #include <nn/fnd/ARMv6/fnd_Interlocked.h>
36     namespace nn {
37         namespace fnd {
38             using ARMv6::Interlocked;
39         }
40     }
41 #else
42     #error processor not selected
43 #endif
44 
45 namespace nn { namespace fnd {
46 
47 /*
48     @brief 排他制御された変数を扱うクラステンプレートです。
49 
50            テンプレート引数 T は POD である必要があります。
51 */
52 template <typename T>
53 class InterlockedVariable
54 {
55 private:
56     template <typename U, typename = void> struct StorageSelecter;
57 
58     template <typename U> struct StorageSelecter<U, typename nn::util::enable_if<sizeof(U) == sizeof(s64)>::type>
59     {
60         typedef s64 Type;
61     };
62 
63     template <typename U> struct StorageSelecter<U, typename nn::util::enable_if<sizeof(U) == sizeof(s32)>::type>
64     {
65         typedef s32 Type;
66     };
67 
68     template <typename U> struct StorageSelecter<U, typename nn::util::enable_if<sizeof(U) == sizeof(s16)>::type>
69     {
70         typedef s16 Type;
71     };
72 
73     template <typename U> struct StorageSelecter<U, typename nn::util::enable_if<sizeof(U) == sizeof(s8)>::type>
74     {
75         typedef s8 Type;
76     };
77 
78 private:
79     volatile T m_v; //!< バリア変数
80 
81     // AtomicUpdateConditional 用関数オブジェクトの定義
82 
83     template <class Converter>
84     struct ConvertFunc
85     {
86         Converter& m_converter;
87         ConvertFunc(Converter converter) : m_converter(converter) {}
88         bool operator()(T& x) { x = m_converter(x); return true; }
89     };
90 
91 #define NN_UTIL_INTERLOCKED_DEFINE_ASSIGN_FUNC(F, op)                       \
92     struct F                                                                \
93     {                                                                       \
94         T m_operand;                                                        \
95         template <typename U> F(const U& operand) : m_operand(operand) {}   \
96         bool operator()(T& x) { x op m_operand; return true; }              \
97     };
98 
99     NN_UTIL_INTERLOCKED_DEFINE_ASSIGN_FUNC(AssignFunc, =)
100     NN_UTIL_INTERLOCKED_DEFINE_ASSIGN_FUNC(PlusFunc, +=)
101     NN_UTIL_INTERLOCKED_DEFINE_ASSIGN_FUNC(MinusFunc, -=)
102     NN_UTIL_INTERLOCKED_DEFINE_ASSIGN_FUNC(OrFunc, |=)
103     NN_UTIL_INTERLOCKED_DEFINE_ASSIGN_FUNC(AndFunc, &=)
104     NN_UTIL_INTERLOCKED_DEFINE_ASSIGN_FUNC(XorFunc, ^=)
105 
106 #undef NN_UTIL_INTERLOCKED_DEFINE_ASSIGN_FUNC
107 
108 #define NN_UTIL_INTERLOCKED_DEFINE_UNARY_FUNC(F, preop, postop) \
109     struct F                                                    \
110     {                                                           \
111         T result;                                               \
112         bool operator()(T& x) { result = preop x postop; return true; }   \
113     };
114 
115     NN_UTIL_INTERLOCKED_DEFINE_UNARY_FUNC(PreIncFunc,  ++,   )
116     NN_UTIL_INTERLOCKED_DEFINE_UNARY_FUNC(PreDecFunc,  --,   )
117     NN_UTIL_INTERLOCKED_DEFINE_UNARY_FUNC(PostIncFunc,   , ++)
118     NN_UTIL_INTERLOCKED_DEFINE_UNARY_FUNC(PostDecFunc,   , --)
119 
120 #undef NN_UTIL_INTERLOCKED_DEFINE_UNARY_FUNC
121 
122     struct CompareAndSwapFunc
123     {
124         T m_comparand;
125         T m_value;
126         T m_result;
127         bool operator()(T& x)
128         {
129             m_result = x;
130             if (x == m_comparand)
131             {
132                 x = m_value;
133                 return true;
134             }
135             else
136             {
137                 return false;
138             }
139         }
140         CompareAndSwapFunc(T comparand, T value) : m_comparand(comparand), m_value(value) {}
141     };
142 
143 public:
144     /*
145         @brief コンストラクタです。
146 
147         @param[in] v 初期値
148     */
149     InterlockedVariable() : m_v() {}
150     InterlockedVariable(T v) : m_v(v) {}
151 
152     operator T() const
153     {
154         typename StorageSelecter<T>::Type x = reinterpret_cast<const volatile typename StorageSelecter<T>::Type&>(m_v);
155         return reinterpret_cast<T&>(x);
156     }
157     T operator ->()
158     {
159         typename StorageSelecter<T>::Type x = reinterpret_cast<const volatile typename StorageSelecter<T>::Type&>(m_v);
160         return reinterpret_cast<T&>(x);
161     }
162 
163     /*
164         @brief バリア変数を取得します。
165 
166         @return バリア変数を返します。
167     */
168     T Read() const { return *this; }
169 
170     /*
171         @brief バリア変数を排他制御なしで変更します。
172 
173         @param[in] v 変更後の値
174     */
175     void WriteNotAtomic (T v) { m_v = v; }
176 
177     /*
178         @brief 関数オブジェクトを排他制御下で実行します。
179 
180         @param[in] updater 関数オブジェクト
181 
182         @return 関数オブジェクトの実行の結果を返します。
183     */
184     template <class Updater>
185     bool AtomicUpdateConditional(Updater& updater) { return Interlocked::AtomicUpdate(&m_v, updater); }
186 
187     /*
188         @brief バリア変数と comparand を比較し、等しければバリア変数を value に変更します。
189 
190         @param[in] comparand 比較する値
191         @param[in] value 等しい場合に設定する値
192 
193         @return 比較、代入を行う直前でのバリア変数を返します。
194     */
195     T CompareAndSwap(T comparand, T value)
196     {
197         CompareAndSwapFunc f(comparand, value);
198         AtomicUpdateConditional(f);
199         return f.m_result;
200     }
201 
202     /*
203         @brief バリア変数と comparand を比較し、等しければバリア変数を value に変更します。
204 
205         @param[in] comparand 比較する値
206         @param[in] value 等しい場合に設定する値
207 
208         @return value への変更が成功すれば 0 を返し、失敗すれば 1 を返します。
209     */
210     int CompareAndSwapWeak(T comparand, T value)
211     {
212         return Interlocked::CompareAndSwapWeak(&m_v, comparand, value);
213     }
214 
215     /*
216         @brief バリア変数と cmpValue が等しくなるまで待ち、等しくなれば内部を newValue に変更します。
217 
218         @param[in] comparand 比較する値
219         @param[in] newValue 等しい場合に設定する値
220     */
221     void CompareAndSpinWaitAndSwap(T cmpValue, T newValue)
222     {
223         for(;;)
224         {
225             if( m_v == cmpValue )
226             {
227                 if( CompareAndSwapWeak(cmpValue, newValue) == 0 )
228                 {
229                     break;
230                 }
231             }
232         }
233     }
234 
235     // 演算子のオーバーロード
236 
237 #define NN_UTIL_INTERLOCKED_DEFINE_ASSIGN_OPERATOR(F, op)                                       \
238     template <typename V>                                                                       \
239     void operator op(V v) { F f(v); AtomicUpdateConditional(f); }
240 
241     NN_UTIL_INTERLOCKED_DEFINE_ASSIGN_OPERATOR(AssignFunc, =)
242     NN_UTIL_INTERLOCKED_DEFINE_ASSIGN_OPERATOR(PlusFunc, +=)
243     NN_UTIL_INTERLOCKED_DEFINE_ASSIGN_OPERATOR(MinusFunc, -=)
244     NN_UTIL_INTERLOCKED_DEFINE_ASSIGN_OPERATOR(OrFunc, |=)
245     NN_UTIL_INTERLOCKED_DEFINE_ASSIGN_OPERATOR(AndFunc, &=)
246     NN_UTIL_INTERLOCKED_DEFINE_ASSIGN_OPERATOR(XorFunc, ^=)
247 
248 #undef NN_UTIL_INTERLOCKED_DEFINE_ASSIGN_OPERATOR
249 
250 #define NN_UTIL_INTERLOCKED_DEFINE_UNARY_OPERATOR(F, op, premarker)                                     \
251     InterlockedVariable operator op(premarker) { F f; AtomicUpdateConditional(f); return f.result; }    \
252 
253     NN_UTIL_INTERLOCKED_DEFINE_UNARY_OPERATOR(PreIncFunc,  ++,    )
254     NN_UTIL_INTERLOCKED_DEFINE_UNARY_OPERATOR(PreDecFunc,  --,    )
255     NN_UTIL_INTERLOCKED_DEFINE_UNARY_OPERATOR(PostIncFunc, ++, int)
256     NN_UTIL_INTERLOCKED_DEFINE_UNARY_OPERATOR(PostDecFunc, --, int)
257 
258 #undef NN_UTIL_INTERLOCKED_DEFINE_UNARY_OPERATOR
259 };
260 
261 }}
262 
263 #endif // __cplusplus
264 
265 #endif  // NN_UTIL_INTERLOCKED_H_
266