/*---------------------------------------------------------------------------* Project: Horizon File: os_Thread.cpp Copyright (C)2009 Nintendo Co., Ltd. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Rev: 38846 $ *---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include //--------------------------------------------------------------------------- using namespace nn; using namespace fnd; using namespace nn::svc; using namespace nn::os; namespace nn{ namespace os{ namespace { struct StackBufferAdapter { uptr stackBottom; StackBufferAdapter(uptr stackBottom) : stackBottom(stackBottom) {} uptr GetStackBottom() const { return stackBottom; } }; } namespace detail { s32 ConvertSvcToLibraryPriority(s32 svc) { if( svc >= SVC_USER_THREAD_PRIORITY_HIGHEST ) { const s32 offset = svc - SVC_USER_THREAD_PRIORITY_HIGHEST; return offset; } else if( svc >= SVC_LIBRARY_THREAD_PRIORITY_HIGHEST ) { const s32 offset = svc - SVC_LIBRARY_THREAD_PRIORITY_HIGHEST; return LIBRARY_THREAD_PRIORITY_BASE + offset; } else { return PRIVILEGED_THREAD_PRIORITY_BASE + svc; } } s32 ConvertLibraryToSvcPriority(s32 lib) { if ( (USER_THREAD_PRIORITY_HIGHEST <= lib) && (lib <= USER_THREAD_PRIORITY_LOWEST) ) { return SVC_USER_THREAD_PRIORITY_HIGHEST + lib; } if ( (LIBRARY_THREAD_PRIORITY_HIGHEST <= lib) && (lib <= LIBRARY_THREAD_PRIORITY_LOWEST) ) { const s32 offset = lib - LIBRARY_THREAD_PRIORITY_HIGHEST; return SVC_LIBRARY_THREAD_PRIORITY_HIGHEST + offset; } if ( (PRIVILEGED_THREAD_PRIORITY_HIGHEST <= lib) && (lib <= PRIVILEGED_THREAD_PRIORITY_LOWEST) ) { const s32 offset = lib - PRIVILEGED_THREAD_PRIORITY_HIGHEST; return SVC_PRIVILEGED_THREAD_PRIORITY_HIGHEST + offset; } return -1; } void InitializeThreadEnvrionment() { #ifdef NN_PROCESSOR_ARM_V6 #if ! defined(NN_HARDWARE_CTR_LEGACY) ThreadLocalStorage::ClearAllSlots(); CTR::SetupThreadCppExceptionEnvironment(); #endif _fp_init(); #endif } } /* Please see man pages for details */ struct Thread::FunctionInfo { void (*destroy)(void* p); // void (*invoke)(ThreadFunc f, const void* p); // void (*f)(uptr); // void* p; // void* pStackBottom; bool isAutoStack; NN_PADDING3; // Calls the handler. void Invoke() { invoke(f, p); }; // Finalization process after calling the handler. void Destroy() { destroy(p); } }; Thread Thread::s_MainThread = Thread::InitializeAsCurrentTag(); Thread::AutoStackManager* Thread::s_pAutoStackManager = NULL; // Write any require processes when initializing or finalizing threads. // TLS may be required to transfer data. inline void Thread::OnThreadStart() { nn::os::detail::InitializeThreadEnvrionment(); } inline void Thread::OnThreadExit() { // Nop } void Thread::NoParameterFunc(void (*f)()) { f(); } #include asm void Thread::CallDestructorAndExit(void* pStackBottom NN_IS_UNUSED_VAR) { // r0: pStackBottom // Calls the stack destructor // (s_pAutoStackManager->*f)(pStackBottom, false); mov r2, #0 // Set false to the second argument mov r1, r0 // Set pStackBottom to the first argument ldr r0, =__cpp(&s_pAutoStackManager) // Gets the s_pAutoStackManager address ldr r0, [r0, #0] // Loads s_pAutoStackManager ldr r3, [r0, #0] // Loads the vtable address ldr r3, [r3, #4] // Gets the virtual function address from vtable ldr lr, =__cpp(nn::svc::ExitThread) // Set return destination to ExitThread bx r3 LTORG } #include // Thread process executed from a different context. Calls the handler. void Thread::ThreadStart(uptr p) { FunctionInfo& info = *reinterpret_cast(p); OnThreadStart(); info.Invoke(); info.Destroy(); OnThreadExit(); if( info.isAutoStack ) { CallDestructorAndExit(info.pStackBottom); } nn::svc::ExitThread(); NN_TASSERT_(0); } Result Thread::TryInitializeAndStartImpl(const TypeInfo& typeInfo, ThreadFunc f, const void* p, uptr stackBottom, s32 priority, s32 coreNo, bool isAutoStack) { // Called in the order: Thread::Start -> CreateThread ---Different context from here---> ThreadStart -> f // stack require 8-byte alignment. // TODO: Technically, this portion depends on ARM, so the file must be divided. uptr stack = stackBottom; // The parameter passed to the handler is copied to the stack region. stack -= typeInfo.size; stack &= 0xfffffff8; void* obj = reinterpret_cast(stack); typeInfo.copy(p, obj); // Information passed to the thread is written to the stack region. stack -= sizeof(FunctionInfo); stack &= 0xfffffff8; FunctionInfo& info = *reinterpret_cast(stack); info.destroy = typeInfo.destroy; info.invoke = typeInfo.invoke; info.f = f; info.p = obj; info.isAutoStack = isAutoStack; info.pStackBottom = reinterpret_cast(stackBottom); Handle handle; NN_UTIL_RETURN_IF_FAILED( nn::svc::CreateThread( &handle, ThreadStart, stack, stack, os::detail::ConvertLibraryToSvcPriority(priority), coreNo) ); this->SetHandle(handle); this->m_CanFinalize = false; this->m_UsingAutoStack = false; return ResultSuccess(); } void Thread::SleepImpl(nn::fnd::TimeSpan span) { if( span.GetNanoSeconds() >= SLEEP_SPIN_THRESHOLD ) { nn::svc::SleepThread(span.GetNanoSeconds()); } else { SpinWaitCpuCycles(Tick(span)); } } #if NN_PLATFORM_HAS_MMU Result Thread::TryInitializeAndStartImplUsingAutoStack(const TypeInfo& typeInfo, ThreadFunc f, const void* p, size_t stackSize, s32 priority, s32 coreNo) { // Called in the order: Thread::Start -> CreateThread ---Different context from here---> ThreadStart -> f NN_NULL_TASSERT_(s_pAutoStackManager); void* pStackBottom = s_pAutoStackManager->Construct(stackSize); uptr stack = reinterpret_cast(pStackBottom); Result result = TryInitializeAndStartImpl(typeInfo, f, p, stack, priority, coreNo, true); if (result.IsFailure()) { s_pAutoStackManager->Destruct(pStackBottom, true); return result; } this->m_UsingAutoStack = true; return ResultSuccess(); } #endif // if NN_PLATFORM_HAS_MMU // For the main thread Thread::Thread(const Thread::InitializeAsCurrentTag&) { Handle handle; NN_OS_ERROR_IF_FAILED(nn::svc::DuplicateHandle(&handle, PSEUDO_HANDLE_CURRENT_THREAD)); this->SetHandle(handle); this->m_CanFinalize = false; this->m_UsingAutoStack = false; } void Thread::SetAutoStackManager(AutoStackManager* pManager) { s_pAutoStackManager = pManager; } void Thread::FinalizeImpl() { if (!m_CanFinalize) { NN_TASSERTMSG_(m_CanFinalize, "Thread should be Joined or Detached before being Finalized."); this->WaitOne(); this->m_CanFinalize = true; } } }} // namespace nn::os #include using namespace nn::os; extern "C" { void nnosThreadInitializeAndStart(nnosThread* p, void (*f)(uptr), uptr param, uptr stackBottom, s32 priority, s32 coreNo) { Thread* pThread = new (p) Thread(); StackBufferAdapter stack(stackBottom); pThread->Start(f, param, stack, priority, coreNo); } bool nnosThreadTryInitializeAndStart(nnosThread* p, void (*f)(uptr), uptr param, uptr stackBottom, s32 priority, s32 coreNo) { Thread* pThread = new (p) Thread(); StackBufferAdapter stack(stackBottom); Result result = pThread->TryStart(f, param, stack, priority, coreNo); return result.IsSuccess(); } void nnosThreadFinalize(nnosThread* p) { Thread* pThread = reinterpret_cast(p); pThread->~Thread(); } void nnosThreadJoin(nnosThread* p) { Thread* pThread = reinterpret_cast(p); pThread->Join(); } void nnosThreadSleep(s64 nanoSeconds) { Thread::Sleep(TimeSpan::FromNanoSeconds(nanoSeconds)); } void nnosThreadYield() { Thread::Yield(); } bit32 nnosThreadGetCurrentId(void) { return Thread::GetCurrentId(); } s32 nnosThreadGetPriority(const nnosThread* p) { const Thread* pThread = reinterpret_cast(p); return pThread->GetPriority(); } s32 nnosThreadGetCurrentPriority() { return Thread::GetCurrentPriority(); } void nnosThreadChangePriority(nnosThread* p, s32 priority) { Thread* pThread = reinterpret_cast(p); return pThread->ChangePriority(priority); } void nnosThreadChangeCurrentPriority(s32 priority) { Thread::ChangeCurrentPriority(priority); } // Sets and gets thread Affinity void nnosThreadGetAffinityMask(const nnosThread* p, bit8* pAffinityMask, s32 numProcessor) { const Thread* pThread = reinterpret_cast(p); pThread->GetAffinityMask(pAffinityMask, numProcessor); } void nnosThreadGetCurrentAffinityMask(bit8* pAffinityMask, s32 numProcessor) { Thread::GetCurrentAffinityMask(pAffinityMask, numProcessor); } void nnosThreadGetDefaultAffinityMask(bit8* pAffinityMask, s32 numProcessor) { Thread::GetDefaultAffinityMask(pAffinityMask, numProcessor); } void nnosThreadChangeAffinityMask(nnosThread* p, const bit8* pAffinityMask, s32 numProcessor) { Thread* pThread = reinterpret_cast(p); pThread->ChangeAffinityMask(pAffinityMask, numProcessor); } void nnosThreadChangeCurrentAffinityMask(const bit8* pAffinityMask, s32 numProcessor) { Thread::ChangeCurrentAffinityMask(pAffinityMask, numProcessor); } void nnosThreadSetDefaultAffinityMask(const bit8* pAffinityMask, s32 numProcessor) { Thread::SetDefaultAffinityMask(pAffinityMask, numProcessor); } // Sets and gets IdealProcessor s32 nnosThreadGetIdealProcessor(const nnosThread* p) { const Thread* pThread = reinterpret_cast(p); return pThread->GetIdealProcessor(); } s32 nnosThreadGetCurrentIdealProcessor() { return Thread::GetCurrentIdealProcessor(); } s32 nnosThreadGetDefaultIdealProcessor() { return Thread::GetDefaultIdealProcessor(); } void nnosThreadChangeIdealProcessor(nnosThread* p, s32 coreNo) { Thread* pThread = reinterpret_cast(p); pThread->ChangeIdealProcessor(coreNo); } void nnosThreadChangeCurrentIdealProcessor(s32 coreNo) { Thread::ChangeCurrentIdealProcessor(coreNo); } void nnosThreadSetDefaultIdealProcessor(s32 coreNo) { Thread::SetDefaultIdealProcessor(coreNo); } s32 nnosThreadGetCurrentProcessorNumber() { return Thread::GetCurrentProcessorNumber(); } bit32 nnosThreadGetId(nnosThread* p) { Thread* pThread = reinterpret_cast(p); return pThread->GetId(); } bool nnosThreadIsAlive(nnosThread* p) { Thread* pThread = reinterpret_cast(p); return pThread->IsAlive(); } nnosThread* nnosThreadGetMainThread(void) { return reinterpret_cast(&nn::os::Thread::GetMainThread()); } }