1 /*---------------------------------------------------------------------------*
2 Project: Horizon
3 File: os_Thread.cpp
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 #include <nn/assert.h>
17 #include <nn/Handle.h>
18 #include <nn/svc/svc_Stub.h>
19 #include <nn/dbg.h>
20 #include <nn/os.h>
21 #include <nn/os/os_PrivatePriority.h>
22 #include <nn/err.h>
23 //---------------------------------------------------------------------------
24
25 using namespace nn;
26 using namespace fnd;
27 using namespace nn::svc;
28 using namespace nn::os;
29
30 namespace nn{ namespace os{
31
32 namespace
33 {
34 struct StackBufferAdapter
35 {
36 uptr stackBottom;
StackBufferAdapternn::os::__anonb22adbb20111::StackBufferAdapter37 StackBufferAdapter(uptr stackBottom) : stackBottom(stackBottom) {}
GetStackBottomnn::os::__anonb22adbb20111::StackBufferAdapter38 uptr GetStackBottom() const { return stackBottom; }
39 };
40 }
41
42 namespace detail
43 {
ConvertSvcToLibraryPriority(s32 svc)44 s32 ConvertSvcToLibraryPriority(s32 svc)
45 {
46 if( svc >= SVC_USER_THREAD_PRIORITY_HIGHEST )
47 {
48 const s32 offset = svc - SVC_USER_THREAD_PRIORITY_HIGHEST;
49 return offset;
50 }
51 else if( svc >= SVC_LIBRARY_THREAD_PRIORITY_HIGHEST )
52 {
53 const s32 offset = svc - SVC_LIBRARY_THREAD_PRIORITY_HIGHEST;
54 return LIBRARY_THREAD_PRIORITY_BASE + offset;
55 }
56 else
57 {
58 return PRIVILEGED_THREAD_PRIORITY_BASE + svc;
59 }
60 }
ConvertLibraryToSvcPriority(s32 lib)61 s32 ConvertLibraryToSvcPriority(s32 lib)
62 {
63 if ( (USER_THREAD_PRIORITY_HIGHEST <= lib)
64 && (lib <= USER_THREAD_PRIORITY_LOWEST) )
65 {
66 return SVC_USER_THREAD_PRIORITY_HIGHEST + lib;
67 }
68 if ( (LIBRARY_THREAD_PRIORITY_HIGHEST <= lib)
69 && (lib <= LIBRARY_THREAD_PRIORITY_LOWEST) )
70 {
71 const s32 offset = lib - LIBRARY_THREAD_PRIORITY_HIGHEST;
72 return SVC_LIBRARY_THREAD_PRIORITY_HIGHEST + offset;
73 }
74 if ( (PRIVILEGED_THREAD_PRIORITY_HIGHEST <= lib)
75 && (lib <= PRIVILEGED_THREAD_PRIORITY_LOWEST) )
76 {
77 const s32 offset = lib - PRIVILEGED_THREAD_PRIORITY_HIGHEST;
78 return SVC_PRIVILEGED_THREAD_PRIORITY_HIGHEST + offset;
79 }
80 return -1;
81 }
82 }
83
84
85
86
87 /*!
88 @brief スレッドに渡す構造体です。
89 */
90 struct Thread::FunctionInfo
91 {
92 void (*destroy)(void* p); //!< 終了処理関数
93 void (*invoke)(ThreadFunc f, const void* p); //!< ハンドラを呼び出す関数
94 void (*f)(uptr); //!< ハンドラ
95 void* p; //!< ハンドラに渡すパラメータ
96 void* pStackBottom;
97 bool isAutoStack;
98 NN_PADDING3;
99
100 // ハンドラを呼び出します。
Invokenn::os::Thread::FunctionInfo101 void Invoke()
102 {
103 invoke(f, p);
104 };
105
106 // ハンドラ呼出し後の終了処理です。
Destroynn::os::Thread::FunctionInfo107 void Destroy()
108 {
109 destroy(p);
110 }
111 };
112
113
114
115 Thread Thread::s_MainThread = Thread::InitializeAsCurrentTag();
116 Thread::AutoStackManager* Thread::s_pAutoStackManager = NULL;
117
118
119
120
121
122 // スレッドの初期化時・終了時に何か処理が必要な場合はここに記述する。
123 // データの受け渡しには TLS が必要かもしれない。
124
OnThreadStart()125 inline void Thread::OnThreadStart()
126 {
127 #ifdef NN_PROCESSOR_ARM_V6
128 #if ! defined(NN_HARDWARE_CTR_LEGACY)
129 ThreadLocalStorage::ClearAllSlots();
130 #endif
131 #endif
132 }
133
OnThreadExit()134 inline void Thread::OnThreadExit()
135 {
136 // nop
137 }
138
NoParameterFunc(void (* f)())139 void Thread::NoParameterFunc(void (*f)())
140 {
141 f();
142 }
143
144 #include <nn/hw/ARM/code32.h>
CallDestructorAndExit(void * pStackBottom NN_IS_UNUSED_VAR)145 asm void Thread::CallDestructorAndExit(void* pStackBottom NN_IS_UNUSED_VAR)
146 {
147 // r0: pStackBottom
148
149 // スタックデストラクタを呼ぶ
150 // (s_pAutoStackManager->*f)(pStackBottom, false);
151
152 mov r2, #0 // false を第 2 引数に
153 mov r1, r0 // pStackBottom を第 1 引数に
154
155 ldr r0, =__cpp(&s_pAutoStackManager) // s_pAutoStackManager のアドレスを取得
156 ldr r0, [r0, #0] // s_pAutoStackManager をロード
157
158 ldr r3, [r0, #0] // vtable のアドレスをロード
159 ldr r3, [r3, #4] // vtable から仮想関数のアドレスを取得
160
161 ldr lr, =__cpp(nn::svc::ExitThread) // 返り先を ExitThread に
162 bx r3
163
164 LTORG
165 }
166 #include <nn/hw/ARM/codereset.h>
167
168 // 別コンテキストから実行されるスレッド処理です。ハンドラを呼び出します。
ThreadStart(uptr p)169 void Thread::ThreadStart(uptr p)
170 {
171 FunctionInfo& info = *reinterpret_cast<FunctionInfo*>(p);
172
173 OnThreadStart();
174 info.Invoke();
175 info.Destroy();
176 OnThreadExit();
177
178 if( info.isAutoStack )
179 {
180 CallDestructorAndExit(info.pStackBottom);
181 }
182
183 nn::svc::ExitThread();
184
185 NN_TASSERT_(0);
186 }
187
TryInitializeAndStartImpl(const TypeInfo & typeInfo,ThreadFunc f,const void * p,uptr stackBottom,s32 priority,s32 coreNo,bool isAutoStack)188 Result Thread::TryInitializeAndStartImpl(const TypeInfo& typeInfo, ThreadFunc f, const void* p, uptr stackBottom, s32 priority, s32 coreNo, bool isAutoStack)
189 {
190 // Thread::Start -> CreateThread ---ここから別コンテキスト---> ThreadStart -> f の順に呼ばれる
191
192 // stack は 8 バイトアライメントを要求する。
193 // TODO: 厳密にはこの部分は ARM 依存であるため、ファイルを分ける必要がある。
194 uptr stack = stackBottom;
195
196 // ハンドラに渡すパラメータをスタック領域にコピーします。
197 stack -= typeInfo.size;
198 stack &= 0xfffffff8;
199 void* obj = reinterpret_cast<void*>(stack);
200 typeInfo.copy(p, obj);
201
202 // スレッドに渡すに情報をスタック領域に書き込みます。
203 stack -= sizeof(FunctionInfo);
204 stack &= 0xfffffff8;
205 FunctionInfo& info = *reinterpret_cast<FunctionInfo*>(stack);
206 info.destroy = typeInfo.destroy;
207 info.invoke = typeInfo.invoke;
208 info.f = f;
209 info.p = obj;
210 info.isAutoStack = isAutoStack;
211 info.pStackBottom = reinterpret_cast<void*>(stackBottom);
212
213 Handle handle;
214 NN_UTIL_RETURN_IF_FAILED(
215 nn::svc::CreateThread(
216 &handle,
217 ThreadStart,
218 stack,
219 stack,
220 os::detail::ConvertLibraryToSvcPriority(priority),
221 coreNo) );
222
223 this->SetHandle(handle);
224 this->m_CanFinalize = false;
225 this->m_UsingAutoStack = false;
226 return ResultSuccess();
227 }
228
229
SleepImpl(nn::fnd::TimeSpan span)230 void Thread::SleepImpl(nn::fnd::TimeSpan span)
231 {
232 if( span.GetNanoSeconds() >= SLEEP_SPIN_THRESHOLD )
233 {
234 nn::svc::SleepThread(span.GetNanoSeconds());
235 }
236 else
237 {
238 SpinWaitCpuCycles(Tick(span));
239 }
240 }
241
242
243 #if NN_PLATFORM_HAS_MMU
TryInitializeAndStartImplUsingAutoStack(const TypeInfo & typeInfo,ThreadFunc f,const void * p,size_t stackSize,s32 priority,s32 coreNo)244 Result Thread::TryInitializeAndStartImplUsingAutoStack(const TypeInfo& typeInfo, ThreadFunc f, const void* p, size_t stackSize, s32 priority, s32 coreNo)
245 {
246 // Thread::Start -> CreateThread ---ここから別コンテキスト---> ThreadStart -> f の順に呼ばれる
247 NN_NULL_TASSERT_(s_pAutoStackManager);
248
249 void* pStackBottom = s_pAutoStackManager->Construct(stackSize);
250 uptr stack = reinterpret_cast<uptr>(pStackBottom);
251
252 Result result = TryInitializeAndStartImpl(typeInfo, f, p, stack, priority, coreNo, true);
253
254 if (result.IsFailure())
255 {
256 s_pAutoStackManager->Destruct(pStackBottom, true);
257 return result;
258 }
259
260 this->m_UsingAutoStack = true;
261 return ResultSuccess();
262 }
263 #endif // if NN_PLATFORM_HAS_MMU
264
265 // メインスレッド用
Thread(const Thread::InitializeAsCurrentTag &)266 Thread::Thread(const Thread::InitializeAsCurrentTag&)
267 {
268 Handle handle;
269 NN_ERR_THROW_FATAL(nn::svc::DuplicateHandle(&handle, PSEUDO_HANDLE_CURRENT_THREAD));
270 this->SetHandle(handle);
271 this->m_CanFinalize = false;
272 this->m_UsingAutoStack = false;
273 }
274
SetAutoStackManager(AutoStackManager * pManager)275 void Thread::SetAutoStackManager(AutoStackManager* pManager)
276 {
277 s_pAutoStackManager = pManager;
278 }
279
280 }} // namespace nn::os
281
282
283
284
285 #include <new>
286 using namespace nn::os;
287
288 extern "C" {
289
nnosThreadInitializeAndStart(nnosThread * p,void (* f)(uptr),uptr param,uptr stackBottom,s32 priority,s32 coreNo)290 void nnosThreadInitializeAndStart(nnosThread* p, void (*f)(uptr), uptr param, uptr stackBottom, s32 priority, s32 coreNo)
291 {
292 Thread* pThread = new (p) Thread();
293 StackBufferAdapter stack(stackBottom);
294 pThread->Start(f, param, stack, priority, coreNo);
295 }
296
nnosThreadTryInitializeAndStart(nnosThread * p,void (* f)(uptr),uptr param,uptr stackBottom,s32 priority,s32 coreNo)297 bool nnosThreadTryInitializeAndStart(nnosThread* p, void (*f)(uptr), uptr param, uptr stackBottom, s32 priority, s32 coreNo)
298 {
299 Thread* pThread = new (p) Thread();
300 StackBufferAdapter stack(stackBottom);
301 Result result = pThread->TryStart(f, param, stack, priority, coreNo);
302 return result.IsSuccess();
303 }
304
nnosThreadFinalize(nnosThread * p)305 void nnosThreadFinalize(nnosThread* p)
306 {
307 Thread* pThread = reinterpret_cast<Thread*>(p);
308 pThread->~Thread();
309 }
310
nnosThreadJoin(nnosThread * p)311 void nnosThreadJoin(nnosThread* p)
312 {
313 Thread* pThread = reinterpret_cast<Thread*>(p);
314 pThread->Join();
315 }
316
nnosThreadSleep(s64 nanoSeconds)317 void nnosThreadSleep(s64 nanoSeconds)
318 {
319 Thread::Sleep(TimeSpan::FromNanoSeconds(nanoSeconds));
320 }
321
nnosThreadYield()322 void nnosThreadYield()
323 {
324 Thread::Yield();
325 }
326
nnosThreadGetCurrentId(void)327 bit32 nnosThreadGetCurrentId(void)
328 {
329 return Thread::GetCurrentId();
330 }
331
nnosThreadGetPriority(const nnosThread * p)332 s32 nnosThreadGetPriority(const nnosThread* p)
333 {
334 const Thread* pThread = reinterpret_cast<const Thread*>(p);
335 return pThread->GetPriority();
336 }
337
nnosThreadGetCurrentPriority()338 s32 nnosThreadGetCurrentPriority()
339 {
340 return Thread::GetCurrentPriority();
341 }
342
nnosThreadChangePriority(nnosThread * p,s32 priority)343 void nnosThreadChangePriority(nnosThread* p, s32 priority)
344 {
345 Thread* pThread = reinterpret_cast<Thread*>(p);
346 return pThread->ChangePriority(priority);
347 }
348
nnosThreadChangeCurrentPriority(s32 priority)349 void nnosThreadChangeCurrentPriority(s32 priority)
350 {
351 Thread::ChangeCurrentPriority(priority);
352 }
353
354 // スレッド Affinity の設定・取得
nnosThreadGetAffinityMask(const nnosThread * p,bit8 * pAffinityMask,s32 numProcessor)355 void nnosThreadGetAffinityMask(const nnosThread* p, bit8* pAffinityMask, s32 numProcessor)
356 {
357 const Thread* pThread = reinterpret_cast<const Thread*>(p);
358 pThread->GetAffinityMask(pAffinityMask, numProcessor);
359 }
360
nnosThreadGetCurrentAffinityMask(bit8 * pAffinityMask,s32 numProcessor)361 void nnosThreadGetCurrentAffinityMask(bit8* pAffinityMask, s32 numProcessor)
362 {
363 Thread::GetCurrentAffinityMask(pAffinityMask, numProcessor);
364 }
365
nnosThreadGetDefaultAffinityMask(bit8 * pAffinityMask,s32 numProcessor)366 void nnosThreadGetDefaultAffinityMask(bit8* pAffinityMask, s32 numProcessor)
367 {
368 Thread::GetDefaultAffinityMask(pAffinityMask, numProcessor);
369 }
370
nnosThreadChangeAffinityMask(nnosThread * p,const bit8 * pAffinityMask,s32 numProcessor)371 void nnosThreadChangeAffinityMask(nnosThread* p, const bit8* pAffinityMask, s32 numProcessor)
372 {
373 Thread* pThread = reinterpret_cast<Thread*>(p);
374 pThread->ChangeAffinityMask(pAffinityMask, numProcessor);
375 }
376
nnosThreadChangeCurrentAffinityMask(const bit8 * pAffinityMask,s32 numProcessor)377 void nnosThreadChangeCurrentAffinityMask(const bit8* pAffinityMask, s32 numProcessor)
378 {
379 Thread::ChangeCurrentAffinityMask(pAffinityMask, numProcessor);
380 }
381
nnosThreadSetDefaultAffinityMask(const bit8 * pAffinityMask,s32 numProcessor)382 void nnosThreadSetDefaultAffinityMask(const bit8* pAffinityMask, s32 numProcessor)
383 {
384 Thread::SetDefaultAffinityMask(pAffinityMask, numProcessor);
385 }
386
387 // IdealProcessor の取得・設定
nnosThreadGetIdealProcessor(const nnosThread * p)388 s32 nnosThreadGetIdealProcessor(const nnosThread* p)
389 {
390 const Thread* pThread = reinterpret_cast<const Thread*>(p);
391 return pThread->GetIdealProcessor();
392 }
393
nnosThreadGetCurrentIdealProcessor()394 s32 nnosThreadGetCurrentIdealProcessor()
395 {
396 return Thread::GetCurrentIdealProcessor();
397 }
398
nnosThreadGetDefaultIdealProcessor()399 s32 nnosThreadGetDefaultIdealProcessor()
400 {
401 return Thread::GetDefaultIdealProcessor();
402 }
403
nnosThreadChangeIdealProcessor(nnosThread * p,s32 coreNo)404 void nnosThreadChangeIdealProcessor(nnosThread* p, s32 coreNo)
405 {
406 Thread* pThread = reinterpret_cast<Thread*>(p);
407 pThread->ChangeIdealProcessor(coreNo);
408 }
409
nnosThreadChangeCurrentIdealProcessor(s32 coreNo)410 void nnosThreadChangeCurrentIdealProcessor(s32 coreNo)
411 {
412 Thread::ChangeCurrentIdealProcessor(coreNo);
413 }
414
nnosThreadSetDefaultIdealProcessor(s32 coreNo)415 void nnosThreadSetDefaultIdealProcessor(s32 coreNo)
416 {
417 Thread::SetDefaultIdealProcessor(coreNo);
418 }
419
nnosThreadGetCurrentProcessorNumber()420 s32 nnosThreadGetCurrentProcessorNumber()
421 {
422 return Thread::GetCurrentProcessorNumber();
423 }
424
nnosThreadGetId(nnosThread * p)425 bit32 nnosThreadGetId(nnosThread* p)
426 {
427 Thread* pThread = reinterpret_cast<Thread*>(p);
428 return pThread->GetId();
429 }
430
nnosThreadIsAlive(nnosThread * p)431 bool nnosThreadIsAlive(nnosThread* p)
432 {
433 Thread* pThread = reinterpret_cast<Thread*>(p);
434 return pThread->IsAlive();
435 }
436
nnosThreadGetMainThread(void)437 nnosThread* nnosThreadGetMainThread(void)
438 {
439 return reinterpret_cast<nnosThread*>(&nn::os::Thread::GetMainThread());
440 }
441
442 }
443