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: 29304 $
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/err.h>
23 //---------------------------------------------------------------------------
24 
25 using namespace nn;
26 using namespace fnd;
27 using namespace nn::svc;
28 using namespace nn::os;
29 
30 namespace nn{ namespace os{
31 
32     namespace
33     {
34         struct StackBufferAdapter
35         {
36             uptr stackBottom;
StackBufferAdapternn::os::__anonb22adbb20111::StackBufferAdapter37             StackBufferAdapter(uptr stackBottom) : stackBottom(stackBottom) {}
GetStackBottomnn::os::__anonb22adbb20111::StackBufferAdapter38             uptr GetStackBottom() const { return stackBottom; }
39         };
40     }
41 
42     namespace detail
43     {
ConvertSvcToLibraryPriority(s32 svc)44         s32 ConvertSvcToLibraryPriority(s32 svc)
45         {
46             if( svc >= SVC_USER_THREAD_PRIORITY_HIGHEST )
47             {
48                 const s32 offset = svc - SVC_USER_THREAD_PRIORITY_HIGHEST;
49                 return offset;
50             }
51             else if( svc >= SVC_LIBRARY_THREAD_PRIORITY_HIGHEST )
52             {
53                 const s32 offset = svc - SVC_LIBRARY_THREAD_PRIORITY_HIGHEST;
54                 return LIBRARY_THREAD_PRIORITY_BASE + offset;
55             }
56             else
57             {
58                 return PRIVILEGED_THREAD_PRIORITY_BASE + svc;
59             }
60         }
ConvertLibraryToSvcPriority(s32 lib)61         s32 ConvertLibraryToSvcPriority(s32 lib)
62         {
63             if ( (USER_THREAD_PRIORITY_HIGHEST <= lib)
64                                               && (lib <= USER_THREAD_PRIORITY_LOWEST) )
65             {
66                 return SVC_USER_THREAD_PRIORITY_HIGHEST + lib;
67             }
68             if ( (LIBRARY_THREAD_PRIORITY_HIGHEST <= lib)
69                                                  && (lib <= LIBRARY_THREAD_PRIORITY_LOWEST) )
70             {
71                 const s32 offset = lib - LIBRARY_THREAD_PRIORITY_HIGHEST;
72                 return SVC_LIBRARY_THREAD_PRIORITY_HIGHEST + offset;
73             }
74             if ( (PRIVILEGED_THREAD_PRIORITY_HIGHEST <= lib)
75                                                     && (lib <= PRIVILEGED_THREAD_PRIORITY_LOWEST) )
76             {
77                 const s32 offset = lib - PRIVILEGED_THREAD_PRIORITY_HIGHEST;
78                 return SVC_PRIVILEGED_THREAD_PRIORITY_HIGHEST + offset;
79             }
80             return -1;
81         }
82     }
83 
84 
85 
86 
87 /*!
88     @brief    スレッドに渡す構造体です。
89  */
90 struct Thread::FunctionInfo
91 {
92     void (*destroy)(void* p);                    //!< 終了処理関数
93     void (*invoke)(ThreadFunc f, const void* p); //!< ハンドラを呼び出す関数
94     void (*f)(uptr);                             //!< ハンドラ
95     void* p;                                     //!< ハンドラに渡すパラメータ
96     void* pStackBottom;
97     bool  isAutoStack;
98     NN_PADDING3;
99 
100     // ハンドラを呼び出します。
Invokenn::os::Thread::FunctionInfo101     void Invoke()
102     {
103         invoke(f, p);
104     };
105 
106     // ハンドラ呼出し後の終了処理です。
Destroynn::os::Thread::FunctionInfo107     void Destroy()
108     {
109         destroy(p);
110     }
111 };
112 
113 
114 
115 Thread Thread::s_MainThread = Thread::InitializeAsCurrentTag();
116 Thread::AutoStackManager*    Thread::s_pAutoStackManager = NULL;
117 
118 
119 
120 
121 
122 // スレッドの初期化時・終了時に何か処理が必要な場合はここに記述する。
123 // データの受け渡しには TLS が必要かもしれない。
124 
OnThreadStart()125 inline void Thread::OnThreadStart()
126 {
127 #ifdef NN_PROCESSOR_ARM_V6
128     #if ! defined(NN_HARDWARE_CTR_LEGACY)
129     ThreadLocalStorage::ClearAllSlots();
130     #endif
131 #endif
132 }
133 
OnThreadExit()134 inline void Thread::OnThreadExit()
135 {
136     // nop
137 }
138 
NoParameterFunc(void (* f)())139 void Thread::NoParameterFunc(void (*f)())
140 {
141     f();
142 }
143 
144 #include <nn/hw/ARM/code32.h>
CallDestructorAndExit(void * pStackBottom NN_IS_UNUSED_VAR)145 asm void Thread::CallDestructorAndExit(void* pStackBottom NN_IS_UNUSED_VAR)
146 {
147     // r0: pStackBottom
148 
149     // スタックデストラクタを呼ぶ
150     // (s_pAutoStackManager->*f)(pStackBottom, false);
151 
152     mov     r2, #0                                  // false を第 2 引数に
153     mov     r1, r0                                  // pStackBottom を第 1 引数に
154 
155     ldr     r0, =__cpp(&s_pAutoStackManager)        // s_pAutoStackManager のアドレスを取得
156     ldr     r0, [r0, #0]                            // s_pAutoStackManager をロード
157 
158     ldr     r3, [r0, #0]                            // vtable のアドレスをロード
159     ldr     r3, [r3, #4]                            // vtable から仮想関数のアドレスを取得
160 
161     ldr     lr, =__cpp(nn::svc::ExitThread)         // 返り先を ExitThread に
162     bx      r3
163 
164     LTORG
165 }
166 #include <nn/hw/ARM/codereset.h>
167 
168 // 別コンテキストから実行されるスレッド処理です。ハンドラを呼び出します。
ThreadStart(uptr p)169 void Thread::ThreadStart(uptr p)
170 {
171     FunctionInfo& info = *reinterpret_cast<FunctionInfo*>(p);
172 
173     OnThreadStart();
174     info.Invoke();
175     info.Destroy();
176     OnThreadExit();
177 
178     if( info.isAutoStack )
179     {
180         CallDestructorAndExit(info.pStackBottom);
181     }
182 
183     nn::svc::ExitThread();
184 
185     NN_TASSERT_(0);
186 }
187 
TryInitializeAndStartImpl(const TypeInfo & typeInfo,ThreadFunc f,const void * p,uptr stackBottom,s32 priority,s32 coreNo,bool isAutoStack)188 Result Thread::TryInitializeAndStartImpl(const TypeInfo& typeInfo, ThreadFunc f, const void* p, uptr stackBottom, s32 priority, s32 coreNo, bool isAutoStack)
189 {
190     // Thread::Start -> CreateThread ---ここから別コンテキスト---> ThreadStart -> f の順に呼ばれる
191 
192     // stack は 8 バイトアライメントを要求する。
193     // TODO: 厳密にはこの部分は ARM 依存であるため、ファイルを分ける必要がある。
194     uptr stack = stackBottom;
195 
196     // ハンドラに渡すパラメータをスタック領域にコピーします。
197     stack -= typeInfo.size;
198     stack &= 0xfffffff8;
199     void* obj = reinterpret_cast<void*>(stack);
200     typeInfo.copy(p, obj);
201 
202     // スレッドに渡すに情報をスタック領域に書き込みます。
203     stack -= sizeof(FunctionInfo);
204     stack &= 0xfffffff8;
205     FunctionInfo& info = *reinterpret_cast<FunctionInfo*>(stack);
206     info.destroy = typeInfo.destroy;
207     info.invoke = typeInfo.invoke;
208     info.f = f;
209     info.p = obj;
210     info.isAutoStack = isAutoStack;
211     info.pStackBottom = reinterpret_cast<void*>(stackBottom);
212 
213     Handle handle;
214     NN_UTIL_RETURN_IF_FAILED(
215         nn::svc::CreateThread(
216             &handle,
217             ThreadStart,
218             stack,
219             stack,
220             os::detail::ConvertLibraryToSvcPriority(priority),
221             coreNo) );
222 
223     this->SetHandle(handle);
224     this->m_CanFinalize = false;
225     this->m_UsingAutoStack = false;
226     return ResultSuccess();
227 }
228 
229 
SleepImpl(nn::fnd::TimeSpan span)230 void Thread::SleepImpl(nn::fnd::TimeSpan span)
231 {
232     if( span.GetNanoSeconds() >= SLEEP_SPIN_THRESHOLD )
233     {
234         nn::svc::SleepThread(span.GetNanoSeconds());
235     }
236     else
237     {
238         SpinWaitCpuCycles(Tick(span));
239     }
240 }
241 
242 
243 #if NN_PLATFORM_HAS_MMU
TryInitializeAndStartImplUsingAutoStack(const TypeInfo & typeInfo,ThreadFunc f,const void * p,size_t stackSize,s32 priority,s32 coreNo)244 Result Thread::TryInitializeAndStartImplUsingAutoStack(const TypeInfo& typeInfo, ThreadFunc f, const void* p, size_t stackSize, s32 priority, s32 coreNo)
245 {
246     // Thread::Start -> CreateThread ---ここから別コンテキスト---> ThreadStart -> f の順に呼ばれる
247     NN_NULL_TASSERT_(s_pAutoStackManager);
248 
249     void* pStackBottom = s_pAutoStackManager->Construct(stackSize);
250     uptr stack = reinterpret_cast<uptr>(pStackBottom);
251 
252     Result result = TryInitializeAndStartImpl(typeInfo, f, p, stack, priority, coreNo, true);
253 
254     if (result.IsFailure())
255     {
256         s_pAutoStackManager->Destruct(pStackBottom, true);
257         return result;
258     }
259 
260     this->m_UsingAutoStack = true;
261     return ResultSuccess();
262 }
263 #endif  // if NN_PLATFORM_HAS_MMU
264 
265 // メインスレッド用
Thread(const Thread::InitializeAsCurrentTag &)266 Thread::Thread(const Thread::InitializeAsCurrentTag&)
267 {
268     Handle handle;
269     NN_ERR_THROW_FATAL(nn::svc::DuplicateHandle(&handle, PSEUDO_HANDLE_CURRENT_THREAD));
270     this->SetHandle(handle);
271     this->m_CanFinalize = false;
272     this->m_UsingAutoStack = false;
273 }
274 
SetAutoStackManager(AutoStackManager * pManager)275 void Thread::SetAutoStackManager(AutoStackManager* pManager)
276 {
277     s_pAutoStackManager = pManager;
278 }
279 
280 }} // namespace nn::os
281 
282 
283 
284 
285 #include <new>
286 using namespace nn::os;
287 
288 extern "C" {
289 
nnosThreadInitializeAndStart(nnosThread * p,void (* f)(uptr),uptr param,uptr stackBottom,s32 priority,s32 coreNo)290 void nnosThreadInitializeAndStart(nnosThread* p, void (*f)(uptr), uptr param, uptr stackBottom, s32 priority, s32 coreNo)
291 {
292     Thread* pThread = new (p) Thread();
293     StackBufferAdapter stack(stackBottom);
294     pThread->Start(f, param, stack, priority, coreNo);
295 }
296 
nnosThreadTryInitializeAndStart(nnosThread * p,void (* f)(uptr),uptr param,uptr stackBottom,s32 priority,s32 coreNo)297 bool nnosThreadTryInitializeAndStart(nnosThread* p, void (*f)(uptr), uptr param, uptr stackBottom, s32 priority, s32 coreNo)
298 {
299     Thread* pThread = new (p) Thread();
300     StackBufferAdapter stack(stackBottom);
301     Result result = pThread->TryStart(f, param, stack, priority, coreNo);
302     return result.IsSuccess();
303 }
304 
nnosThreadFinalize(nnosThread * p)305 void nnosThreadFinalize(nnosThread* p)
306 {
307     Thread* pThread = reinterpret_cast<Thread*>(p);
308     pThread->~Thread();
309 }
310 
nnosThreadJoin(nnosThread * p)311 void nnosThreadJoin(nnosThread* p)
312 {
313     Thread* pThread = reinterpret_cast<Thread*>(p);
314     pThread->Join();
315 }
316 
nnosThreadSleep(s64 nanoSeconds)317 void nnosThreadSleep(s64 nanoSeconds)
318 {
319     Thread::Sleep(TimeSpan::FromNanoSeconds(nanoSeconds));
320 }
321 
nnosThreadYield()322 void nnosThreadYield()
323 {
324     Thread::Yield();
325 }
326 
nnosThreadGetCurrentId(void)327 bit32 nnosThreadGetCurrentId(void)
328 {
329     return Thread::GetCurrentId();
330 }
331 
nnosThreadGetPriority(const nnosThread * p)332 s32 nnosThreadGetPriority(const nnosThread* p)
333 {
334     const Thread* pThread = reinterpret_cast<const Thread*>(p);
335     return pThread->GetPriority();
336 }
337 
nnosThreadGetCurrentPriority()338 s32 nnosThreadGetCurrentPriority()
339 {
340     return Thread::GetCurrentPriority();
341 }
342 
nnosThreadChangePriority(nnosThread * p,s32 priority)343 void nnosThreadChangePriority(nnosThread* p, s32 priority)
344 {
345     Thread* pThread = reinterpret_cast<Thread*>(p);
346     return pThread->ChangePriority(priority);
347 }
348 
nnosThreadChangeCurrentPriority(s32 priority)349 void nnosThreadChangeCurrentPriority(s32 priority)
350 {
351     Thread::ChangeCurrentPriority(priority);
352 }
353 
354 // スレッド Affinity の設定・取得
nnosThreadGetAffinityMask(const nnosThread * p,bit8 * pAffinityMask,s32 numProcessor)355 void nnosThreadGetAffinityMask(const nnosThread* p, bit8* pAffinityMask, s32 numProcessor)
356 {
357     const Thread* pThread = reinterpret_cast<const Thread*>(p);
358     pThread->GetAffinityMask(pAffinityMask, numProcessor);
359 }
360 
nnosThreadGetCurrentAffinityMask(bit8 * pAffinityMask,s32 numProcessor)361 void nnosThreadGetCurrentAffinityMask(bit8* pAffinityMask, s32 numProcessor)
362 {
363     Thread::GetCurrentAffinityMask(pAffinityMask, numProcessor);
364 }
365 
nnosThreadGetDefaultAffinityMask(bit8 * pAffinityMask,s32 numProcessor)366 void nnosThreadGetDefaultAffinityMask(bit8* pAffinityMask, s32 numProcessor)
367 {
368     Thread::GetDefaultAffinityMask(pAffinityMask, numProcessor);
369 }
370 
nnosThreadChangeAffinityMask(nnosThread * p,const bit8 * pAffinityMask,s32 numProcessor)371 void nnosThreadChangeAffinityMask(nnosThread* p, const bit8* pAffinityMask, s32 numProcessor)
372 {
373     Thread* pThread = reinterpret_cast<Thread*>(p);
374     pThread->ChangeAffinityMask(pAffinityMask, numProcessor);
375 }
376 
nnosThreadChangeCurrentAffinityMask(const bit8 * pAffinityMask,s32 numProcessor)377 void nnosThreadChangeCurrentAffinityMask(const bit8* pAffinityMask, s32 numProcessor)
378 {
379     Thread::ChangeCurrentAffinityMask(pAffinityMask, numProcessor);
380 }
381 
nnosThreadSetDefaultAffinityMask(const bit8 * pAffinityMask,s32 numProcessor)382 void nnosThreadSetDefaultAffinityMask(const bit8* pAffinityMask, s32 numProcessor)
383 {
384     Thread::SetDefaultAffinityMask(pAffinityMask, numProcessor);
385 }
386 
387 // IdealProcessor の取得・設定
nnosThreadGetIdealProcessor(const nnosThread * p)388 s32 nnosThreadGetIdealProcessor(const nnosThread* p)
389 {
390     const Thread* pThread = reinterpret_cast<const Thread*>(p);
391     return pThread->GetIdealProcessor();
392 }
393 
nnosThreadGetCurrentIdealProcessor()394 s32 nnosThreadGetCurrentIdealProcessor()
395 {
396     return Thread::GetCurrentIdealProcessor();
397 }
398 
nnosThreadGetDefaultIdealProcessor()399 s32 nnosThreadGetDefaultIdealProcessor()
400 {
401     return Thread::GetDefaultIdealProcessor();
402 }
403 
nnosThreadChangeIdealProcessor(nnosThread * p,s32 coreNo)404 void nnosThreadChangeIdealProcessor(nnosThread* p, s32 coreNo)
405 {
406     Thread* pThread = reinterpret_cast<Thread*>(p);
407     pThread->ChangeIdealProcessor(coreNo);
408 }
409 
nnosThreadChangeCurrentIdealProcessor(s32 coreNo)410 void nnosThreadChangeCurrentIdealProcessor(s32 coreNo)
411 {
412     Thread::ChangeCurrentIdealProcessor(coreNo);
413 }
414 
nnosThreadSetDefaultIdealProcessor(s32 coreNo)415 void nnosThreadSetDefaultIdealProcessor(s32 coreNo)
416 {
417     Thread::SetDefaultIdealProcessor(coreNo);
418 }
419 
nnosThreadGetCurrentProcessorNumber()420 s32 nnosThreadGetCurrentProcessorNumber()
421 {
422     return Thread::GetCurrentProcessorNumber();
423 }
424 
nnosThreadGetId(nnosThread * p)425 bit32 nnosThreadGetId(nnosThread* p)
426 {
427     Thread* pThread = reinterpret_cast<Thread*>(p);
428     return pThread->GetId();
429 }
430 
nnosThreadIsAlive(nnosThread * p)431 bool nnosThreadIsAlive(nnosThread* p)
432 {
433     Thread* pThread = reinterpret_cast<Thread*>(p);
434     return pThread->IsAlive();
435 }
436 
nnosThreadGetMainThread(void)437 nnosThread* nnosThreadGetMainThread(void)
438 {
439     return reinterpret_cast<nnosThread*>(&nn::os::Thread::GetMainThread());
440 }
441 
442 }
443