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