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(&param, 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