/*---------------------------------------------------------------------------* 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: 24755 $ *---------------------------------------------------------------------------*/ #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; } } /*! @brief スレッドに渡す構造体です。 */ 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; // ハンドラを呼び出します。 void Invoke() { invoke(f, p); }; // ハンドラ呼出し後の終了処理です。 void Destroy() { destroy(p); } }; Thread Thread::s_MainThread = Thread::InitializeAsCurrentTag(); Thread::AutoStackManager* Thread::s_pAutoStackManager = NULL; // スレッドの初期化時・終了時に何か処理が必要な場合はここに記述する。 // データの受け渡しには TLS が必要かもしれない。 inline void Thread::OnThreadStart() { #ifdef NN_PROCESSOR_ARM_V6 #if ! defined(NN_HARDWARE_CTR_LEGACY) ThreadLocalStorage::ClearAllSlots(); #endif #endif } inline void Thread::OnThreadExit() { // nop } void Thread::NoParameterFunc(void (*f)()) { f(); } #include asm void Thread::CallDestructorAndExit(void* pStackBottom NN_IS_UNUSED_VAR) { // r0: pStackBottom // スタックデストラクタを呼ぶ // (s_pAutoStackManager->*f)(pStackBottom, false); mov r2, #0 // false を第 2 引数に mov r1, r0 // pStackBottom を第 1 引数に ldr r0, =__cpp(&s_pAutoStackManager) // s_pAutoStackManager のアドレスを取得 ldr r0, [r0, #0] // s_pAutoStackManager をロード ldr r3, [r0, #0] // vtable のアドレスをロード ldr r3, [r3, #4] // vtable から仮想関数のアドレスを取得 ldr lr, =__cpp(nn::svc::ExitThread) // 返り先を ExitThread に bx r3 LTORG } #include // 別コンテキストから実行されるスレッド処理です。ハンドラを呼び出します。 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) { // Thread::Start -> CreateThread ---ここから別コンテキスト---> ThreadStart -> f の順に呼ばれる // stack は 8 バイトアライメントを要求する。 // TODO: 厳密にはこの部分は ARM 依存であるため、ファイルを分ける必要がある。 uptr stack = stackBottom; // ハンドラに渡すパラメータをスタック領域にコピーします。 stack -= typeInfo.size; stack &= 0xfffffff8; void* obj = reinterpret_cast(stack); typeInfo.copy(p, obj); // スレッドに渡すに情報をスタック領域に書き込みます。 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) { // Thread::Start -> CreateThread ---ここから別コンテキスト---> 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 // メインスレッド用 Thread::Thread(const Thread::InitializeAsCurrentTag&) { Handle handle; NN_UTIL_PANIC_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; } }} // 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); } // スレッド 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); } // 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()); } }