1#pragma once
2
3#include <utility>
4#ifdef NNSDK
5#include <nn/os.h>
6#endif
7
8#include <basis/seadRawPrint.h>
9#include <container/seadTList.h>
10#include <heap/seadDisposer.h>
11#include <heap/seadHeapMgr.h>
12#include <hostio/seadHostIONode.h>
13#include <hostio/seadHostIOReflexible.h>
14#include <mc/seadCoreInfo.h>
15#include <prim/seadEnum.h>
16#include <prim/seadNamable.h>
17#include <prim/seadSafeString.h>
18#include <prim/seadScopedLock.h>
19#include <thread/seadMessageQueue.h>
20#include <thread/seadThreadLocalStorage.h>
21#include <time/seadTickSpan.h>
22
23namespace sead
24{
25class Heap;
26class Thread;
27
28using ThreadList = TList<Thread*>;
29using ThreadListNode = TListNode<Thread*>;
30
31class Thread : public IDisposer, public INamable, public hostio::Reflexible
32{
33public:
34 SEAD_ENUM(State, cInitialized, cRunning, cQuitting, cTerminated, cReleased)
35
36 Thread(const SafeString& name, Heap* heap, s32 priority, MessageQueue::BlockType block_type,
37 MessageQueue::Element quit_msg, s32 stack_size, s32 message_queue_size);
38 ~Thread() override;
39
40 Thread(const Thread&) = delete;
41 Thread& operator=(const Thread&) = delete;
42
43 virtual void destroy() { waitDone(); }
44
45 virtual bool sendMessage(MessageQueue::Element msg, MessageQueue::BlockType block_type);
46 virtual MessageQueue::Element recvMessage(MessageQueue::BlockType block_type);
47 virtual const MessageQueue& getMessageQueue() const { return mMessageQueue; }
48
49 virtual bool start();
50 virtual void quit(bool is_jam);
51 virtual void waitDone();
52 virtual void quitAndDestroySingleThread(bool is_jam) { quitAndWaitDoneSingleThread(is_jam); }
53 virtual void quitAndWaitDoneSingleThread(bool is_jam);
54
55 virtual void setPriority(s32 prio);
56 virtual s32 getPriority() const;
57 virtual MessageQueue::BlockType getBlockType() const { return mBlockType; }
58 virtual s32 getStackSize() const { return mStackSize; }
59 virtual s32 calcStackUsedSizePeak() const;
60
61 u32 getId() const { return mId; }
62 State getState() const { return mState; }
63 bool isDone() const { return mState == State::cTerminated || mState == State::cReleased; }
64 bool isActive() const { return mState == State::cRunning || mState == State::cQuitting; }
65
66 const CoreIdMask& getAffinity() const { return mAffinity; }
67 void setAffinity(const CoreIdMask& affinity);
68
69 static void yield();
70 static void sleep(TickSpan howLong);
71
72 void checkStackOverFlow(const char* source_file, s32 source_line) const;
73 void checkStackEndCorruption(const char* source_file, s32 source_line) const;
74 void checkStackPointerOverFlow(const char* source_file, s32 source_line) const;
75 void setStackOverflowExceptionEnable(bool);
76
77 ThreadListNode* getThreadListNode() { return &mListNode; }
78
79#ifdef SEAD_DEBUG
80 void listenPropertyEvent(const hostio::PropertyEvent* event) override;
81 void genMessage(hostio::Context* context) override;
82#endif
83
84 bool isDefaultPriority() const { return getPriority() == cDefaultPriority; }
85
86 Heap* getCurrentHeap() const { return mCurrentHeap; }
87 Heap* setCurrentHeap(Heap* heap) { return std::exchange(obj&: mCurrentHeap, new_value&: heap); }
88 FindContainHeapCache* getFindContainHeapCache() { return &mFindContainHeapCache; }
89
90 static const s32 cDefaultPriority;
91
92protected:
93#ifdef NNSDK
94 Thread(Heap* heap, nn::os::ThreadType*, u32);
95#endif
96
97 virtual void run_();
98 virtual void calc_(MessageQueue::Element msg) = 0;
99 virtual uintptr_t getStackCheckStartAddress_() const;
100
101 void initStackCheck_();
102 void initStackCheckWithCurrentStackPointer_();
103
104#ifdef NNSDK
105 static void ninThreadFunc_(void*);
106#endif
107
108 MessageQueue mMessageQueue;
109 s32 mStackSize = 0;
110 ThreadListNode mListNode;
111 Heap* mCurrentHeap = nullptr;
112 FindContainHeapCache mFindContainHeapCache;
113 MessageQueue::BlockType mBlockType = MessageQueue::BlockType::Blocking;
114 MessageQueue::Element mQuitMsg = 0;
115 u32 mId = 0;
116 State mState = State::cInitialized;
117 CoreIdMask mAffinity{CoreId::cMain};
118#ifdef NNSDK
119 nn::os::ThreadType* mThreadInner = nullptr;
120#endif
121 void* mStackTop = nullptr;
122 void* mStackTopForCheck = nullptr;
123 s32 mPriority = 0;
124};
125
126class ThreadMgr : public hostio::Node
127{
128 SEAD_SINGLETON_DISPOSER(ThreadMgr)
129public:
130 ThreadMgr();
131 virtual ~ThreadMgr();
132
133 void initialize(Heap* heap);
134 void destroy();
135
136 bool isMainThread() const;
137 Thread* getMainThread() const { return mMainThread; }
138 Thread* getCurrentThread() const { return reinterpret_cast<Thread*>(mThreadPtrTLS.getValue()); }
139
140 static void waitDoneMultipleThread(Thread* const* threads, s32 num);
141 static void quitAndWaitDoneMultipleThread(Thread** threads, s32 num, bool is_jam);
142
143 static void checkCurrentThreadStackOverFlow(const char* source_file, s32 source_line);
144 static void checkCurrentThreadStackEndCorruption(const char* source_file, s32 source_line);
145 static void checkCurrentThreadStackPointerOverFlow(const char* source_file, s32 source_line);
146
147 CriticalSection* getListCS() { return &mListCS; }
148
149 bool tryRemoveFromFindContainHeapCache(Heap* heap)
150 {
151 const auto end = mList.end();
152 ScopedLock<CriticalSection> lock(getListCS());
153 bool found = false;
154 for (auto it = mList.begin(); it != end; ++it)
155 {
156 bool result = !(*it)->getFindContainHeapCache()->tryRemoveHeap(heap);
157 found |= result;
158 if (result)
159 break;
160 }
161 return found;
162 }
163
164#ifdef SEAD_DEBUG
165 void initHostIO();
166 void genMessage(hostio::Context* context) override;
167 void listenPropertyEvent(const hostio::PropertyEvent* event) override;
168#endif
169
170protected:
171 friend class Thread;
172
173 void addThread_(Thread* thread)
174 {
175 ScopedLock<CriticalSection> lock(getListCS());
176 mList.pushBack(item: thread->getThreadListNode());
177 }
178
179 void removeThread_(Thread* thread)
180 {
181 ScopedLock<CriticalSection> lock(getListCS());
182 mList.erase(item: thread->getThreadListNode());
183 }
184
185 void initMainThread_(Heap* heap);
186 void destroyMainThread_();
187 static u32 getCurrentThreadID_();
188
189private:
190 ThreadList mList;
191 CriticalSection mListCS;
192 Thread* mMainThread = nullptr;
193 ThreadLocalStorage mThreadPtrTLS;
194};
195
196class MainThread : public Thread
197{
198public:
199#ifdef NNSDK
200 MainThread(Heap* heap, nn::os::ThreadType* nn_thread, u32 thread_id)
201 : Thread(heap, nn_thread, thread_id)
202 {
203 }
204#endif
205 ~MainThread() override { mState = State::cTerminated; }
206
207 void destroy() override { SEAD_ASSERT_MSG(false, "Main thread can not destroy"); }
208 void quit(bool) override { SEAD_ASSERT_MSG(false, "Main thread can not quit"); }
209 void waitDone() override { SEAD_ASSERT_MSG(false, "Main thread can not waitDone"); }
210 void quitAndDestroySingleThread(bool) override
211 {
212 SEAD_ASSERT_MSG(false, "Main thread can not quit");
213 }
214 void setPriority(s32) override { SEAD_ASSERT_MSG(false, "Main thread can not set priority"); }
215
216protected:
217 void calc_(MessageQueue::Element) override {}
218};
219} // namespace sead
220