1 /*---------------------------------------------------------------------------* 2 Project: Horizon 3 File: os_ManagedThread.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 <calloca> 17 #include <nn/Handle.h> 18 #include <nn/assert.h> 19 #include <nn/dbg.h> 20 #include <nn/math.h> 21 #include <nn/os.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 <nn/svc/svc_Stub.h> 26 #include <rt_fp.h> 27 28 #include "os_Limits.h" 29 30 //--------------------------------------------------------------------------- 31 32 using namespace nn; 33 using namespace fnd; 34 using namespace nn::svc; 35 using namespace nn::os; 36 37 #define ASSERT_INITIALIZE() \ 38 NN_TASSERTMSG_(GetStaticStates().mainThread.IsValid(), \ 39 "Please call ManagedThread::InitializeAndAttachMainThread before use of ManagedThread.") 40 41 namespace nn{ namespace os{ 42 43 ThreadLocalStorage ManagedThread::ms_ThreadStorage = ThreadLocalStorage::WithoutInitialize(); 44 ThreadLocalStorage ManagedThread::ms_IdStorage = ThreadLocalStorage::WithoutInitialize(); 45 46 47 48 namespace 49 { 50 typedef fnd::IntrusiveLinkedList<ManagedThread> ThreadList; 51 typedef CriticalSection LockType; 52 typedef CriticalSection::ScopedLock ScopedLock; 53 54 struct StaticStates 55 { 56 ManagedThread mainThread; 57 ThreadList threadList; 58 LockType lock; 59 bool isInitialized; 60 NN_PADDING3; 61 }; 62 63 bit8 s_StaticStatesBuffer[sizeof(StaticStates)] NN_ATTRIBUTE_ALIGN(8); 64 GetStaticStates()65 inline StaticStates& GetStaticStates() 66 { 67 return *reinterpret_cast<StaticStates*>(s_StaticStatesBuffer); 68 } 69 70 struct StackBufferAdapter 71 { 72 uptr stackBottom; StackBufferAdapternn::os::__anondfbbd4790111::StackBufferAdapter73 StackBufferAdapter(uptr stackBottom) : stackBottom(stackBottom) {} GetStackBottomnn::os::__anondfbbd4790111::StackBufferAdapter74 uptr GetStackBottom() const { return stackBottom; } 75 }; 76 AllocateOnStack(uptr * pStackBottom,size_t size)77 void* AllocateOnStack(uptr* pStackBottom, size_t size) 78 { 79 *pStackBottom -= size; 80 *pStackBottom = math::RoundDown(*pStackBottom, 8); 81 return reinterpret_cast<void*>(*pStackBottom); 82 } 83 84 #if NN_PLATFORM_HAS_MMU GetMainThreadStackSize()85 size_t GetMainThreadStackSize() 86 { 87 MemoryInfo mi; 88 PageInfo pi; 89 90 Result result = svc::QueryMemory(&mi, &pi, NN_OS_ADDR_STACK_END - 1); 91 NN_PANIC_IF_FAILED(result); 92 93 return mi.size; 94 } 95 #endif 96 } 97 98 99 /* ------------------------------------------------------------------------ 100 Thread Starter 101 ------------------------------------------------------------------------ */ 102 103 struct ManagedThread::StartParam 104 { 105 void (*destroy)(void* p); // 106 void (*invoke)(ThreadFunc f, const void* p); // 107 void (*f)(uptr); // 108 void* p; // 109 ManagedThread* pObj; 110 LightEvent* pStartEvent; 111 112 // Call handler. Invokenn::os::ManagedThread::StartParam113 void Invoke() 114 { 115 invoke(f, p); 116 }; 117 118 // End processing after handler call. Destroynn::os::ManagedThread::StartParam119 void Destroy() 120 { 121 destroy(p); 122 } 123 }; 124 NoParameterFunc(void (* f)())125 void ManagedThread::NoParameterFunc(void (*f)()) 126 { 127 f(); 128 } 129 130 // Thread process that runs from different context. Call handler. ThreadStart(StartParam * p)131 void ManagedThread::ThreadStart(StartParam* p) 132 { 133 StartParam& param = *p; 134 135 { 136 SetCurrentThread(param.pObj); 137 SetCurrentThreadId(Thread::GetCurrentId()); 138 param.pStartEvent->Wait(); 139 } 140 { 141 param.Invoke(); 142 param.Destroy(); 143 } 144 } 145 146 147 148 /* ------------------------------------------------------------------------ 149 Thread creation 150 ------------------------------------------------------------------------ */ 151 SetupStackAndParam(StartParam * pStartParam,const TypeInfo & typeInfo,ThreadFunc f,const void * p,uptr stackBottom,size_t stackSize)152 uptr ManagedThread::SetupStackAndParam(StartParam* pStartParam, const TypeInfo& typeInfo, ThreadFunc f, const void* p, uptr stackBottom, size_t stackSize) 153 { 154 // The stack requires 8-byte alignment. 155 // TODO: Strictly speaking, the files must be separated because this part is ARM-dependent. 156 uptr stack = stackBottom; 157 158 void* pLightEventBuffer = AllocateOnStack(&stack, sizeof(LightEvent)); 159 void* pCopiedParam = AllocateOnStack(&stack, typeInfo.size); 160 161 LightEvent* pStartEvent = new(pLightEventBuffer) LightEvent(false); 162 163 // Copy the parameter that is passed to the handler to the stack region. 164 typeInfo.copy(p, pCopiedParam); 165 166 // Write information that is passed to the thread to the stack region. 167 pStartParam->destroy = typeInfo.destroy; 168 pStartParam->invoke = typeInfo.invoke; 169 pStartParam->f = f; 170 pStartParam->p = pCopiedParam; 171 pStartParam->pObj = this; 172 pStartParam->pStartEvent = pStartEvent; 173 174 m_StackBottom = stack; 175 m_StackBufferEnd = stackBottom; 176 m_StackSize = stackSize; 177 m_pName = NULL; 178 m_pStartEvent = pStartEvent; 179 180 return stack; 181 } 182 TryInitializeImpl(const TypeInfo & typeInfo,ThreadFunc f,const void * p,uptr stackBottom,size_t stackSize,s32 priority,s32 coreNo)183 Result ManagedThread::TryInitializeImpl(const TypeInfo& typeInfo, ThreadFunc f, const void* p, uptr stackBottom, size_t stackSize, s32 priority, s32 coreNo) 184 { 185 return TryInitializeImpl( 186 typeInfo, 187 f, 188 p, 189 stackBottom, 190 stackSize, 191 priority, 192 coreNo, 193 false ); 194 } 195 TryInitializeImpl(const TypeInfo & typeInfo,ThreadFunc f,const void * p,uptr stackBottom,size_t stackSize,s32 priority,s32 coreNo,bool useAutoStack)196 Result ManagedThread::TryInitializeImpl(const TypeInfo& typeInfo, ThreadFunc f, const void* p, uptr stackBottom, size_t stackSize, s32 priority, s32 coreNo, bool useAutoStack) 197 { 198 ASSERT_INITIALIZE(); 199 200 StartParam param; 201 const uptr newStackBottom = SetupStackAndParam(¶m, typeInfo, f, p, stackBottom, stackSize); 202 203 StackBufferAdapter sba(newStackBottom); 204 Result result = Thread::ProtectedAccessor::TryStart<StartParam, StackBufferAdapter>( 205 &m_Thread, 206 ThreadStart, 207 param, 208 sba, 209 priority, 210 coreNo, 211 (useAutoStack ? stackBottom: NULL) ); 212 213 214 if( result.IsSuccess() ) 215 { 216 m_Id = m_Thread.GetId(); 217 Register(this); 218 } 219 else 220 { 221 if (result.GetSummary() != Result::SUMMARY_OUT_OF_RESOURCE) 222 { 223 NN_OS_ERROR_IF_FAILED(result); 224 } 225 } 226 227 return result; 228 } 229 TryInitializeImplUsingAutoStack(const TypeInfo & typeInfo,ThreadFunc f,const void * p,size_t stackSize,s32 priority,s32 coreNo)230 Result ManagedThread::TryInitializeImplUsingAutoStack(const TypeInfo& typeInfo, ThreadFunc f, const void* p, size_t stackSize, s32 priority, s32 coreNo) 231 { 232 ASSERT_INITIALIZE(); 233 234 const uptr stackBottom = Thread::ProtectedAccessor::PreStartUsingAutoStack(&m_Thread, stackSize); 235 Result result = TryInitializeImpl(typeInfo, f, p, stackBottom, stackSize, priority, coreNo, true); 236 return Thread::ProtectedAccessor::PostStartUsingAutoStack(&m_Thread, result, stackBottom); 237 } 238 239 240 241 242 /* ------------------------------------------------------------------------ 243 Destroy Thread 244 ------------------------------------------------------------------------ */ 245 246 247 248 /* ------------------------------------------------------------------------ 249 . 250 ------------------------------------------------------------------------ */ 251 Finalize()252 void ManagedThread::Finalize() 253 { 254 if( IsChained() ) 255 { 256 m_Thread.Finalize(); 257 Unregister(this); 258 } 259 } 260 Join()261 void ManagedThread::Join() 262 { 263 m_Thread.Join(); 264 } 265 Detach()266 void ManagedThread::Detach() 267 { 268 m_Thread.Detach(); 269 } 270 271 272 273 /* ------------------------------------------------------------------------ 274 . 275 ------------------------------------------------------------------------ */ 276 Start()277 void ManagedThread::Start() 278 { 279 NN_TASSERTMSG_(m_pStartEvent != NULL, 280 "ManagedThread is not initialized or this is the main thread."); 281 m_pStartEvent->Signal(); 282 } 283 GetStackBufferBegin() const284 uptr ManagedThread::GetStackBufferBegin() const 285 { 286 return m_StackBufferEnd - m_StackSize; 287 } 288 GetStackBottom() const289 uptr ManagedThread::GetStackBottom() const 290 { 291 return m_StackBottom; 292 } 293 GetStackBufferEnd() const294 uptr ManagedThread::GetStackBufferEnd() const 295 { 296 return m_StackBufferEnd; 297 } 298 GetStackSize() const299 size_t ManagedThread::GetStackSize() const 300 { 301 return GetStackBottom() - GetStackBufferBegin(); 302 } 303 GetStackBufferSize() const304 size_t ManagedThread::GetStackBufferSize() const 305 { 306 return m_StackSize; 307 } 308 GetName() const309 const char* ManagedThread::GetName() const 310 { 311 return m_pName; 312 } 313 SetName(const char8 * pName)314 void ManagedThread::SetName(const char8* pName) 315 { 316 m_pName = pName; 317 } 318 SetCurrentThread(ManagedThread * p)319 void ManagedThread::SetCurrentThread(ManagedThread* p) 320 { 321 ms_ThreadStorage.SetValue(reinterpret_cast<uptr>(p)); 322 } 323 SetCurrentThreadId(bit32 id)324 void ManagedThread::SetCurrentThreadId(bit32 id) 325 { 326 ms_IdStorage.SetValue(id); 327 } 328 GetCurrentThread()329 ManagedThread* ManagedThread::GetCurrentThread() 330 { 331 return reinterpret_cast<ManagedThread*>(ms_ThreadStorage.GetValue()); 332 } 333 FindByStackAddress(uptr address)334 ManagedThread* ManagedThread::FindByStackAddress(uptr address) 335 { 336 class Finder : public EnumerateCallback 337 { 338 private: 339 uptr m_Address; 340 ManagedThread* m_pFound; 341 342 public: 343 Finder(uptr address) : m_Address(address), m_pFound(NULL) {} 344 345 virtual bool operator()(ManagedThread* p) 346 { 347 if( (p->GetStackBufferBegin() <= m_Address) && (m_Address <= p->GetStackBufferEnd()) ) 348 { 349 m_pFound = p; 350 return false; 351 } 352 return true; 353 } 354 ManagedThread* GetFound() const { return m_pFound; } 355 356 } finder(address); 357 358 Enumerate(&finder); 359 360 return finder.GetFound(); 361 } 362 FindById(bit32 id)363 ManagedThread* ManagedThread::FindById(bit32 id) 364 { 365 class Finder : public EnumerateCallback 366 { 367 private: 368 bit32 m_Id; 369 ManagedThread* m_pFound; 370 371 public: 372 Finder(bit32 id) : m_Id(id), m_pFound(NULL) {} 373 374 virtual bool operator()(ManagedThread* p) 375 { 376 if( p->GetId() == m_Id ) 377 { 378 m_pFound = p; 379 return false; 380 } 381 return true; 382 } 383 ManagedThread* GetFound() const { return m_pFound; } 384 385 } finder(id); 386 387 Enumerate(&finder); 388 389 return finder.GetFound(); 390 } 391 Enumerate(EnumerateCallback * p)392 void ManagedThread::Enumerate(EnumerateCallback* p) 393 { 394 StaticStates& ss = GetStaticStates(); 395 ScopedLock lock(ss.lock); 396 397 ManagedThread* pThread = ss.threadList.GetFront(); 398 while( pThread != NULL ) 399 { 400 (*p)(pThread); 401 pThread = ss.threadList.GetNext(pThread); 402 } 403 } 404 Register(ManagedThread * p)405 void ManagedThread::Register(ManagedThread* p) 406 { 407 StaticStates& ss = GetStaticStates(); 408 ScopedLock lock(ss.lock); 409 ss.threadList.PushBack(p); 410 } 411 Unregister(ManagedThread * p)412 void ManagedThread::Unregister(ManagedThread* p) 413 { 414 StaticStates& ss = GetStaticStates(); 415 ScopedLock lock(ss.lock); 416 ss.threadList.Erase(p); 417 } 418 GetCurrentManagedCount()419 s32 ManagedThread::GetCurrentManagedCount() 420 { 421 class Counter : public EnumerateCallback 422 { 423 private: 424 s32 m_Count; 425 426 public: 427 Counter() : m_Count(0) {} 428 429 virtual bool operator()(ManagedThread* p) 430 { 431 NN_UNUSED_VAR(p); 432 m_Count++; 433 return true; 434 } 435 s32 GetCount() const { return m_Count; } 436 437 } counter; 438 439 Enumerate(&counter); 440 441 return counter.GetCount(); 442 } 443 444 #if NN_PLATFORM_HAS_MMU InitializeAsMainThread()445 void ManagedThread::InitializeAsMainThread() 446 { 447 Thread::ProtectedAccessor::InitializeAsMainThread(&m_Thread); 448 m_StackBottom = NN_OS_ADDR_STACK_END; 449 m_StackBufferEnd = NN_OS_ADDR_STACK_END; 450 m_StackSize = GetMainThreadStackSize(); 451 m_pName = "MainThread"; 452 m_pStartEvent = NULL; 453 m_Id = m_Thread.GetId(); 454 455 ms_ThreadStorage.SetValueTo( 456 os::detail::GetMainThreadThreadLocalRegion(), reinterpret_cast<uptr>(this)); 457 ms_IdStorage.SetValueTo( 458 os::detail::GetMainThreadThreadLocalRegion(), m_Id); 459 460 Register(this); 461 } 462 InitializeEnvironment()463 void ManagedThread::InitializeEnvironment() 464 { 465 new(s_StaticStatesBuffer) StaticStates(); 466 StaticStates& ss = GetStaticStates(); 467 468 ms_ThreadStorage.Initialize(); 469 ms_IdStorage.Initialize(); 470 471 ss.lock.Initialize(); 472 ss.mainThread.InitializeAsMainThread(); 473 ss.isInitialized = true; 474 } 475 IsEnabled()476 bool ManagedThread::IsEnabled() 477 { 478 return GetStaticStates().isInitialized; 479 } 480 #endif 481 482 483 484 485 486 487 488 489 490 491 }} // namespace nn::os 492 493 494