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