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