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