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: 29304 $
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 
71 class Mutex : public WaitObject
72 {
73 public:
74 
75     /*!
76       @brief        ミューテックスを構築し、初期化します。
77 
78       @param[in]    initialLocked   true を指定した場合、初期化と同時にロック状態にします。所有者は作成したスレッドになります。
79     */
80     explicit Mutex(bool initialLocked);
81 
82     /*!
83       @brief        ミューテックスオブジェクトを構築します。
84 
85                     ミューテックスオブジェクトを構築しますが、初期化はしません。
86                     別途、@ref Initialize を呼ぶ必要があります。
87     */
Mutex()88     Mutex() {}
89 
90     /*!
91       @brief        オブジェクトを構築し、初期化を行います。
92     */
93     Mutex(const nn::WithInitialize&);
94 
95     /*!
96       @brief        ミューテックスを初期化します。
97 
98       @param[in]    initialLocked   true を指定した場合、初期化と同時にロック状態にします。所有者は作成したスレッドになります。
99     */
100     void Initialize(bool initialLocked = false);
101 
102     /*!
103       @brief        ミューテックスの初期化を試みます。
104 
105       @param[in]    initialLocked   true を指定した場合、初期化と同時にロック状態にします。所有者は作成したスレッドになります。
106 
107       @return       関数の実行結果を返します。
108     */
109     nn::Result TryInitialize(bool initialLocked = false);
110 
111     /*!
112       @brief        ミューテックスを破棄します。
113 
114                     デストラクタからも自動で呼ばれます。
115       @return       無し。
116     */
Finalize()117     void Finalize() { WaitObject::Finalize(); }
118 
119     /*!
120       @brief デストラクタです。
121     */
~Mutex()122     ~Mutex() {}
123 
124     /*!
125       @brief        ミューテックスをロックします。
126 
127                     他スレッドでロックされている場合はブロックします。
128                     自スレッドで既にロックされている場合は、ネスト回数が増加します。
129 
130       @return       無し。
131     */
Lock()132     void Lock() { this->WaitOne(); }
133 
134     /*!
135       @brief        ミューテックスのロックを試みます。
136 
137                     ミューテックスのロックを試み、成功すれば true が返ります。
138                     ロックできなかった場合、最大で timeout で指定した時間だけ待ち、
139                     その間にロックを取得できれば true を返します。
140                     timeout で指定した時間だけ待ってもロックを取得できなかった場合は、false を返します。
141 
142       @param[in]    timeout     タイムアウト時間を指定します。0 を指定すると即座に処理を返します。
143 
144       @return       ロックに成功したかを返します。
145     */
146     bool TryLock(nn::fnd::TimeSpan timeout = 0) { return this->WaitOne(timeout); }
147 
148     /*!
149       @brief        ミューテックスをアンロックします。
150 
151                     自スレッドでロックされているミューテックスのネスト回数を 1 減らします。
152                     ミューテックスをロックしていないスレッドでこの関数を呼ぶことはできません。
153 
154       @return       無し。
155     */
156     void Unlock();
157 
158     /*!
159         @class nn::os::Mutex::ScopedLock
160         @brief オブジェクトの生成時からオブジェクトの存在するスコープを抜けるまで間、Mutexをロックします。
161     */
162     class ScopedLock;
163 
164 private:
165 
166     Result TryInitializeImpl(bool initialLocked);
167 
168 };
169 
170 // インライン実装
171 
TryInitializeImpl(bool initialLocked)172 inline Result Mutex::TryInitializeImpl(bool initialLocked)
173 {
174     Handle handle;
175     NN_UTIL_RETURN_IF_FAILED(nn::svc::CreateMutex(&handle, initialLocked));
176     this->SetHandle(handle);
177     return ResultSuccess();
178 }
179 
Initialize(bool initialLocked)180 inline void Mutex::Initialize(bool initialLocked)
181 {
182     NN_ERR_THROW_FATAL(TryInitializeImpl(initialLocked));
183 }
184 
TryInitialize(bool initialLocked)185 inline nn::Result Mutex::TryInitialize(bool initialLocked)
186 {
187     Result result = TryInitializeImpl(initialLocked);
188     if (result.GetSummary() == Result::SUMMARY_OUT_OF_RESOURCE)
189     {
190         return result;
191     }
192     NN_ERR_THROW_FATAL(result);
193     return result;
194 }
195 
Mutex(bool initialLocked)196 inline Mutex::Mutex(bool initialLocked)
197 {
198     Initialize(initialLocked);
199 }
200 
Mutex(const nn::WithInitialize &)201 inline Mutex::Mutex(const nn::WithInitialize&)
202 {
203     Initialize(false);
204 }
205 
Unlock()206 inline void Mutex::Unlock()
207 {
208     NN_ERR_THROW_FATAL(nn::svc::ReleaseMutex(GetHandle()));
209 }
210 
211 NN_UTIL_DETAIL_DEFINE_SCOPED_LOCK(Mutex, Lock(), Unlock());
212 
213 }} // namespace nn::os
214 
215 #endif // __cplusplus
216 
217 // 以下、C 用宣言
218 
219 #include <nn/util/detail/util_CLibImpl.h>
220 
221 /*!
222   @addtogroup   nn_os
223   @{
224 
225   @defgroup     nn_os_Mutex_c       Mutex (C)
226 
227   @brief        @ref nn::os::Mutex の C インタフェースモジュールです。
228 
229   @{
230 */
231 
232 /*!
233   @struct       nnosMutex
234   @brief        ミューテックスを表す C の構造体です。
235   @brief 対応するクラス @ref nn::os::Mutex を参照してください。
236 */
237 NN_UTIL_DETAIL_CLIBIMPL_DEFINE_BUFFER_CLASS(nnosMutex, nn::os::Mutex, 4, u32);
238 NN_UTIL_DETAIL_CLIBIMPL_DECLARE_CONVERSION(nnosMutexToWaitObject, nnosMutex, nnosWaitObject);
239 NN_UTIL_DETAIL_CLIBIMPL_DECLARE_CONVERSION(nnosWaitObjectToMutex, nnosWaitObject, nnosMutex);
240 
241 /*!
242   @brief 対応する C++ 関数 @ref nn::os::Mutex::Initialize を参照してください。
243 */
244 NN_EXTERN_C void nnosMutexInitialize(nnosMutex* this_, bool initialLocked);
245 
246 /*!
247   @brief 対応する C++ 関数 @ref nn::os::Mutex::TryInitialize を参照してください。
248 */
249 NN_EXTERN_C bool nnosMutexTryInitialize(nnosMutex* this_, bool initialLocked);
250 
251 /*!
252   @brief 対応する C++ 関数 @ref nn::os::Mutex::Lock を参照してください。
253 */
254 NN_EXTERN_C void nnosMutexLock(nnosMutex* this_);
255 
256 /*!
257   @brief 対応する C++ 関数 @ref nn::os::Mutex::TryLock を参照してください。
258 */
259 NN_EXTERN_C bool nnosMutexTryLock(nnosMutex* this_, s64 timeout);
260 
261 /*!
262   @brief 対応する C++ 関数 @ref nn::os::Mutex::Unlock を参照してください。
263 */
264 NN_EXTERN_C void nnosMutexUnlock(nnosMutex* this_);
265 
266 /*!
267   @brief 対応する C++ 関数 @ref nn::os::Mutex::Finalize を参照してください。
268 */
269 NN_EXTERN_C void nnosMutexFinalize(nnosMutex* this_);
270 
271 /*!
272   @}
273 
274   @}
275 */
276 
277 #endif /* NN_OS_IPC_OS_MUTEX_H_ */
278