1 /*---------------------------------------------------------------------------*
2 Project: Horizon
3 File: os_Thread.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 Thread に関する API の宣言
18
19 :include nn/os.h
20 */
21
22 #ifndef NN_OS_OS_THREAD_H_
23 #define NN_OS_OS_THREAD_H_
24
25 #include <nn/types.h>
26 #include <nn/Handle.h>
27 #include <nn/os/os_Synchronization.h>
28 #include <nn/os/os_Result.h>
29 #include <nn/os/os_Tick.h>
30 #include <nn/os/os_SpinWaitSelect.h>
31
32 #include <nn/util/util_Result.h>
33 #include <nn/util/util_TypeTraits.h>
34 #include <nn/err.h>
35
36 /*
37 @def NN_OS_CORE_NO_ALL
38 @brief 対応する C++ 関数を参照してください。@ref nn::os::CORE_NO_ALL
39 */
40 #define NN_OS_CORE_NO_ALL (-1)
41
42 /*
43 @def NN_OS_CORE_NO_USE_PROCESS_VALUE
44 @brief 対応する C++ 関数を参照してください。@ref nn::os::CORE_NO_USE_PROCESS_VALUE
45 */
46 #define NN_OS_CORE_NO_USE_PROCESS_VALUE (-2)
47
48 #define NN_OS_THREAD_PRIORITY_RANGE_SIZE 32
49
50 /*
51 @def NN_OS_LOWEST_THREAD_PRIORITY
52 @brief 対応する C++ 関数を参照してください。@ref nn::os::LOWEST_THREAD_PRIORITY
53 */
54 #define NN_OS_LOWEST_THREAD_PRIORITY (NN_OS_THREAD_PRIORITY_RANGE_SIZE - 1)
55
56 /*
57 @def NN_OS_HIGHEST_THREAD_PRIORITY
58 @brief 対応する C++ 関数を参照してください。@ref nn::os::HIGHEST_THREAD_PRIORITY
59 */
60 #define NN_OS_HIGHEST_THREAD_PRIORITY 0
61
62 /*
63 @def NN_OS_DEFAULT_THREAD_PRIORITY
64 @brief 対応する C++ 関数を参照してください。@ref nn::os::DEFAULT_THREAD_PRIORITY
65 */
66 #define NN_OS_DEFAULT_THREAD_PRIORITY 16
67
68
69
70 #ifdef __cplusplus
71
72 #include <nn/os/os_SvcTypes.autogen.h>
73
74 namespace nn{ namespace os{
75
76 namespace detail {
77 s32 ConvertSvcToLibraryPriority(s32 svc);
78 s32 ConvertLibraryToSvcPriority(s32 lib);
79 }
80
81 /*
82 @brief スレッドの最低優先度を表す定数です。31 です。
83 */
84 const s32 LOWEST_THREAD_PRIORITY = NN_OS_LOWEST_THREAD_PRIORITY;
85
86 /*
87 @brief スレッドの最高優先度を表す定数です。0 です。
88 */
89 const s32 HIGHEST_THREAD_PRIORITY = NN_OS_HIGHEST_THREAD_PRIORITY;
90
91 /*
92 @brief デフォルトのスレッド優先度を表す定数です。16 です。
93 */
94 const s32 DEFAULT_THREAD_PRIORITY = NN_OS_DEFAULT_THREAD_PRIORITY;
95
96 /*
97 @brief スレッドを実行するプロセッサ番号がどれでも良いことを表します。
98 */
99 const s32 CORE_NO_ALL = NN_OS_CORE_NO_ALL;
100
101 /*
102 @brief スレッドの実行をするプロセッサ番号を指定する際、アプリケーションで設定されているデフォルトの番号を使うことを表します。
103 */
104 const s32 CORE_NO_USE_PROCESS_VALUE = NN_OS_CORE_NO_USE_PROCESS_VALUE;
105
106 const s32 THREAD_PRIORITY_RANGE_SIZE = NN_OS_THREAD_PRIORITY_RANGE_SIZE;
107
108 /* @typedef void (*ThreadFunc)(uptr param)
109 @brief スレッドで実行する関数の型を表します。
110 */
111
112 /*!
113 @brief スレッドを表すクラスです。
114
115 Bug: 現在の実装では、優先プロセッサやアフィニティマスクの設定はできません。
116
117 スレッドには優先度があります。
118 スレッド優先度には 0~31 までの整数で指定することができ、0 が一番高い優先度を表します。
119 標準的なスレッドは @ref DEFAULT_THREAD_PRIORITY (16)を指定します。
120
121 スレッドの動作にはスタック領域が必要ですが、
122 スタック領域の管理は、自分で管理することもライブラリに任せることもできます。
123
124 自分でスタック領域を管理する場合は @ref Start 関数にスタック領域を渡し、スレッドを開始します。
125 スタック領域は uptr GetStackBottom() 関数を持つようなクラスのインスタンスを渡します。
126 @ref StackMemoryBlock や @ref StackBuffer はこの条件を満たすクラスのため、直接渡すことができます。
127 自分でスタック領域を管理する場合は、
128 スレッドが終了する前にスタック領域が無効になる(解放されるなど)ことがないように十分に注意してください。
129
130 ライブラリにスタック領域の管理を任せる場合は @ref StartUsingAutoStack 関数にスタックサイズを渡し、スレッドを開始します。
131 ライブラリが確保するスタック領域のサイズは4096byte単位です。
132
133 スレッドオブジェクトが有効になるのは Start 系関数から抜けた後です。
134 スレッドが走り出した時点ではないことに注意してください。
135 オブジェクトが有効かどうかは IsValid 関数で取得することができます。
136
137 @ref Start によって開始された Thread オブジェクトを破棄する前には、
138 必ず明示的に @ref Joinを呼ぶ必要があります。
139 @ref Detach を呼ぶことはできません。
140
141 @ref StartUsingAutoStack によって開始された Thread オブジェクトを破棄する前には、
142 必ず明示的に @ref Join か @ref Detach を呼ぶ必要があります。
143
144 Thread オブジェクトは @ref WaitObject を継承しており、Wait 動作の解放はスレッドの終了を意味します。
145 @ref Join 動作をブロック無しに行う必要がある際は、
146 先に @ref WaitOne や @ref WaitObject::WaitAny などを呼んで Wait 動作の解放を確認しておきます。
147
148 ※現在の実装では優先プロセッサやアフィニティマスクをスレッド開始時以外に指定することはできません。
149
150 スレッドには、優先プロセッサおよびアフィニティマスクを指定することができます。
151 優先プロセッサには、0 ~ (コア数-1) まで指定することができます。
152 また、@ref CORE_NO_ALL を指定すると全てのプロセッサを平等に扱うように優先プロセッサを設定し、
153 全てのプロセッサを含むアフィニティマスクを使用します。
154 @ref CORE_NO_USE_PROCESS_VALUE を指定すると、所属するアプリケーションのデフォルトの値を使用します。
155 優先プロセッサを指定すると、スレッドは指定されたプロセッサ上で優先して動作しますが、
156 必ずしも指定どおりのプロセッサ上で動作するわけではありません。
157
158 アフィニティマスクは、スレッドがどのプロセッサ上で実行されるべきかを指定するものです。
159 bit8の配列で表現し、各要素は下位1ビット目が最初のプロセッサを表します。
160 ビットを1にすることで、対象のプロセッサで走行する許可を与えます。
161 必ず、どれかのプロセッサをしてする必要があり、全てのビット0にしたマスクを指定することはできません。
162
163 スレッドの開始時には優先プロセッサを指定することができますが、
164 ここでプロセッサ番号を指定した場合、
165 スレッド開始時のアフィニティマスクはそのプロセッサ番号のみが指定されたことになります。
166
167 Thread オブジェクトは 32個まで作成することが出来ます。
168 また、API によってはこの制限にカウントされるリソースを消費する場合がありますので、ご注意ください。
169 */
170
171 class Thread : public WaitObject
172 {
173 public:
174 class AutoStackManager
175 {
176 public:
177 virtual void* Construct(size_t stackSize) = 0;
178 virtual void Destruct(void* pStackBottom, bool isError) = 0;
179 };
180
181 public:
182
183 /*!
184 @brief コンストラクタです。
185
186 スレッドを開始するには、@ref Start または @ref TryStart を使います。
187 */
Thread()188 Thread() : m_CanFinalize(true) {}
189
190 /*!
191 @brief デストラクタ
192
193 スレッドオブジェクトを破棄します。
194 破棄時の注意は @ref Finalize を参照してください。
195 */
196 ~Thread();
197
198 /*!
199 @brief スレッドを初期化し実行します。
200
201 ユーザが用意したスタックを使ってスレッドを初期化し、スレッドを開始します。
202 スレッドの終了が確認されるまで、スタック領域は維持されている必要があります。
203
204 @param[in] f 実行を開始する関数へのポインタ
205 @param[in] stack スタック領域を表すオブジェクト
206 @param[in] priority スレッドの優先度
207 @param[in] coreNo スレッドの優先プロセッサ
208
209 @tparam Stack スタックを表す型
210
211 @return 無し。
212
213 :overload partialtemplate
214 */
215 template <typename Stack>
216 void Start(void (*f)(), Stack& stack, s32 priority = DEFAULT_THREAD_PRIORITY, s32 coreNo = CORE_NO_USE_PROCESS_VALUE);
217
218 /*!
219 @brief スレッドを初期化し実行します。
220
221 ユーザが用意したスタックを使ってスレッドを初期化し、スレッドを開始します。
222 スレッドの終了が確認されるまで、スタック領域は維持されている必要があります。
223
224 @param[in] f 実行を開始する関数へのポインタ
225 @param[in] param 実行を開始する関数へ渡す引数
226 @param[in] stack スタック領域を表すオブジェクト
227 @param[in] priority スレッドの優先度
228 @param[in] coreNo スレッドの優先プロセッサ
229
230 @tparam T 実行する関数の引数の型(コピーできる型である必要があります)
231 @tparam U T に変換できる型
232 @tparam Stack スタックを表す型
233
234 @return 無し。
235
236 :overload fulltemplate
237 */
238 template <typename T, typename U, typename Stack>
239 void Start(void (*f)(T), U param, Stack& stack, s32 priority = DEFAULT_THREAD_PRIORITY, s32 coreNo = CORE_NO_USE_PROCESS_VALUE);
240 template <typename T, typename Stack>
241 void Start(void (*f)(const T*), const T& param, Stack& stack, s32 priority = DEFAULT_THREAD_PRIORITY, s32 coreNo = CORE_NO_USE_PROCESS_VALUE);
242 template <typename T, typename Stack>
243 void Start(void (*f)(const T&), const T& param, Stack& stack, s32 priority = DEFAULT_THREAD_PRIORITY, s32 coreNo = CORE_NO_USE_PROCESS_VALUE);
244
245 /*!
246 @brief スレッドの初期化と実行を試みます。
247
248 スレッドを初期化し実行をしますが、
249 リソース不足などが原因で失敗した場合にエラーを返します。
250
251 @sa Start
252
253 @param[in] f 実行を開始する関数へのポインタ
254 @param[in] stack スタック領域を表すオブジェクト
255 @param[in] priority スレッドの優先度
256 @param[in] coreNo スレッドの優先プロセッサおよびアフィニティマスクの指定
257
258 @tparam Stack スタックを表す型
259
260 @return 関数の実行結果を返します。
261 */
262 template <typename Stack>
263 nn::Result TryStart(void (*f)(), Stack& stack, s32 priority = DEFAULT_THREAD_PRIORITY, s32 coreNo = CORE_NO_USE_PROCESS_VALUE);
264
265 /*!
266 @brief スレッドの初期化と実行を試みます。
267
268 スレッドを初期化し実行をしますが、
269 リソース不足などが原因で失敗した場合にエラーを返します。
270
271 @sa Start
272
273 @param[in] f 実行を開始する関数へのポインタ
274 @param[in] param 実行を開始する関数へ渡す引数
275 @param[in] stack スタック領域を表すオブジェクト
276 @param[in] priority スレッドの優先度
277 @param[in] coreNo スレッドの優先プロセッサおよびアフィニティマスクの指定
278
279 @tparam T 実行する関数の引数の型(コピーできる型である必要があります)
280 @tparam U T に変換できる型
281 @tparam Stack スタックを表す型
282
283 @return 関数の実行結果を返します。
284 */
285 template <typename T, typename U, typename Stack>
286 nn::Result TryStart(void (*f)(T), U param, Stack& stack, s32 priority = DEFAULT_THREAD_PRIORITY, s32 coreNo = CORE_NO_USE_PROCESS_VALUE);
287
288 /*!
289 @brief スレッドを初期化し実行します。
290
291 スタックは指定されたサイズ以上のサイズに自動的に確保され、
292 スレッド終了時に自動的に破棄されます。
293
294 @param[in] f 実行を開始する関数へのポインタ
295 @param[in] stackSize スタックサイズ
296 @param[in] priority スレッドの優先度
297 @param[in] coreNo スレッドの優先プロセッサおよびアフィニティマスクの指定
298
299 @return 無し。
300 */
301 void StartUsingAutoStack(void (*f)(), size_t stackSize, s32 priority = DEFAULT_THREAD_PRIORITY, s32 coreNo = CORE_NO_USE_PROCESS_VALUE);
302
303 /*!
304 @brief スレッドを初期化し実行します。
305
306 スタックは指定されたサイズ以上のサイズに自動的に確保され、
307 スレッド終了時に自動的に破棄されます。
308
309 @param[in] f 実行を開始する関数へのポインタ
310 @param[in] param 実行を開始する関数へ渡す引数
311 @param[in] stackSize スタックサイズ
312 @param[in] priority スレッドの優先度
313 @param[in] coreNo スレッドの優先プロセッサおよびアフィニティマスクの指定
314
315 @tparam T 実行する関数の引数の型(コピーできる型である必要があります)
316 @tparam U T に変換できる型
317
318 @return 無し。
319 */
320 template <typename T, typename U>
321 void StartUsingAutoStack(void (*f)(T), U param, size_t stackSize, s32 priority = DEFAULT_THREAD_PRIORITY, s32 coreNo = CORE_NO_USE_PROCESS_VALUE);
322
323 /*!
324 :overload notemplate
325
326 @brief スレッドの初期化と実行を試みます。
327
328 スレッドを初期化し実行をしますが、
329 リソース不足などが原因で失敗した場合にエラーを返します。
330
331 @sa StartUsingAutoStack
332
333 @param[in] f 実行を開始する関数へのポインタ
334 @param[in] stackSize スタックサイズ
335 @param[in] priority スレッドの優先度
336 @param[in] coreNo スレッドの優先プロセッサおよびアフィニティマスクの指定
337
338 @return 関数の実行結果を返します。
339 */
340 nn::Result TryStartUsingAutoStack(void (*f)(), size_t stackSize, s32 priority = DEFAULT_THREAD_PRIORITY, s32 coreNo = CORE_NO_USE_PROCESS_VALUE);
341
342 /*!
343 :overload template
344
345 @brief スレッドの初期化と実行を試みます。
346
347 スレッドを初期化し実行をしますが、
348 リソース不足などが原因で失敗した場合にエラーを返します。
349
350 @sa StartUsingAutoStack
351
352 @param[in] f 実行を開始する関数へのポインタ
353 @param[in] param 実行を開始する関数へ渡す引数
354 @param[in] stackSize スタックサイズ
355 @param[in] priority スレッドの優先度
356 @param[in] coreNo スレッドの優先プロセッサおよびアフィニティマスクの指定
357
358 @tparam T 実行する関数の引数の型(コピーできる型である必要があります)
359 @tparam U T に変換できる型
360
361 @return 関数の実行結果を返します。
362 */
363 template <typename T, typename U>
364 nn::Result TryStartUsingAutoStack(void (*f)(T), U param, size_t stackSize, s32 priority = DEFAULT_THREAD_PRIORITY, s32 coreNo = CORE_NO_USE_PROCESS_VALUE);
365
366 /*!
367 @brief スレッドオブジェクトを破棄します。
368
369 スレッドを破棄する前に、必ず @ref Detach か @ref Join を呼んでいる必要があります。
370 呼んでいなかった場合、PANIC します。
371
372
373 @return 無し。
374 */
375 void Finalize();
376
377 /*!
378 @brief スレッドの終了を待ちます。
379
380 この関数はスレッドの終了を無条件で待ちます。
381 タイムアウトつきで待つなど、細かい制御が必要な場合は、
382 @ref WaitObject にある関数を使ってスレッドの終了を待った後、
383 この関数を呼び出すようにしてください。
384
385 @return 無し。
386 */
387 void Join();
388
389 /*!
390 @brief スレッドのデタッチを行います。
391
392 このオブジェクトがスレッドに対して今後一切の操作を行わないことを宣言します。
393 このオブジェクトに対しては、@ref Finalize とデストラクタ以外を呼ぶことができなくなります。
394
395 @ref Start を使って開始したスレッドに対してこの関数を呼ぶことはできません。
396
397 @return 無し。
398 */
399 void Detach();
400
401 /*!
402 @brief スレッドが生きているかどうかを取得します。
403
404 @return スレッドが生きているならtrue、そうでないならfalseを返します。
405 */
IsAlive()406 bool IsAlive() const { return !const_cast<Thread*>(this)->WaitOne(0); }
407
408 /*!
409 @brief カレントスレッドを指定時間だけ休止状態にします。
410
411 @param[in] span 休止する時間
412
413 @return 無し。
414 */
415 static void Sleep(nn::fnd::TimeSpan span);
416
417 /*!
418 @brief カレントスレッドと同じ優先度のスレッドに実行機会を与えます。
419
420 スケジューラは優先度スケジューリングを行いますので
421 常に実行可能なスレッドの中で最高優先度のスレッドが動作しています。
422 最高優先度のスレッドが複数存在する場合、
423 スケジューラはカレントスレッドを継続して動作させようと試みますので
424 通常はカレントスレッドが実行可能でなくならない限り
425 同優先度の他のスレッドは実行されません。
426
427 この関数はカレントスレッドを実行可能としたまま
428 同優先度の他のスレッドに実行権を明け渡します。
429 同優先度のスレッドが存在しない場合は何も起りません。
430
431 @return 無し。
432 */
433 static void Yield();
434
435 // スレッドの状態の取得・設定
436 // 非 static メンバ関数は、インスタンスに対して作用する。
437 // "Current" を含む名前の static メンバ関数は、カレントスレッドに対して作用する。
438 // "Default" を含む名前の static メンバ関数は、CreateThread で coreNo = CORE_NO_USE_PROCESS_VALUE のとき、
439 // 作成されるスレッドの属性のデフォルト値に作用する。
440
441 // コンテキストの取得
442 // TODO: 未実装
443 // void GetContext(nn::os::ThreadContext* pContext) const;
444 // static void GetCurrentContext(nn::os::ThreadContext* pContext);
445
446 /*!
447 @brief インスタンスのスレッドのスレッド ID を取得します。
448
449 @return インスタンスのスレッドのスレッド ID を返します。
450 */
451 bit32 GetId() const;
452
453 /*!
454 @brief カレントスレッドのスレッド ID を取得します。
455
456 @return カレントスレッドのスレッド ID を返します。
457 */
458 static bit32 GetCurrentId();
459
460 /*!
461 @brief インスタンスのスレッドの優先度を取得します。
462
463 @return インスタンスのスレッドの優先度を返します。
464 */
465 s32 GetPriority() const;
466
467 /*!
468 @brief カレントスレッドの優先度を取得します。
469
470 @return カレントスレッドの優先度を返します。
471 */
472 static s32 GetCurrentPriority();
473
474 /*!
475 @brief インスタンスのスレッドの優先度を設定します。
476
477 @param[in] priority スレッドの優先度を指定します。
478
479 @return 無し。
480 */
481 void ChangePriority(s32 priority);
482
483 /*!
484 @brief カレントスレッドの優先度を設定します。
485
486 @param[in] priority スレッドの優先度を指定します。
487
488 @return 無し。
489 */
490 static void ChangeCurrentPriority(s32 priority);
491
492 // スレッド Affinity の設定・取得
493 // AffinityMask は構造体やクラスで包む?
494 /*!
495 @brief インスタンスのアフィニティマスクを取得します。
496
497 @param[out] pAffinityMask 取得したアフィニティマスクを格納するバッファを指定します。
498 @param[in] numProcessor pAffinityMask が指すバッファのビット数を指定します。
499
500 @return 無し。
501
502 Bug: この関数は未実装です。この関数を使わないでください。
503 */
504 void GetAffinityMask(bit8* pAffinityMask, s32 numProcessor) const;
505
506 /*!
507 @brief カレントスレッドのアフィニティマスクを取得します。
508
509 @param[out] pAffinityMask 取得したアフィニティマスクを格納するバッファを指定します。
510 @param[in] numProcessor pAffinityMask が指すバッファのビット数を指定します。
511
512 @return 無し。
513
514 Bug: この関数は未実装です。この関数を使わないでください。
515 */
516 static void GetCurrentAffinityMask(bit8* pAffinityMask, s32 numProcessor);
517
518 /*!
519 @brief coreNo = CORE_NO_USE_PROCESS_VALUE で作成されるスレッドのアフィニティマスクを取得します。
520
521 @param[out] pAffinityMask 取得したアフィニティマスクを格納するバッファを指定します。
522 @param[in] numProcessor pAffinityMask が指すバッファのビット数を指定します。
523
524 @return 無し。
525
526 Bug: この関数は未実装です。この関数を使わないでください。
527 */
528 static void GetDefaultAffinityMask(bit8* pAffinityMask, s32 numProcessor);
529
530 /*!
531 @brief インスタンスのアフィニティマスクを設定します。
532
533 @param[in] pAffinityMask 新しく設定するアフィニティマスクを指定します。
534 @param[in] numProcessor pAffinityMask が指すビット列の有効なビット数を指定します。
535
536 @return 無し。
537
538 Bug: この関数は未実装です。この関数を使わないでください。
539 */
540 void ChangeAffinityMask(const bit8* pAffinityMask, s32 numProcessor);
541
542 /*!
543 @brief カレントスレッドのアフィニティマスクを設定します。
544
545 @param[in] pAffinityMask 新しく設定するアフィニティマスクを指定します。
546 @param[in] numProcessor pAffinityMask が指すビット列の有効なビット数を指定します。
547
548 @return 無し。
549
550 Bug: この関数は未実装です。この関数を使わないでください。
551 */
552 static void ChangeCurrentAffinityMask(const bit8* pAffinityMask, s32 numProcessor);
553
554 /*!
555 @brief coreNo = CORE_NO_USE_PROCESS_VALUE で作成されるスレッドのアフィニティマスクを設定します。
556
557 @param[in] pAffinityMask 新しく設定するアフィニティマスクを指定します。
558 @param[in] numProcessor pAffinityMask が指すビット列の有効なビット数を指定します。
559
560 @return 無し。
561
562 Bug: この関数は未実装です。この関数を使わないでください。
563 */
564 static void SetDefaultAffinityMask(const bit8* pAffinityMask, s32 numProcessor);
565
566 // IdealProcessor の取得・設定
567 /*!
568 @brief インスタンスの優先プロセッサ番号を取得します。
569
570 @return 優先プロセッサ番号を返します。
571
572 Bug: この関数は未実装です。この関数を使わないでください。
573 */
574 s32 GetIdealProcessor() const;
575
576 /*!
577 @brief カレントスレッドの優先プロセッサ番号を取得します。
578
579 @return 優先プロセッサ番号を返します。
580
581 Bug: この関数は未実装です。この関数を使わないでください。
582 */
583 static s32 GetCurrentIdealProcessor();
584
585 /*!
586 @brief coreNo = CORE_NO_USE_PROCESS_VALUE で作成されるスレッドの優先プロセッサ番号を取得します。
587
588 @return 優先プロセッサ番号を返します。
589
590 Bug: この関数は未実装です。この関数を使わないでください。
591 */
592 static s32 GetDefaultIdealProcessor();
593
594 /*!
595 @brief インスタンスの優先プロセッサ番号を設定します。
596
597 @param[in] coreNo 優先プロセッサ番号
598
599 @return 無し。
600
601 Bug: この関数は未実装です。この関数を使わないでください。
602 */
603 void ChangeIdealProcessor(s32 coreNo);
604
605 /*!
606 @brief カレントスレッドの優先プロセッサ番号を設定します。
607
608 @param[in] coreNo 優先プロセッサ番号
609
610 @return 無し。
611
612 Bug: この関数は未実装です。この関数を使わないでください。
613 */
614 static void ChangeCurrentIdealProcessor(s32 coreNo);
615
616 /*!
617 @brief coreNo = CORE_NO_USE_PROCESS_VALUE で作成されるスレッドの IdealProcessor を設定します。
618
619 @param[in] coreNo 優先プロセッサ番号
620
621 @return 無し。
622
623 Bug: この関数は未実装です。この関数を使わないでください。
624 */
625 static void SetDefaultIdealProcessor(s32 coreNo);
626
627 /*!
628 @brief カレントスレッドの動作しているプロセッサ番号の取得
629
630 @return カレントスレッドの動作しているプロセッサ番号を返します。
631
632 Bug: この関数は未実装です。この関数を使わないでください。
633 */
634 static s32 GetCurrentProcessorNumber();
635
636 /*!
637 @brief メインスレッドを表すオブジェクトを取得します。
638 @return メインスレッドを表すオブジェクト。
639 */
GetMainThread()640 static Thread& GetMainThread() { return s_MainThread; }
641
642 static void SetAutoStackManager(AutoStackManager* pManager);
643
644
645 private:
646 struct InitializeAsCurrentTag {};
647 struct TypeInfo;
648 struct FunctionInfo;
649
650 private:
651 bool m_CanFinalize;
652 bool m_UsingAutoStack;
653 NN_PADDING2;
654
655 static Thread s_MainThread;
656 static AutoStackManager* s_pAutoStackManager;
657
658 private:
659 Thread(const InitializeAsCurrentTag&);
660 void FinalizeImpl();
661
662 Result TryInitializeAndStartImpl(const TypeInfo& typeInfo, ThreadFunc f, const void* p, uptr stackBottom, s32 priority, s32 coreNo, bool isAutoStack);
663 Result TryInitializeAndStartImplUsingAutoStack(const TypeInfo& typeInfo, ThreadFunc f, const void* p, size_t stackSize, s32 priority, s32 coreNo);
664
665 private:
666 static void OnThreadStart();
667 static void OnThreadExit();
668
669 static void ThreadStart(uptr);
670 static void ThreadStartUsingAutoStack(uptr);
671 static void NoParameterFunc(void (*)());
672 static void SleepImpl(nn::fnd::TimeSpan span);
673
674 static void CallDestructorAndExit(void* pStackBottom);
675 };
676
677 /*!
678 @brief スレッド用のスタックを、static 領域や構造体の中などに配置したい場合に使うクラステンプレートです。
679
680 @ref GetStackBottom 関数の戻り値を @ref Thread::Start 関数などに渡すことで、
681 スレッドのスタックとしてこの領域を使用することができます。
682
683 @tparam Size スタックのサイズを表します。
684 */
685 template <size_t Size>
686 class StackBuffer
687 {
688 private:
689 typename nn::util::aligned_storage<Size, 8>::type m_Buffer;
690 public:
691
692 /*!
693 @brief スタックの底アドレスを返します。
694
695 @return スタックの底アドレス。
696 */
GetStackBottom()697 uptr GetStackBottom() const { return reinterpret_cast<uptr>(this + 1); }
698
699 void MarkCanary(bit32 value = 0xDEADBEEF) { reinterpret_cast<bit32*>(this)[0] = value; }
700 bool CheckCanary(bit32 value = 0xDEADBEEF) const { return reinterpret_cast<const bit32*>(this)[0] == value; }
701 };
702
703 }}
704
705 // インライン実装
706
707 #ifdef NN_SYSTEM_PROCESS
708
709 #include <new>
710
711 #include <nn/dbg/dbg_Logger.h>
712
713 namespace nn { namespace os {
714
715 struct Thread::TypeInfo
716 {
717 private:
718
719 template <typename T, typename U>
CopyTypeInfo720 static void Copy(const void* src, void* dst)
721 {
722 new (dst) T(*reinterpret_cast<const U*>(src));
723 }
724 template <typename T>
CopyTypeInfo725 static void Copy(const void* src, void* dst)
726 {
727 new (dst) T(*reinterpret_cast<const T*>(src));
728 }
729
730 template <typename T>
DestroyTypeInfo731 static void Destroy(void* p)
732 {
733 reinterpret_cast<T*>(p)->~T();
734 }
735
736 template <typename T>
InvokeTypeInfo737 static void Invoke(ThreadFunc f, const void* p)
738 {
739 (*reinterpret_cast<void (*)(T)>(f))(*reinterpret_cast<const T*>(p));
740 }
741 template <typename T>
Invoke2TypeInfo742 static void Invoke2(ThreadFunc f, const void* p)
743 {
744 (*reinterpret_cast<void (*)(const T*)>(f))(reinterpret_cast<const T*>(p));
745 }
746
747 public:
748
749 size_t size;
750 void (*copy)(const void* src, void* dst);
751 void (*destroy)(void* p);
752 void (*invoke)(ThreadFunc f, const void* p);
753
754 template <typename T, typename U>
755 void SetData(typename nn::util::enable_if<nn::util::is_convertible<U, T>::value>::type* = 0)
756 {
757 this->size = sizeof(T);
758 this->copy = &(Copy<T, U>);
759 this->destroy = &(Destroy<T>);
760 this->invoke = &(Invoke<T>);
761 }
762 template <typename T>
SetDataTypeInfo763 void SetData()
764 {
765 this->size = sizeof(T);
766 this->copy = &(Copy<T>);
767 this->destroy = &(Destroy<T>);
768 this->invoke = &(Invoke2<T>);
769 }
770
771 };
772
773 template <typename T, typename U, typename Stack>
Start(void (* f)(T),U param,Stack & stack,s32 priority,s32 coreNo)774 inline void Thread::Start(void (*f)(T), U param, Stack& stack, s32 priority, s32 coreNo)
775 {
776 TypeInfo info;
777 info.SetData<T, U>();
778 NN_ERR_THROW_FATAL(TryInitializeAndStartImpl(info, reinterpret_cast<ThreadFunc>(f), ¶m, stack.GetStackBottom(), priority, coreNo, false));
779 }
780
781 template <typename T, typename Stack>
Start(void (* f)(const T *),const T & param,Stack & stack,s32 priority,s32 coreNo)782 inline void Thread::Start(void (*f)(const T*), const T& param, Stack& stack, s32 priority, s32 coreNo)
783 {
784 TypeInfo info;
785 info.SetData<T>();
786 NN_ERR_THROW_FATAL(TryInitializeAndStartImpl(info, reinterpret_cast<ThreadFunc>(f), ¶m, stack.GetStackBottom(), priority, coreNo, false));
787 }
788
789 template <typename Stack>
Start(void (* f)(),Stack & stack,s32 priority,s32 coreNo)790 inline void Thread::Start(void (*f)(), Stack& stack, s32 priority, s32 coreNo)
791 {
792 Start(NoParameterFunc, f, stack, priority, coreNo);
793 }
794
795 template <typename T, typename U, typename Stack>
TryStart(void (* f)(T),U param,Stack & stack,s32 priority,s32 coreNo)796 inline nn::Result Thread::TryStart(void (*f)(T), U param, Stack& stack, s32 priority, s32 coreNo)
797 {
798 TypeInfo info;
799 info.SetData<T, U>();
800 Result result = TryInitializeAndStartImpl(info, reinterpret_cast<ThreadFunc>(f), ¶m, stack.GetStackBottom(), priority, coreNo, false);
801 if (result.GetSummary() == Result::SUMMARY_OUT_OF_RESOURCE)
802 {
803 return result;
804 }
805 NN_ERR_THROW_FATAL(result);
806 return result;
807 }
808
809 template <typename Stack>
TryStart(void (* f)(),Stack & stack,s32 priority,s32 coreNo)810 inline nn::Result Thread::TryStart(void (*f)(), Stack& stack, s32 priority, s32 coreNo)
811 {
812 return TryStart(NoParameterFunc, f, stack, priority, coreNo);
813 }
814
815 template <typename T, typename U>
StartUsingAutoStack(void (* f)(T),U param,size_t stackSize,s32 priority,s32 coreNo)816 inline void Thread::StartUsingAutoStack(void (*f)(T), U param, size_t stackSize, s32 priority, s32 coreNo)
817 {
818 TypeInfo info;
819 info.SetData<T, U>();
820 NN_ERR_THROW_FATAL(TryInitializeAndStartImplUsingAutoStack(info, reinterpret_cast<ThreadFunc>(f), ¶m, stackSize, priority, coreNo));
821 }
822
StartUsingAutoStack(void (* f)(),size_t stackSize,s32 priority,s32 coreNo)823 inline void Thread::StartUsingAutoStack(void (*f)(), size_t stackSize, s32 priority, s32 coreNo)
824 {
825 StartUsingAutoStack(NoParameterFunc, f, stackSize, priority, coreNo);
826 }
827
828 template <typename T, typename U>
TryStartUsingAutoStack(void (* f)(T),U param,size_t stackSize,s32 priority,s32 coreNo)829 inline nn::Result Thread::TryStartUsingAutoStack(void (*f)(T), U param, size_t stackSize, s32 priority, s32 coreNo)
830 {
831 TypeInfo info;
832 info.SetData<T, U>();
833 Result result = TryInitializeAndStartImplUsingAutoStack(info, reinterpret_cast<ThreadFunc>(f), ¶m, stackSize, priority, coreNo);
834 if (result.GetSummary() == Result::SUMMARY_OUT_OF_RESOURCE)
835 {
836 return result;
837 }
838 NN_ERR_THROW_FATAL(result);
839 return result;
840 }
841
TryStartUsingAutoStack(void (* f)(),size_t stackSize,s32 priority,s32 coreNo)842 inline nn::Result Thread::TryStartUsingAutoStack(void (*f)(), size_t stackSize, s32 priority, s32 coreNo)
843 {
844 return TryStartUsingAutoStack(NoParameterFunc, f, stackSize, priority, coreNo);
845 }
846
FinalizeImpl()847 inline void Thread::FinalizeImpl()
848 {
849 if (!m_CanFinalize)
850 {
851 NN_TPANIC_("Thread is not Joined or Detached either.");
852 }
853 }
854
Finalize()855 inline void Thread::Finalize()
856 {
857 FinalizeImpl();
858 this->WaitObject::Finalize();
859 }
860
~Thread()861 inline Thread::~Thread()
862 {
863 FinalizeImpl();
864 }
865
Join()866 inline void Thread::Join()
867 {
868 this->WaitOne();
869 this->m_CanFinalize = true;
870 }
871
Detach()872 inline void Thread::Detach()
873 {
874 NN_TASSERT_(m_UsingAutoStack);
875 this->m_CanFinalize = true;
876 Finalize();
877 }
878
Sleep(nn::fnd::TimeSpan span)879 inline void Thread::Sleep(nn::fnd::TimeSpan span)
880 {
881 SleepImpl(span);
882 }
883
Yield()884 inline void Thread::Yield()
885 {
886 nn::svc::SleepThread(0);
887 }
888
GetId()889 inline bit32 Thread::GetId() const
890 {
891 bit32 ret;
892 NN_ERR_THROW_FATAL(nn::svc::GetThreadId(&ret, GetHandle()));
893 return ret;
894 }
895
GetCurrentId()896 inline bit32 Thread::GetCurrentId()
897 {
898 bit32 ret;
899 NN_ERR_THROW_FATAL(nn::svc::GetThreadId(&ret, PSEUDO_HANDLE_CURRENT_THREAD));
900 return ret;
901 }
902
GetPriority()903 inline s32 Thread::GetPriority() const
904 {
905 s32 ret;
906 NN_ERR_THROW_FATAL(nn::svc::GetThreadPriority(&ret, GetHandle()));
907 return os::detail::ConvertSvcToLibraryPriority(ret);
908 }
909
GetCurrentPriority()910 inline s32 Thread::GetCurrentPriority()
911 {
912 s32 ret;
913 NN_ERR_THROW_FATAL(nn::svc::GetThreadPriority(&ret, PSEUDO_HANDLE_CURRENT_THREAD));
914 return os::detail::ConvertSvcToLibraryPriority(ret);
915 }
916
ChangePriority(s32 priority)917 inline void Thread::ChangePriority(s32 priority)
918 {
919 NN_ERR_THROW_FATAL(nn::svc::SetThreadPriority(GetHandle(), os::detail::ConvertLibraryToSvcPriority(priority)));
920 }
921
ChangeCurrentPriority(s32 priority)922 inline void Thread::ChangeCurrentPriority(s32 priority)
923 {
924 NN_ERR_THROW_FATAL(nn::svc::SetThreadPriority(PSEUDO_HANDLE_CURRENT_THREAD, os::detail::ConvertLibraryToSvcPriority(priority)));
925 }
926
GetAffinityMask(bit8 * pAffinityMask,s32 numProcessor)927 inline void Thread::GetAffinityMask(bit8* pAffinityMask, s32 numProcessor) const
928 {
929 NN_ERR_THROW_FATAL(nn::svc::GetThreadAffinityMask(pAffinityMask, GetHandle(), numProcessor));
930 }
931
GetCurrentAffinityMask(bit8 * pAffinityMask,s32 numProcessor)932 inline void Thread::GetCurrentAffinityMask(bit8* pAffinityMask, s32 numProcessor)
933 {
934 NN_ERR_THROW_FATAL(nn::svc::GetThreadAffinityMask(pAffinityMask, PSEUDO_HANDLE_CURRENT_THREAD, numProcessor));
935 }
936
GetDefaultAffinityMask(bit8 * pAffinityMask,s32 numProcessor)937 inline void Thread::GetDefaultAffinityMask(bit8* pAffinityMask, s32 numProcessor)
938 {
939 NN_ERR_THROW_FATAL(nn::svc::GetProcessAffinityMask(pAffinityMask, PSEUDO_HANDLE_CURRENT_PROCESS, numProcessor));
940 }
941
ChangeAffinityMask(const bit8 * pAffinityMask,s32 numProcessor)942 inline void Thread::ChangeAffinityMask(const bit8* pAffinityMask, s32 numProcessor)
943 {
944 NN_ERR_THROW_FATAL(nn::svc::SetThreadAffinityMask(GetHandle(), pAffinityMask, numProcessor));
945 }
946
ChangeCurrentAffinityMask(const bit8 * pAffinityMask,s32 numProcessor)947 inline void Thread::ChangeCurrentAffinityMask(const bit8* pAffinityMask, s32 numProcessor)
948 {
949 NN_ERR_THROW_FATAL(nn::svc::SetThreadAffinityMask(PSEUDO_HANDLE_CURRENT_THREAD, pAffinityMask, numProcessor));
950 }
951
SetDefaultAffinityMask(const bit8 * pAffinityMask,s32 numProcessor)952 inline void Thread::SetDefaultAffinityMask(const bit8* pAffinityMask, s32 numProcessor)
953 {
954 NN_ERR_THROW_FATAL(nn::svc::SetProcessAffinityMask(PSEUDO_HANDLE_CURRENT_PROCESS, pAffinityMask, numProcessor));
955 }
956
GetIdealProcessor()957 inline s32 Thread::GetIdealProcessor() const
958 {
959 s32 ret;
960 NN_ERR_THROW_FATAL(nn::svc::GetThreadIdealProcessor(&ret, GetHandle()));
961 return ret;
962 }
963
GetCurrentIdealProcessor()964 inline s32 Thread::GetCurrentIdealProcessor()
965 {
966 s32 ret;
967 NN_ERR_THROW_FATAL(nn::svc::GetThreadIdealProcessor(&ret, PSEUDO_HANDLE_CURRENT_THREAD));
968 return ret;
969 }
970
GetDefaultIdealProcessor()971 inline s32 Thread::GetDefaultIdealProcessor()
972 {
973 s32 ret;
974 NN_ERR_THROW_FATAL(nn::svc::GetProcessIdealProcessor(&ret, PSEUDO_HANDLE_CURRENT_PROCESS));
975 return ret;
976 }
977
ChangeIdealProcessor(s32 coreNo)978 inline void Thread::ChangeIdealProcessor(s32 coreNo)
979 {
980 NN_ERR_THROW_FATAL(nn::svc::SetThreadIdealProcessor(GetHandle(), coreNo));
981 }
982
ChangeCurrentIdealProcessor(s32 coreNo)983 inline void Thread::ChangeCurrentIdealProcessor(s32 coreNo)
984 {
985 NN_ERR_THROW_FATAL(nn::svc::SetThreadIdealProcessor(PSEUDO_HANDLE_CURRENT_THREAD, coreNo));
986 }
987
SetDefaultIdealProcessor(s32 coreNo)988 inline void Thread::SetDefaultIdealProcessor(s32 coreNo)
989 {
990 NN_ERR_THROW_FATAL(nn::svc::SetProcessIdealProcessor(PSEUDO_HANDLE_CURRENT_PROCESS, coreNo));
991 }
992
GetCurrentProcessorNumber()993 inline s32 Thread::GetCurrentProcessorNumber()
994 {
995 return nn::svc::GetCurrentProcessorNumber();
996 }
997
998
999 /*!
1000 @}
1001 */
1002 /*!
1003 @}
1004 */
1005
1006 }} // namespace nn::os
1007
1008 #endif // NN_SYSTEM_PROCESS
1009
1010 #endif // __cplusplus
1011
1012 // 以下、C 用宣言
1013
1014 #include <nn/util/detail/util_CLibImpl.h>
1015
1016
1017 /*!
1018 @addtogroup nn_os os
1019 @{
1020
1021 @defgroup nn_os_Thread_c Thread (C)
1022
1023 @brief @ref nn::os::Thread の C インタフェースモジュールです。
1024
1025 @{
1026 */
1027
1028 /*!
1029 @struct nnosThread
1030 @brief スレッドを表す C の構造体です。
1031
1032 @brief 対応するクラス @ref nn::os::Thread を参照してください。
1033 */
1034 NN_UTIL_DETAIL_CLIBIMPL_DEFINE_BUFFER_CLASS(nnosThread, nn::os::Thread, 8, u32);
1035
1036 /*!
1037 @brief 対応する C++ 関数 @ref nn::os::Thread::Start を参照してください。
1038 */
1039 NN_EXTERN_C void nnosThreadInitializeAndStart(nnosThread* this_, void (*f)(uptr), uptr param, uptr stackBottom, s32 priority, s32 coreNo);
1040
1041 /*!
1042 @brief 対応する C++ 関数 @ref nn::os::Thread::TryStart を参照してください。
1043 */
1044 NN_EXTERN_C bool nnosThreadTryInitializeAndStart(nnosThread* this_, void (*f)(uptr), uptr param, uptr stackBottom, s32 priority, s32 coreNo);
1045
1046 /*!
1047 @brief 対応する C++ 関数 @ref nn::os::Thread::Finalize を参照してください。
1048 */
1049 NN_EXTERN_C void nnosThreadFinalize(nnosThread* this_);
1050
1051 /*!
1052 @brief 対応する C++ 関数 @ref nn::os::Thread::Join を参照してください。
1053 */
1054 NN_EXTERN_C void nnosThreadJoin(nnosThread* this_);
1055
1056 /*!
1057 @brief 対応する C++ 関数 @ref nn::os::Thread::Sleep を参照してください。
1058 */
1059 NN_EXTERN_C void nnosThreadSleep(s64 nanoSeconds);
1060
1061 /*!
1062 @brief 対応する C++ 関数 @ref nn::os::Thread::Yield を参照してください。
1063 */
1064 NN_EXTERN_C void nnosThreadYield(void);
1065
1066 /*!
1067 @brief 対応する C++ 関数 @ref nn::os::Thread::GetCurrentId を参照してください。
1068 */
1069 NN_EXTERN_C bit32 nnosThreadGetCurrentId(void);
1070
1071 /*!
1072 @brief 対応する C++ 関数 @ref nn::os::Thread::GetPriority を参照してください。
1073 */
1074 NN_EXTERN_C s32 nnosThreadGetPriority(const nnosThread* this_);
1075
1076 /*!
1077 @brief 対応する C++ 関数 @ref nn::os::Thread::GetCurrentPriority を参照してください。
1078 */
1079 NN_EXTERN_C s32 nnosThreadGetCurrentPriority(void);
1080
1081 /*!
1082 @brief 対応する C++ 関数 @ref nn::os::Thread::ChangePriority を参照してください。
1083 */
1084 NN_EXTERN_C void nnosThreadChangePriority(nnosThread* this_, s32 priority);
1085
1086 /*!
1087 @brief 対応する C++ 関数 @ref nn::os::Thread::ChangeCurrentPriority を参照してください。
1088 */
1089 NN_EXTERN_C void nnosThreadChangeCurrentPriority(s32 priority);
1090
1091 // スレッド Affinity の設定・取得
1092
1093 /*!
1094 @brief 対応する C++ 関数 @ref nn::os::Thread::GetAffinityMask を参照してください。
1095 */
1096 NN_EXTERN_C void nnosThreadGetAffinityMask(const nnosThread* this_, bit8* pAffinityMask, s32 numProcessor);
1097
1098 /*!
1099 @brief 対応する C++ 関数 @ref nn::os::Thread::GetCurrentAffinityMask を参照してください。
1100 */
1101 NN_EXTERN_C void nnosThreadGetCurrentAffinityMask(bit8* pAffinityMask, s32 numProcessor);
1102
1103 /*!
1104 @brief 対応する C++ 関数 @ref nn::os::Thread::GetDefaultAffinityMask を参照してください。
1105 */
1106 NN_EXTERN_C void nnosThreadGetDefaultAffinityMask(bit8* pAffinityMask, s32 numProcessor);
1107
1108 /*!
1109 @brief 対応する C++ 関数 @ref nn::os::Thread::ChangeAffinityMask を参照してください。
1110 */
1111 NN_EXTERN_C void nnosThreadChangeAffinityMask(nnosThread* this_, const bit8* pAffinityMask, s32 numProcessor);
1112
1113 /*!
1114 @brief 対応する C++ 関数 @ref nn::os::Thread::ChangeCurrentAffinityMask を参照してください。
1115 */
1116 NN_EXTERN_C void nnosThreadChangeCurrentAffinityMask(const bit8* pAffinityMask, s32 numProcessor);
1117
1118 /*!
1119 @brief 対応する C++ 関数 @ref nn::os::Thread::SetDefaultAffinityMask を参照してください。
1120 */
1121 NN_EXTERN_C void nnosThreadSetDefaultAffinityMask(const bit8* pAffinityMask, s32 numProcessor);
1122
1123 // IdealProcessor の取得・設定
1124
1125 /*!
1126 @brief 対応する C++ 関数 @ref nn::os::Thread::GetIdealProcessor を参照してください。
1127 */
1128 NN_EXTERN_C s32 nnosThreadGetIdealProcessor(const nnosThread* this_);
1129
1130 /*!
1131 @brief 対応する C++ 関数 @ref nn::os::Thread::GetCurrentIdealProcessor を参照してください。
1132 */
1133 NN_EXTERN_C s32 nnosThreadGetCurrentIdealProcessor(void);
1134
1135 /*!
1136 @brief 対応する C++ 関数 @ref nn::os::Thread::GetDefaultIdealProcessor を参照してください。
1137 */
1138 NN_EXTERN_C s32 nnosThreadGetDefaultIdealProcessor(void);
1139
1140 /*!
1141 @brief 対応する C++ 関数 @ref nn::os::Thread::ChangeIdealProcessor を参照してください。
1142 */
1143 NN_EXTERN_C void nnosThreadChangeIdealProcessor(nnosThread* this_, s32 coreNo);
1144
1145 /*!
1146 @brief 対応する C++ 関数 @ref nn::os::Thread::ChangeCurrentIdealProcessor を参照してください。
1147 */
1148 NN_EXTERN_C void nnosThreadChangeCurrentIdealProcessor(s32 coreNo);
1149
1150 /*!
1151 @brief 対応する C++ 関数 @ref nn::os::Thread::SetDefaultIdealProcessor を参照してください。
1152 */
1153 NN_EXTERN_C void nnosThreadSetDefaultIdealProcessor(s32 coreNo);
1154
1155 // カレントスレッドの動作しているプロセッサ番号の取得
1156
1157 /*!
1158 @brief 対応する C++ 関数 @ref nn::os::Thread::GetCurrentProcessorNumber を参照してください。
1159 */
1160 NN_EXTERN_C s32 nnosThreadGetCurrentProcessorNumber(void);
1161
1162 /*!
1163 @brief 対応する C++ 関数 @ref nn::os::Thread::GetID を参照してください。
1164 */
1165 NN_EXTERN_C bit32 nnosThreadGetId(nnosThread* this_);
1166
1167 /*!
1168 @brief 対応する C++ 関数 @ref nn::os::Thread::IsAlive を参照してください。
1169 */
1170 NN_EXTERN_C bool nnosThreadIsAlive(nnosThread* this_);
1171
1172 /*!
1173 @brief 対応する C++ 関数 @ref nn::os::Thread::GetMainThread を参照してください。
1174 */
1175 NN_EXTERN_C nnosThread* nnosThreadGetMainThread(void);
1176
1177 /*!
1178 @}
1179 @}
1180 */
1181
1182 /* NN_OS_THREAD_H_ */
1183 #endif
1184
1185