1 /*---------------------------------------------------------------------------*
2   Project:  Horizon
3   File:     os_Mutex.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: 33844 $
14  *---------------------------------------------------------------------------*/
15 
16 /*! @file
17     @brief      Mutex に関する API の宣言
18 
19     :include nn/os.h
20 */
21 
22 #ifndef NN_OS_OS_MUTEX_H_
23 #define NN_OS_OS_MUTEX_H_
24 
25 #include <nn/types.h>
26 #include <nn/Handle.h>
27 #include <nn/Result.h>
28 #include <nn/WithInitialize.h>
29 #include <nn/os/os_Synchronization.h>
30 
31 #include <nn/util/util_Result.h>
32 #include <nn/util/detail/util_ScopedLockImpl.h>
33 #include <nn/err.h>
34 
35 #ifdef __cplusplus
36 
37 namespace nn { namespace os {
38 
39 /*!
40     @brief ミューテックスを扱う為のクラスです。ミューテックスは排他制御を行うための同期オブジェクトです。
41 
42             通常は @ref nn::os::Mutex ではなく、@ref nn::os::CriticalSection を使用すべきです。
43             スレッド間でロックが衝突しない場合のパフォーマンスでは
44             @ref nn::os::CriticalSection の方が @ref nn::os::Mutex より
45             圧倒的に高速です。
46 
47            プログラムの特定の箇所について複数のスレッドからの同時実行を抑制し、
48            データやレジスタなどのリソースが複数のスレッドから同時にアクセスされることを防ぎます。
49 
50            ミューテックスは @ref Lock によってロックすることができます。
51            あるスレッドがロックしているミューテックスを、他のスレッドがロックすることはできず、
52            他のスレッドがロックしようとした場合は、ミューテックスがアンロックされるまでブロックします。
53 
54            アンロックは @ref Unlock によって行いますが、
55            ミューテックスのロックはスレッドに紐付けられているため、ロックしているスレッドからしか呼ぶことはできません。
56 
57            Mutex は再帰ロックミューテックスであり、ロックしているスレッドがさらに @ref Lock を呼ぶことができます。
58            このとき、@ref Lock を読んだ回数と同じ回数だけ @ref Unlock が呼ばれるまで、ミューテックスがアンロックされません。
59 
60            Mutex は優先度継承を実装しています。
61            低い優先度を持つスレッド L がロックしているミューテックスのロックを、
62            高い優先度を持つスレッド H が取得しようとしたとき、
63            スレッド L の優先度は一時的にスレッド H と同じ値まで引き上げられます。
64 
65            Mutex オブジェクトの Wait 動作は、ロックの取得です。
66            ロックが取得できると、Wait 動作は解放されます。
67 
68            @ref nn::os::Mutex::ScopedLock を使うとオブジェクトの生成からスコープの有効範囲の終わりまでの間でMutexによるロックをかけることができます。
69 
70            Mutex オブジェクトは 32個まで作成することが出来ます。
71            また、API によってはこの制限にカウントされるリソースを消費する場合がありますので、ご注意ください。
72 */
73 
74 class Mutex : public WaitObject
75 {
76 public:
77 
78     /*!
79       @brief        ミューテックスを構築し、初期化します。
80 
81       @param[in]    initialLocked   true を指定した場合、初期化と同時にロック状態にします。所有者は作成したスレッドになります。
82     */
83     explicit Mutex(bool initialLocked);
84 
85     /*!
86       @brief        ミューテックスオブジェクトを構築します。
87 
88                     ミューテックスオブジェクトを構築しますが、初期化はしません。
89                     別途、@ref Initialize を呼ぶ必要があります。
90     */
Mutex()91     Mutex() {}
92 
93     /*!
94       @brief        オブジェクトを構築し、初期化を行います。
95     */
96     Mutex(const nn::WithInitialize&);
97 
98     /*!
99       @brief        ミューテックスを初期化します。
100 
101       @param[in]    initialLocked   true を指定した場合、初期化と同時にロック状態にします。所有者は作成したスレッドになります。
102     */
103     void Initialize(bool initialLocked = false);
104 
105     /*!
106       @brief        ミューテックスの初期化を試みます。
107 
108       @param[in]    initialLocked   true を指定した場合、初期化と同時にロック状態にします。所有者は作成したスレッドになります。
109 
110       @return       関数の実行結果を返します。
111     */
112     nn::Result TryInitialize(bool initialLocked = false);
113 
114     /*!
115       @brief        ミューテックスを破棄します。
116 
117                     デストラクタからも自動で呼ばれます。
118       @return       無し。
119     */
Finalize()120     void Finalize() { WaitObject::Finalize(); }
121 
122     /*!
123       @brief デストラクタです。
124     */
~Mutex()125     ~Mutex() {}
126 
127     /*!
128       @brief        ミューテックスをロックします。
129 
130                     他スレッドでロックされている場合はブロックします。
131                     自スレッドで既にロックされている場合は、ネスト回数が増加します。
132 
133       @return       無し。
134     */
Lock()135     void Lock() { this->WaitOne(); }
136 
137     /*!
138       @brief        ミューテックスのロックを試みます。
139 
140                     ミューテックスのロックを試み、成功すれば true が返ります。
141                     ロックできなかった場合、最大で timeout で指定した時間だけ待ち、
142                     その間にロックを取得できれば true を返します。
143                     timeout で指定した時間だけ待ってもロックを取得できなかった場合は、false を返します。
144 
145       @param[in]    timeout     タイムアウト時間を指定します。0 を指定すると即座に処理を返します。
146 
147       @return       ロックに成功したかを返します。
148     */
149     bool TryLock(nn::fnd::TimeSpan timeout = 0) { return this->WaitOne(timeout); }
150 
151     /*!
152       @brief        ミューテックスをアンロックします。
153 
154                     自スレッドでロックされているミューテックスのネスト回数を 1 減らします。
155                     ミューテックスをロックしていないスレッドでこの関数を呼ぶことはできません。
156 
157       @return       無し。
158     */
159     void Unlock();
160 
161     /*!
162         @class nn::os::Mutex::ScopedLock
163         @brief オブジェクトの生成時からオブジェクトの存在するスコープを抜けるまで間、Mutexをロックします。
164     */
165     class ScopedLock;
166 
167 private:
168 
169     Result TryInitializeImpl(bool initialLocked);
170 
171 };
172 
173 // インライン実装
174 
TryInitializeImpl(bool initialLocked)175 inline Result Mutex::TryInitializeImpl(bool initialLocked)
176 {
177     Handle handle;
178     NN_UTIL_RETURN_IF_FAILED(nn::svc::CreateMutex(&handle, initialLocked));
179     this->SetHandle(handle);
180     return ResultSuccess();
181 }
182 
Initialize(bool initialLocked)183 inline void Mutex::Initialize(bool initialLocked)
184 {
185     NN_ERR_THROW_FATAL(TryInitializeImpl(initialLocked));
186 }
187 
TryInitialize(bool initialLocked)188 inline nn::Result Mutex::TryInitialize(bool initialLocked)
189 {
190     Result result = TryInitializeImpl(initialLocked);
191     if (result.GetSummary() == Result::SUMMARY_OUT_OF_RESOURCE)
192     {
193         return result;
194     }
195     NN_ERR_THROW_FATAL(result);
196     return result;
197 }
198 
Mutex(bool initialLocked)199 inline Mutex::Mutex(bool initialLocked)
200 {
201     Initialize(initialLocked);
202 }
203 
Mutex(const nn::WithInitialize &)204 inline Mutex::Mutex(const nn::WithInitialize&)
205 {
206     Initialize(false);
207 }
208 
Unlock()209 inline void Mutex::Unlock()
210 {
211     NN_ERR_THROW_FATAL(nn::svc::ReleaseMutex(GetHandle()));
212 }
213 
214 NN_UTIL_DETAIL_DEFINE_SCOPED_LOCK(Mutex, Lock(), Unlock());
215 
216 }} // namespace nn::os
217 
218 #endif // __cplusplus
219 
220 // 以下、C 用宣言
221 
222 #include <nn/util/detail/util_CLibImpl.h>
223 
224 /*!
225   @addtogroup   nn_os
226   @{
227 
228   @defgroup     nn_os_Mutex_c       Mutex (C)
229 
230   @brief        @ref nn::os::Mutex の C インタフェースモジュールです。
231 
232   @{
233 */
234 
235 /*!
236   @struct       nnosMutex
237   @brief        ミューテックスを表す C の構造体です。
238   @brief 対応するクラス @ref nn::os::Mutex を参照してください。
239 */
240 NN_UTIL_DETAIL_CLIBIMPL_DEFINE_BUFFER_CLASS(nnosMutex, nn::os::Mutex, 4, u32);
241 NN_UTIL_DETAIL_CLIBIMPL_DECLARE_CONVERSION(nnosMutexToWaitObject, nnosMutex, nnosWaitObject);
242 NN_UTIL_DETAIL_CLIBIMPL_DECLARE_CONVERSION(nnosWaitObjectToMutex, nnosWaitObject, nnosMutex);
243 
244 /*!
245   @brief 対応する C++ 関数 @ref nn::os::Mutex::Initialize を参照してください。
246 */
247 NN_EXTERN_C void nnosMutexInitialize(nnosMutex* this_, bool initialLocked);
248 
249 /*!
250   @brief 対応する C++ 関数 @ref nn::os::Mutex::TryInitialize を参照してください。
251 */
252 NN_EXTERN_C bool nnosMutexTryInitialize(nnosMutex* this_, bool initialLocked);
253 
254 /*!
255   @brief 対応する C++ 関数 @ref nn::os::Mutex::Lock を参照してください。
256 */
257 NN_EXTERN_C void nnosMutexLock(nnosMutex* this_);
258 
259 /*!
260   @brief 対応する C++ 関数 @ref nn::os::Mutex::TryLock を参照してください。
261 */
262 NN_EXTERN_C bool nnosMutexTryLock(nnosMutex* this_, s64 timeout);
263 
264 /*!
265   @brief 対応する C++ 関数 @ref nn::os::Mutex::Unlock を参照してください。
266 */
267 NN_EXTERN_C void nnosMutexUnlock(nnosMutex* this_);
268 
269 /*!
270   @brief 対応する C++ 関数 @ref nn::os::Mutex::Finalize を参照してください。
271 */
272 NN_EXTERN_C void nnosMutexFinalize(nnosMutex* this_);
273 
274 /*!
275   @}
276 
277   @}
278 */
279 
280 #endif /* NN_OS_IPC_OS_MUTEX_H_ */
281