1 /*---------------------------------------------------------------------------*
2 Project: Horizon
3 File: os_Thread.cpp
4
5 Copyright (C)2009-2012 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: 50238 $
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/os/CTR/os_CppException.h>
23 #include <nn/os/os_ErrorHandlerSelect.h>
24 #include <nn/os/os_Private.h>
25 #include <rt_fp.h>
26
27 #include "os_Limits.h"
28
29 //---------------------------------------------------------------------------
30
31 using namespace nn;
32 using namespace fnd;
33 using namespace nn::svc;
34 using namespace nn::os;
35
36 namespace nn{ namespace os{
37
38 namespace
39 {
40 ThreadLocalRegion* s_pTlr = NULL;
41
42 struct StackBufferAdapter
43 {
44 uptr stackBottom;
StackBufferAdapternn::os::__anon7a8676cc0111::StackBufferAdapter45 StackBufferAdapter(uptr stackBottom) : stackBottom(stackBottom) {}
GetStackBottomnn::os::__anon7a8676cc0111::StackBufferAdapter46 uptr GetStackBottom() const { return stackBottom; }
47 };
48 }
49
50 namespace detail
51 {
ConvertSvcToLibraryPriority(s32 svc)52 s32 ConvertSvcToLibraryPriority(s32 svc)
53 {
54 if( svc >= SVC_USER_THREAD_PRIORITY_HIGHEST )
55 {
56 const s32 offset = svc - SVC_USER_THREAD_PRIORITY_HIGHEST;
57 return offset;
58 }
59 else if( svc >= SVC_LIBRARY_THREAD_PRIORITY_HIGHEST )
60 {
61 const s32 offset = svc - SVC_LIBRARY_THREAD_PRIORITY_HIGHEST;
62 return LIBRARY_THREAD_PRIORITY_BASE + offset;
63 }
64 else
65 {
66 return PRIVILEGED_THREAD_PRIORITY_BASE + svc;
67 }
68 }
ConvertLibraryToSvcPriority(s32 lib)69 s32 ConvertLibraryToSvcPriority(s32 lib)
70 {
71 if ( (USER_THREAD_PRIORITY_HIGHEST <= lib)
72 && (lib <= USER_THREAD_PRIORITY_LOWEST) )
73 {
74 return SVC_USER_THREAD_PRIORITY_HIGHEST + lib;
75 }
76 if ( (LIBRARY_THREAD_PRIORITY_HIGHEST <= lib)
77 && (lib <= LIBRARY_THREAD_PRIORITY_LOWEST) )
78 {
79 const s32 offset = lib - LIBRARY_THREAD_PRIORITY_HIGHEST;
80 return SVC_LIBRARY_THREAD_PRIORITY_HIGHEST + offset;
81 }
82 if ( (PRIVILEGED_THREAD_PRIORITY_HIGHEST <= lib)
83 && (lib <= PRIVILEGED_THREAD_PRIORITY_LOWEST) )
84 {
85 const s32 offset = lib - PRIVILEGED_THREAD_PRIORITY_HIGHEST;
86 return SVC_PRIVILEGED_THREAD_PRIORITY_HIGHEST + offset;
87 }
88 return -1;
89 }
InitializeThreadEnvrionment()90 void InitializeThreadEnvrionment()
91 {
92 #ifdef NN_PROCESSOR_ARM_V6
93 #if ! defined(NN_HARDWARE_CTR_LEGACY)
94 ThreadLocalStorage::ClearAllSlots();
95 CTR::SetupThreadCppExceptionEnvironment();
96 #endif
97 _fp_init();
98 #endif
99 }
SaveThreadLocalRegionAddress()100 void SaveThreadLocalRegionAddress()
101 {
102 NN_TASSERT_( s_pTlr == NULL );
103 s_pTlr = GetThreadLocalRegion();
104 }
GetMainThreadThreadLocalRegion()105 ThreadLocalRegion* GetMainThreadThreadLocalRegion()
106 {
107 return s_pTlr;
108 }
109
110 }
111
112
113
114
115 /* Please see man pages for details
116
117 */
118 struct Thread::FunctionInfo
119 {
120 void (*destroy)(void* p); //
121 void (*invoke)(ThreadFunc f, const void* p); //
122 void (*f)(uptr); //
123 void* p; //
124 void* pAutoStackBuffer;
125
126 // Call handler.
Invokenn::os::Thread::FunctionInfo127 void Invoke()
128 {
129 invoke(f, p);
130 };
131
132 // End processing after handler call.
Destroynn::os::Thread::FunctionInfo133 void Destroy()
134 {
135 destroy(p);
136 }
137 };
138
139
140
141 Thread Thread::s_MainThread = Thread::InitializeAsCurrentTag();
142 Thread::AutoStackManager* Thread::s_pAutoStackManager = NULL;
143
144
145
146
147
148 // When initializing/ending a thread, denote whatever processes may be necessary here.
149 // TLS may be necessary for transferring data.
150
OnThreadStart()151 inline void Thread::OnThreadStart()
152 {
153 nn::os::detail::InitializeThreadEnvrionment();
154 }
155
OnThreadExit()156 inline void Thread::OnThreadExit()
157 {
158 // Nop
159 }
160
NoParameterFunc(void (* f)())161 void Thread::NoParameterFunc(void (*f)())
162 {
163 f();
164 }
165
166 #include <nn/hw/ARM/code32.h>
CallDestructorAndExit(void * pStackBottom NN_IS_UNUSED_VAR)167 asm void Thread::CallDestructorAndExit(void* pStackBottom NN_IS_UNUSED_VAR)
168 {
169 // r0: pStackBottom
170
171 // Call stack destructor.
172 // (s_pAutoStackManager->*f)(pStackBottom, false);
173
174 mov r2, #0 // Put false as the second argument
175 mov r1, r0 // Put pStackBottom as the first argument
176
177 ldr r0, =__cpp(&s_pAutoStackManager) // Get the address of s_pAutoStackManager
178 ldr r0, [r0, #0] // Load s_pAutoStackManager
179
180 ldr r3, [r0, #0] // Load the address of vtable
181 ldr r3, [r3, #__vcall_offsetof_vfunc(AutoStackManager,Destruct)] // Get virtual function address from vtable
182
183 ldr lr, =__cpp(nn::svc::ExitThread) // Put return location in ExitThread
184 bx r3
185
186 LTORG
187 }
188 #include <nn/hw/ARM/codereset.h>
189
190 // Thread process that runs from different context. Call handler.
ThreadStart(uptr p)191 void Thread::ThreadStart(uptr p)
192 {
193 FunctionInfo& info = *reinterpret_cast<FunctionInfo*>(p);
194
195 OnThreadStart();
196 info.Invoke();
197 info.Destroy();
198 OnThreadExit();
199
200 if( info.pAutoStackBuffer != NULL )
201 {
202 CallDestructorAndExit(info.pAutoStackBuffer);
203 }
204
205 nn::svc::ExitThread();
206
207 NN_TASSERT_(0);
208 }
209
TryInitializeAndStartImpl(const TypeInfo & typeInfo,ThreadFunc f,const void * p,uptr stackBottom,s32 priority,s32 coreNo,bool isAutoStack)210 Result Thread::TryInitializeAndStartImpl(
211 const TypeInfo& typeInfo,
212 ThreadFunc f,
213 const void* p,
214 uptr stackBottom,
215 s32 priority,
216 s32 coreNo,
217 bool isAutoStack )
218 {
219 return TryInitializeAndStartImpl(
220 typeInfo,
221 f,
222 p,
223 stackBottom,
224 priority,
225 coreNo,
226 (isAutoStack ? stackBottom: NULL) );
227 }
228
TryInitializeAndStartImpl(const TypeInfo & typeInfo,ThreadFunc f,const void * p,uptr stackBottom,s32 priority,s32 coreNo,uptr autoStackBuffer)229 Result Thread::TryInitializeAndStartImpl(
230 const TypeInfo& typeInfo,
231 ThreadFunc f,
232 const void* p,
233 uptr stackBottom,
234 s32 priority,
235 s32 coreNo,
236 uptr autoStackBuffer )
237 {
238 // Thread::Start -> CreateThread --- Different context from here ---> ThreadStart -> Called in the order of f
239
240 // The stack requires 8-byte alignment.
241 // TODO: Strictly speaking, the files must be separated because this part is ARM-dependent.
242 uptr stack = stackBottom;
243
244 // Copy the parameter that is passed to the handler to the stack region.
245 stack -= typeInfo.size;
246 stack &= 0xfffffff8;
247 void* obj = reinterpret_cast<void*>(stack);
248 typeInfo.copy(p, obj);
249
250 // Write information that is passed to the thread to the stack region.
251 stack -= sizeof(FunctionInfo);
252 stack &= 0xfffffff8;
253 FunctionInfo& info = *reinterpret_cast<FunctionInfo*>(stack);
254 info.destroy = typeInfo.destroy;
255 info.invoke = typeInfo.invoke;
256 info.f = f;
257 info.p = obj;
258 info.pAutoStackBuffer = reinterpret_cast<void*>(autoStackBuffer);
259
260 Handle handle;
261 NN_UTIL_RETURN_IF_FAILED(
262 nn::svc::CreateThread(
263 &handle,
264 ThreadStart,
265 stack,
266 stack,
267 os::detail::ConvertLibraryToSvcPriority(priority),
268 coreNo) );
269
270 this->SetHandle(handle);
271 this->m_CanFinalize = false;
272 this->m_UsingAutoStack = false;
273 return ResultSuccess();
274 }
275
276
SleepImpl(nn::fnd::TimeSpan span)277 void Thread::SleepImpl(nn::fnd::TimeSpan span)
278 {
279 if( span.GetNanoSeconds() >= SLEEP_SPIN_THRESHOLD )
280 {
281 nn::svc::SleepThread(span.GetNanoSeconds());
282 }
283 else
284 {
285 SpinWaitCpuCycles(Tick(span));
286 }
287 }
288
289
290 #if NN_PLATFORM_HAS_MMU
PreStartUsingAutoStack(size_t stackSize)291 uptr Thread::PreStartUsingAutoStack(size_t stackSize)
292 {
293 NN_NULL_TASSERT_(s_pAutoStackManager);
294
295 void* pStackBottom = s_pAutoStackManager->Construct(stackSize);
296 NN_POINTER_TASSERT_(pStackBottom);
297 NN_ALIGN_TASSERT_(pStackBottom, 8);
298
299 return reinterpret_cast<uptr>(pStackBottom);
300 }
PostStartUsingAutoStack(Result result,uptr stackBottom)301 Result Thread::PostStartUsingAutoStack(Result result, uptr stackBottom)
302 {
303 if (result.IsFailure())
304 {
305 s_pAutoStackManager->Destruct(reinterpret_cast<void*>(stackBottom), true);
306 return result;
307 }
308
309 this->m_UsingAutoStack = true;
310 return ResultSuccess();
311 }
TryInitializeAndStartImplUsingAutoStack(const TypeInfo & typeInfo,ThreadFunc f,const void * p,size_t stackSize,s32 priority,s32 coreNo)312 Result Thread::TryInitializeAndStartImplUsingAutoStack(const TypeInfo& typeInfo, ThreadFunc f, const void* p, size_t stackSize, s32 priority, s32 coreNo)
313 {
314 // Thread::Start -> CreateThread --- Different context from here ---> ThreadStart -> Called in the order of f
315
316 const uptr stackBottom = PreStartUsingAutoStack(stackSize);
317 Result result = TryInitializeAndStartImpl(typeInfo, f, p, stackBottom, priority, coreNo, true);
318 return PostStartUsingAutoStack(result, stackBottom);
319 }
320 #endif // if NN_PLATFORM_HAS_MMU
321
322 // For the main thread
Thread(const Thread::InitializeAsCurrentTag &)323 Thread::Thread(const Thread::InitializeAsCurrentTag&)
324 {
325 Handle handle;
326 NN_OS_ERROR_IF_FAILED(nn::svc::DuplicateHandle(&handle, PSEUDO_HANDLE_CURRENT_THREAD));
327 this->SetHandle(handle);
328 this->m_CanFinalize = false;
329 this->m_UsingAutoStack = false;
330 }
331
SetAutoStackManager(AutoStackManager * pManager)332 void Thread::SetAutoStackManager(AutoStackManager* pManager)
333 {
334 s_pAutoStackManager = pManager;
335 }
336
FinalizeImpl()337 void Thread::FinalizeImpl()
338 {
339 if (!m_CanFinalize)
340 {
341 NN_TASSERTMSG_(m_CanFinalize, "Thread should be Joined or Detached before being Finalized.");
342 this->WaitOne();
343 this->m_CanFinalize = true;
344 }
345 }
346
347 #if NN_PLATFORM_HAS_MMU
GetCurrentCount()348 s32 Thread::GetCurrentCount()
349 {
350 return os::detail::GetLimitCurrentCount(LIMITABLE_RESOURCE_MAX_THREAD);
351 }
352
GetMaxCount()353 s32 Thread::GetMaxCount()
354 {
355 return os::detail::GetLimitMaxCount(LIMITABLE_RESOURCE_MAX_THREAD);
356 }
357 #endif // if NN_PLATFORM_HAS_MMU
358
359 }} // namespace nn::os
360
361
362
363
364 #include <new>
365 using namespace nn::os;
366
367 extern "C" {
368
nnosThreadInitializeAndStart(nnosThread * p,void (* f)(uptr),uptr param,uptr stackBottom,s32 priority,s32 coreNo)369 void nnosThreadInitializeAndStart(nnosThread* p, void (*f)(uptr), uptr param, uptr stackBottom, s32 priority, s32 coreNo)
370 {
371 Thread* pThread = new (p) Thread();
372 StackBufferAdapter stack(stackBottom);
373 pThread->Start(f, param, stack, priority, coreNo);
374 }
375
nnosThreadTryInitializeAndStart(nnosThread * p,void (* f)(uptr),uptr param,uptr stackBottom,s32 priority,s32 coreNo)376 bool nnosThreadTryInitializeAndStart(nnosThread* p, void (*f)(uptr), uptr param, uptr stackBottom, s32 priority, s32 coreNo)
377 {
378 Thread* pThread = new (p) Thread();
379 StackBufferAdapter stack(stackBottom);
380 Result result = pThread->TryStart(f, param, stack, priority, coreNo);
381 return result.IsSuccess();
382 }
383
nnosThreadFinalize(nnosThread * p)384 void nnosThreadFinalize(nnosThread* p)
385 {
386 Thread* pThread = reinterpret_cast<Thread*>(p);
387 pThread->~Thread();
388 }
389
nnosThreadJoin(nnosThread * p)390 void nnosThreadJoin(nnosThread* p)
391 {
392 Thread* pThread = reinterpret_cast<Thread*>(p);
393 pThread->Join();
394 }
395
nnosThreadSleep(s64 nanoSeconds)396 void nnosThreadSleep(s64 nanoSeconds)
397 {
398 Thread::Sleep(TimeSpan::FromNanoSeconds(nanoSeconds));
399 }
400
nnosThreadYield()401 void nnosThreadYield()
402 {
403 Thread::Yield();
404 }
405
nnosThreadGetCurrentId(void)406 bit32 nnosThreadGetCurrentId(void)
407 {
408 return Thread::GetCurrentId();
409 }
410
nnosThreadGetPriority(const nnosThread * p)411 s32 nnosThreadGetPriority(const nnosThread* p)
412 {
413 const Thread* pThread = reinterpret_cast<const Thread*>(p);
414 return pThread->GetPriority();
415 }
416
nnosThreadGetCurrentPriority()417 s32 nnosThreadGetCurrentPriority()
418 {
419 return Thread::GetCurrentPriority();
420 }
421
nnosThreadChangePriority(nnosThread * p,s32 priority)422 void nnosThreadChangePriority(nnosThread* p, s32 priority)
423 {
424 Thread* pThread = reinterpret_cast<Thread*>(p);
425 return pThread->ChangePriority(priority);
426 }
427
nnosThreadChangeCurrentPriority(s32 priority)428 void nnosThreadChangeCurrentPriority(s32 priority)
429 {
430 Thread::ChangeCurrentPriority(priority);
431 }
432
433 // Get/set the thread affinity
434 //void nnosThreadGetAffinityMask(const nnosThread* p, bit8* pAffinityMask, s32 numProcessor)
435 //{
436 // const Thread* pThread = reinterpret_cast<const Thread*>(p);
437 // pThread->GetAffinityMask(pAffinityMask, numProcessor);
438 //}
439 //
440 //void nnosThreadGetCurrentAffinityMask(bit8* pAffinityMask, s32 numProcessor)
441 //{
442 // Thread::GetCurrentAffinityMask(pAffinityMask, numProcessor);
443 //}
444 //
445 //void nnosThreadGetDefaultAffinityMask(bit8* pAffinityMask, s32 numProcessor)
446 //{
447 // Thread::GetDefaultAffinityMask(pAffinityMask, numProcessor);
448 //}
449 //
450 //void nnosThreadChangeAffinityMask(nnosThread* p, const bit8* pAffinityMask, s32 numProcessor)
451 //{
452 // Thread* pThread = reinterpret_cast<Thread*>(p);
453 // pThread->ChangeAffinityMask(pAffinityMask, numProcessor);
454 //}
455 //
456 //void nnosThreadChangeCurrentAffinityMask(const bit8* pAffinityMask, s32 numProcessor)
457 //{
458 // Thread::ChangeCurrentAffinityMask(pAffinityMask, numProcessor);
459 //}
460 //
461 //void nnosThreadSetDefaultAffinityMask(const bit8* pAffinityMask, s32 numProcessor)
462 //{
463 // Thread::SetDefaultAffinityMask(pAffinityMask, numProcessor);
464 //}
465
466 // Get/set IdealProcessor
nnosThreadGetIdealProcessor(const nnosThread * p)467 s32 nnosThreadGetIdealProcessor(const nnosThread* p)
468 {
469 const Thread* pThread = reinterpret_cast<const Thread*>(p);
470 return pThread->GetIdealProcessor();
471 }
472
473 //s32 nnosThreadGetCurrentIdealProcessor()
474 //{
475 // return Thread::GetCurrentIdealProcessor();
476 //}
477
nnosThreadGetDefaultIdealProcessor()478 s32 nnosThreadGetDefaultIdealProcessor()
479 {
480 return Thread::GetDefaultIdealProcessor();
481 }
482
483 //void nnosThreadChangeIdealProcessor(nnosThread* p, s32 coreNo)
484 //{
485 // Thread* pThread = reinterpret_cast<Thread*>(p);
486 // pThread->ChangeIdealProcessor(coreNo);
487 //}
488 //
489 //void nnosThreadChangeCurrentIdealProcessor(s32 coreNo)
490 //{
491 // Thread::ChangeCurrentIdealProcessor(coreNo);
492 //}
493 //
494 //void nnosThreadSetDefaultIdealProcessor(s32 coreNo)
495 //{
496 // Thread::SetDefaultIdealProcessor(coreNo);
497 //}
498
nnosThreadGetCurrentProcessorNumber()499 s32 nnosThreadGetCurrentProcessorNumber()
500 {
501 return Thread::GetCurrentProcessorNumber();
502 }
503
nnosThreadGetId(nnosThread * p)504 bit32 nnosThreadGetId(nnosThread* p)
505 {
506 Thread* pThread = reinterpret_cast<Thread*>(p);
507 return pThread->GetId();
508 }
509
nnosThreadIsAlive(nnosThread * p)510 bool nnosThreadIsAlive(nnosThread* p)
511 {
512 Thread* pThread = reinterpret_cast<Thread*>(p);
513 return pThread->IsAlive();
514 }
515
nnosThreadGetMainThread(void)516 nnosThread* nnosThreadGetMainThread(void)
517 {
518 return reinterpret_cast<nnosThread*>(&nn::os::Thread::GetMainThread());
519 }
520
521 }
522