1#include "thread/seadThread.h"
2#include "basis/seadRawPrint.h"
3#include "prim/seadBitUtil.h"
4#include "prim/seadPtrUtil.h"
5#include "prim/seadScopedLock.h"
6#include "thread/seadThreadUtil.h"
7
8namespace sead
9{
10const s32 Thread::cDefaultPriority = 0x10;
11
12bool Thread::sendMessage(MessageQueue::Element msg, MessageQueue::BlockType block_type)
13{
14 if (msg == MessageQueue::cNullElement)
15 {
16 SEAD_ASSERT_MSG(false, "Can not send cNullElement(==%ld)", MessageQueue::cNullElement);
17 return false;
18 }
19
20 if (isDone())
21 {
22 SEAD_ASSERT_MSG(false, "Thread is done. Reject message: %ld", msg);
23 return false;
24 }
25
26 if (mQuitMsg == msg)
27 {
28 SEAD_ASSERT_MSG(false, "use quit()");
29 return false;
30 }
31
32 return mMessageQueue.push(message: msg, block_type);
33}
34
35MessageQueue::Element Thread::recvMessage(MessageQueue::BlockType block_type)
36{
37 if (mState == State::cQuitting)
38 return 0;
39 return mMessageQueue.pop(block_type);
40}
41
42void Thread::quit(bool is_jam)
43{
44 if (isDone())
45 {
46 SEAD_WARN("Thread is done. Can not quit.");
47 return;
48 }
49
50 if (mState == State::cRunning)
51 mState = State::cQuitting;
52
53 if (is_jam)
54 mMessageQueue.jam(message: mQuitMsg, block_type: MessageQueue::BlockType::Blocking);
55 else
56 mMessageQueue.push(message: mQuitMsg, block_type: MessageQueue::BlockType::Blocking);
57}
58
59void Thread::quitAndWaitDoneSingleThread(bool is_jam)
60{
61 quit(is_jam);
62 waitDone();
63}
64
65constexpr u32 cStackCanaryMagic = 0x5EAD5CEC;
66
67static bool checkStackMagic(uintptr_t addr)
68{
69 return BitUtil::bitCastPtr<u32>(value: reinterpret_cast<const void*>(addr)) == cStackCanaryMagic;
70}
71
72s32 Thread::calcStackUsedSizePeak() const
73{
74#ifdef SEAD_DEBUG
75 // FIXME
76 return 0;
77#else
78 return 0;
79#endif
80}
81
82void Thread::checkStackOverFlow(const char* source_file, s32 source_line) const
83{
84 checkStackPointerOverFlow(source_file, source_line);
85 checkStackEndCorruption(source_file, source_line);
86}
87
88void Thread::checkStackEndCorruption(const char* source_file, s32 source_line) const
89{
90 if (ThreadMgr::instance()->getMainThread() == this)
91 return;
92
93 const uintptr_t start = getStackCheckStartAddress_();
94 if (!start)
95 return;
96
97 SEAD_ASSERT_MSG(checkStackMagic(start),
98 "sead::Thread Stack End Corruption! [%s:%p]\n"
99 " Source File: %s\n"
100 " Line Number: %d\n"
101 " Stack Size: %d",
102 getName().cstr(), this,
103 source_file ? source_file : SafeString::cEmptyString.cstr(), source_line,
104 getStackSize());
105}
106
107void Thread::checkStackPointerOverFlow(const char* source_file, s32 source_line) const
108{
109 if (!ThreadMgr::instance() || ThreadMgr::instance()->getCurrentThread() != this)
110 {
111 SEAD_WARN("sead::Thread::checkStackPointerOverFlow cannot be called from other thread.");
112 return;
113 }
114
115 const uintptr_t ptr = ThreadUtil::GetCurrentStackPointer();
116 const uintptr_t start = getStackCheckStartAddress_();
117 if (start)
118 {
119 SEAD_ASSERT_MSG(start <= ptr,
120 "sead::Thread Stack Pointer Overflow! [%s:%p]\n"
121 " Source File: %s\n"
122 " Line Number: %d\n"
123 " Stack Size: %d, Over Size: %ld",
124 getName().cstr(), this,
125 source_file ? source_file : SafeString::cEmptyString.cstr(), source_line,
126 getStackSize(), start - ptr);
127 }
128 else
129 {
130 ThreadMgr::instance()->getCurrentThread();
131 }
132}
133
134void Thread::setStackOverflowExceptionEnable(bool)
135{
136 SEAD_WARN("This platform cannot set stack overflow exception.");
137}
138
139void Thread::run_()
140{
141 while (true)
142 {
143#ifdef SEAD_DEBUG
144 checkStackOverFlow(nullptr, 0);
145#endif
146
147 const MessageQueue::Element msg = mMessageQueue.pop(block_type: mBlockType);
148 if (msg == mQuitMsg)
149 break;
150
151 calc_(msg);
152 }
153}
154
155// NON_MATCHING: the first loop gets unrolled and the loop counter is not negated
156void Thread::initStackCheck_()
157{
158 void* const start = reinterpret_cast<void*>(getStackCheckStartAddress_());
159 void* const end = PtrUtil::addOffset(ptr: mStackTopForCheck, offset: mStackSize);
160 u32* addr = static_cast<u32*>(start);
161
162 if (start >= end)
163 return;
164
165 const size_t len = uintptr_t(end) + (-uintptr_t(start) - 1);
166
167 for (u32 i = 0; i < ((len / 4 + 1) % 8); ++i)
168 *addr++ = cStackCanaryMagic;
169
170 if (len >= 0x1C)
171 {
172 do
173 {
174 for (s32 i = 0; i < 8; ++i)
175 *addr++ = cStackCanaryMagic;
176 } while (addr < end);
177 }
178}
179
180// NON_MATCHING: see Thread::initStackCheck_
181void Thread::initStackCheckWithCurrentStackPointer_()
182{
183 void* const start = reinterpret_cast<void*>(getStackCheckStartAddress_());
184 void* const end = reinterpret_cast<void*>(ThreadUtil::GetCurrentStackPointer());
185 u32* addr = static_cast<u32*>(start);
186
187 if (start >= end)
188 return;
189
190 const size_t len = uintptr_t(end) + (-uintptr_t(start) - 1);
191
192 for (u32 i = 0; i < ((len / 4 + 1) % 8); ++i)
193 *addr++ = cStackCanaryMagic;
194
195 if (len >= 0x1C)
196 {
197 do
198 {
199 for (s32 i = 0; i < 8; ++i)
200 *addr++ = cStackCanaryMagic;
201 } while (addr < end);
202 }
203}
204
205SEAD_SINGLETON_DISPOSER_IMPL(ThreadMgr)
206
207ThreadMgr::~ThreadMgr()
208{
209 ScopedLock<CriticalSection> lock(getListCS());
210
211 for (Thread* thread : mList)
212 thread->quit(is_jam: false);
213
214 bool all_done;
215 do
216 {
217 all_done = true;
218 for (Thread* thread : mList)
219 all_done &= thread->isDone();
220 Thread::yield();
221 } while (!all_done);
222
223 for (Thread* thread : mList)
224 thread->waitDone();
225
226 sInstance = nullptr;
227}
228
229void ThreadMgr::initialize(Heap* heap)
230{
231 initMainThread_(heap);
232 SEAD_ASSERT(mMainThread);
233}
234
235void ThreadMgr::destroy()
236{
237 destroyMainThread_();
238}
239
240void ThreadMgr::destroyMainThread_()
241{
242 if (mMainThread)
243 {
244 delete mMainThread;
245 mMainThread = nullptr;
246 }
247}
248
249bool ThreadMgr::isMainThread() const
250{
251 return getCurrentThread() == mMainThread;
252}
253
254void ThreadMgr::waitDoneMultipleThread(Thread* const* threads, s32 num)
255{
256 bool all_done;
257 do
258 {
259 all_done = true;
260 for (s32 i = 0; i < num; ++i)
261 all_done &= threads[i]->isDone();
262 Thread::yield();
263 } while (!all_done);
264
265 for (s32 i = 0; i < num; ++i)
266 threads[i]->waitDone();
267}
268
269void ThreadMgr::quitAndWaitDoneMultipleThread(Thread** threads, s32 num, bool is_jam)
270{
271 for (s32 i = 0; i < num; ++i)
272 threads[i]->quit(is_jam);
273
274 waitDoneMultipleThread(threads, num);
275}
276
277void ThreadMgr::checkCurrentThreadStackOverFlow(const char* source_file, s32 source_line)
278{
279 if (!ThreadMgr::instance())
280 return;
281 if (Thread* thread = ThreadMgr::instance()->getCurrentThread())
282 thread->checkStackOverFlow(source_file, source_line);
283}
284
285void ThreadMgr::checkCurrentThreadStackEndCorruption(const char* source_file, s32 source_line)
286{
287 if (!ThreadMgr::instance())
288 return;
289 if (Thread* thread = ThreadMgr::instance()->getCurrentThread())
290 thread->checkStackEndCorruption(source_file, source_line);
291}
292
293void ThreadMgr::checkCurrentThreadStackPointerOverFlow(const char* source_file, s32 source_line)
294{
295 if (!ThreadMgr::instance())
296 return;
297 if (Thread* thread = ThreadMgr::instance()->getCurrentThread())
298 thread->checkStackPointerOverFlow(source_file, source_line);
299}
300} // namespace sead
301