1#include "basis/seadNew.h"
2#include "basis/seadRawPrint.h"
3#include "thread/seadThread.h"
4
5namespace sead
6{
7Thread::Thread(const SafeString& name, Heap* heap, s32 priority, MessageQueue::BlockType block_type,
8 MessageQueue::Element quit_msg, s32 stack_size, s32 message_queue_size)
9 : INamable(name), mStackSize(stack_size), mBlockType(block_type), mQuitMsg(quit_msg),
10 mPriority(priority)
11{
12 mListNode.mData = this;
13 mMessageQueue.allocate(size: message_queue_size, heap);
14 mStackTop = new (heap, 0x1000) u8[stack_size];
15 // FIXME: ThreadType has the wrong size
16 mThreadInner = new (heap) nn::os::ThreadType;
17
18 const auto result =
19 nn::os::CreateThread(mThreadInner, ninThreadFunc_, arg: this, srcStack: mStackTop, stackSize: stack_size, priority: mPriority);
20
21 SEAD_ASSERT_MSG(result.IsSuccess(), "CreateThread failed. 0x%08x (module = %d, desc = %d) %s",
22 result.GetInnerValueForDebug(), result.GetModule(), result.GetDescription(),
23 name.cstr());
24
25 nn::os::SetThreadName(mThreadInner, threadName: name.cstr());
26
27 if (ThreadMgr::instance())
28 ThreadMgr::instance()->addThread_(thread: this);
29 else
30 SEAD_ASSERT_MSG(false, "ThreadMgr not initialized");
31}
32
33Thread::~Thread()
34{
35 if (!ThreadMgr::instance())
36 {
37 SEAD_ASSERT_MSG(false, "ThreadMgr not initialized");
38 return;
39 }
40
41 if (ThreadMgr::instance()->getMainThread() != this)
42 {
43 ThreadMgr::instance()->removeThread_(thread: this);
44
45 if (mState != State::cQuitting && mState != State::cTerminated)
46 {
47 if (mState == State::cRunning)
48 {
49 SEAD_ASSERT_MSG(false, "Thread is running. Do quit and waitDone");
50 quitAndWaitDoneSingleThread(is_jam: false);
51 }
52 }
53 else
54 {
55 SEAD_ASSERT_MSG(false, "Thread is not done. Do waitDone");
56 waitDone();
57 }
58
59 nn::os::DestroyThread(mThreadInner);
60
61 if (mThreadInner)
62 delete mThreadInner;
63
64 if (mStackTop)
65 delete[] static_cast<u8*>(mStackTop);
66 }
67
68 mMessageQueue.free();
69}
70
71bool Thread::start()
72{
73 if (mState)
74 {
75 SEAD_WARN("Thread is running or done. Can not start.\n");
76 return false;
77 }
78
79 nn::os::StartThread(mThreadInner);
80
81 const u32 id = nn::os::GetThreadId(thread: mThreadInner);
82 const int state = mState;
83 mId = id;
84
85 if (state == State::cInitialized)
86 mState = State::cRunning;
87
88 return true;
89}
90
91void Thread::waitDone()
92{
93 if ((mState.value() | State::cReleased) == State::cReleased)
94 return;
95
96 nn::os::WaitThread(mThreadInner);
97 SEAD_ASSERT_MSG(mState == State::cTerminated, "Join failed?");
98 mState = State::cReleased;
99}
100
101void Thread::setPriority(s32 prio)
102{
103 mPriority = prio;
104 if (isActive())
105 nn::os::ChangeThreadPriority(thread: mThreadInner, priority: prio);
106}
107
108s32 Thread::getPriority() const
109{
110 return mPriority;
111}
112
113void Thread::setAffinity(const CoreIdMask& affinity)
114{
115 mAffinity = affinity;
116 u64 mask = mAffinity;
117 const auto available_mask = nn::os::GetThreadAvailableCoreMask();
118 SEAD_ASSERT_MSG((~u32(available_mask) & mask) == 0, "invalid core mask. ( mask = %ld )", mask);
119 nn::os::SetThreadCoreMask(mThreadInner, -1, mask);
120}
121
122void Thread::yield()
123{
124 nn::os::YieldThread();
125}
126
127void Thread::sleep(TickSpan howLong)
128{
129 nn::os::SleepThread(nn::os::ConvertToTimeSpan(ticks: howLong.toTicks()));
130}
131
132uintptr_t Thread::getStackCheckStartAddress_() const
133{
134 return uintptr_t(mStackTopForCheck);
135}
136
137void Thread::ninThreadFunc_(void* arg)
138{
139 auto self = static_cast<Thread*>(arg);
140
141 ThreadMgr::instance()->mThreadPtrTLS.setValue(uintptr_t(self));
142
143 if (self->mAffinity.countOnBits() <= 1)
144 nn::os::SetTlsValue(slot: CoreInfo::getCoreNumberTlsSlot(),
145 value: int(nn::os::GetCurrentCoreNumber()) + 1);
146
147#ifdef SEAD_DEBUG
148 uintptr_t stack_addr;
149 nn::os::GetCurrentStackInfo(&stack_addr, nullptr);
150 self->mStackTopForCheck = reinterpret_cast<void*>(stack_addr);
151 self->initStackCheckWithCurrentStackPointer_();
152#endif
153
154 const u32 id = nn::os::GetThreadId(thread: self->mThreadInner);
155 self->mState = State::cRunning;
156 self->mId = id;
157 self->run_();
158 self->mState = State::cTerminated;
159}
160
161Thread::Thread(Heap* heap, nn::os::ThreadType* nn_thread, u32 thread_id)
162 : INamable("sead::MainThread"), mStackSize(nn_thread->_stackSize),
163 mBlockType(MessageQueue::BlockType::NonBlocking), mQuitMsg(0x7FFFFFFF), mId(thread_id),
164 mState(State::cRunning), mThreadInner(nn_thread),
165 mPriority(nn::os::GetThreadPriority(thread: nn_thread))
166{
167 mListNode.mData = this;
168 mMessageQueue.allocate(size: 32, heap);
169
170 uintptr_t stack_addr;
171 size_t stack_size;
172 nn::os::GetCurrentStackInfo(stack_addr: &stack_addr, stack_size: &stack_size);
173 mStackTopForCheck = reinterpret_cast<void*>(stack_addr);
174 mStackSize = s32(stack_size);
175
176#ifdef SEAD_DEBUG
177 initStackCheckWithCurrentStackPointer_();
178#endif
179
180 setAffinity(mAffinity);
181}
182
183ThreadMgr::ThreadMgr() = default;
184
185u32 ThreadMgr::getCurrentThreadID_()
186{
187 return u32(uintptr_t(nn::os::GetCurrentThread()));
188}
189
190void ThreadMgr::initMainThread_(Heap* heap)
191{
192 nn::os::ThreadType* nn_thread = nn::os::GetCurrentThread();
193 nn::os::ChangeThreadPriority(thread: nn::os::GetCurrentThread(), priority: 16);
194 const u64 nn_thread_id = nn::os::GetThreadId(thread: nn_thread);
195
196 auto thread = new (heap) MainThread(heap, nn_thread, nn_thread_id);
197 mMainThread = thread;
198 mThreadPtrTLS.setValue(uintptr_t(thread));
199
200 nn::os::SetTlsValue(slot: CoreInfo::getCoreNumberTlsSlot(), value: int(nn::os::GetCurrentCoreNumber()) + 1);
201}
202} // namespace sead
203