summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel/thread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle/kernel/thread.cpp')
-rw-r--r--src/core/hle/kernel/thread.cpp323
1 files changed, 323 insertions, 0 deletions
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
new file mode 100644
index 000000000..bf4c8353c
--- /dev/null
+++ b/src/core/hle/kernel/thread.cpp
@@ -0,0 +1,323 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <stdio.h>
6
7#include <list>
8#include <vector>
9#include <map>
10#include <string>
11
12#include "common/common.h"
13#include "common/thread_queue_list.h"
14
15#include "core/core.h"
16#include "core/mem_map.h"
17#include "core/hle/hle.h"
18#include "core/hle/svc.h"
19#include "core/hle/kernel/kernel.h"
20#include "core/hle/kernel/thread.h"
21
22namespace Kernel {
23
24class Thread : public Kernel::Object {
25public:
26
27 const char* GetName() { return name; }
28 const char* GetTypeName() { return "Thread"; }
29
30 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; }
31 Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Thread; }
32
33 inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
34 inline bool IsStopped() const { return (status & THREADSTATUS_DORMANT) != 0; }
35 inline bool IsReady() const { return (status & THREADSTATUS_READY) != 0; }
36 inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
37 inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
38
39 ThreadContext context;
40
41 u32 status;
42 u32 entry_point;
43 u32 stack_top;
44 u32 stack_size;
45
46 s32 initial_priority;
47 s32 current_priority;
48
49 s32 processor_id;
50
51 WaitType wait_type;
52
53 char name[Kernel::MAX_NAME_LENGTH + 1];
54};
55
56// Lists all thread ids that aren't deleted/etc.
57std::vector<Handle> g_thread_queue;
58
59// Lists only ready thread ids.
60Common::ThreadQueueList<Handle> g_thread_ready_queue;
61
62Handle g_current_thread_handle;
63Thread* g_current_thread;
64
65
66/// Gets the current thread
67inline Thread* GetCurrentThread() {
68 return g_current_thread;
69}
70
71/// Gets the current thread handle
72Handle GetCurrentThreadHandle() {
73 return GetCurrentThread()->GetHandle();
74}
75
76/// Sets the current thread
77inline void SetCurrentThread(Thread* t) {
78 g_current_thread = t;
79 g_current_thread_handle = t->GetHandle();
80}
81
82/// Saves the current CPU context
83void SaveContext(ThreadContext& ctx) {
84 Core::g_app_core->SaveContext(ctx);
85}
86
87/// Loads a CPU context
88void LoadContext(ThreadContext& ctx) {
89 Core::g_app_core->LoadContext(ctx);
90}
91
92/// Resets a thread
93void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
94 memset(&t->context, 0, sizeof(ThreadContext));
95
96 t->context.cpu_registers[0] = arg;
97 t->context.pc = t->entry_point;
98 t->context.sp = t->stack_top;
99 t->context.cpsr = 0x1F; // Usermode
100
101 if (t->current_priority < lowest_priority) {
102 t->current_priority = t->initial_priority;
103 }
104
105 t->wait_type = WAITTYPE_NONE;
106}
107
108/// Change a thread to "ready" state
109void ChangeReadyState(Thread* t, bool ready) {
110 Handle handle = t->GetHandle();
111 if (t->IsReady()) {
112 if (!ready) {
113 g_thread_ready_queue.remove(t->current_priority, handle);
114 }
115 } else if (ready) {
116 if (t->IsRunning()) {
117 g_thread_ready_queue.push_front(t->current_priority, handle);
118 } else {
119 g_thread_ready_queue.push_back(t->current_priority, handle);
120 }
121 t->status = THREADSTATUS_READY;
122 }
123}
124
125/// Changes a threads state
126void ChangeThreadState(Thread* t, ThreadStatus new_status) {
127 if (!t || t->status == new_status) {
128 return;
129 }
130 ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0);
131 t->status = new_status;
132
133 if (new_status == THREADSTATUS_WAIT) {
134 if (t->wait_type == WAITTYPE_NONE) {
135 printf("ERROR: Waittype none not allowed here\n");
136 }
137 }
138}
139
140/// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields)
141void CallThread(Thread* t) {
142 // Stop waiting
143 if (t->wait_type != WAITTYPE_NONE) {
144 t->wait_type = WAITTYPE_NONE;
145 }
146 ChangeThreadState(t, THREADSTATUS_READY);
147}
148
149/// Switches CPU context to that of the specified thread
150void SwitchContext(Thread* t) {
151 Thread* cur = GetCurrentThread();
152
153 // Save context for current thread
154 if (cur) {
155 SaveContext(cur->context);
156
157 if (cur->IsRunning()) {
158 ChangeReadyState(cur, true);
159 }
160 }
161 // Load context of new thread
162 if (t) {
163 SetCurrentThread(t);
164 ChangeReadyState(t, false);
165 t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY;
166 t->wait_type = WAITTYPE_NONE;
167 LoadContext(t->context);
168 } else {
169 SetCurrentThread(NULL);
170 }
171}
172
173/// Gets the next thread that is ready to be run by priority
174Thread* NextThread() {
175 Handle next;
176 Thread* cur = GetCurrentThread();
177
178 if (cur && cur->IsRunning()) {
179 next = g_thread_ready_queue.pop_first_better(cur->current_priority);
180 } else {
181 next = g_thread_ready_queue.pop_first();
182 }
183 if (next == 0) {
184 return NULL;
185 }
186 return Kernel::g_object_pool.GetFast<Thread>(next);
187}
188
189/// Puts the current thread in the wait state for the given type
190void WaitCurrentThread(WaitType wait_type) {
191 Thread* t = GetCurrentThread();
192 t->wait_type = wait_type;
193 ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND)));
194}
195
196/// Resumes a thread from waiting by marking it as "ready"
197void ResumeThreadFromWait(Handle handle) {
198 u32 error;
199 Thread* t = Kernel::g_object_pool.Get<Thread>(handle, error);
200 if (t) {
201 t->status &= ~THREADSTATUS_WAIT;
202 if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
203 ChangeReadyState(t, true);
204 }
205 }
206}
207
208/// Creates a new thread
209Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority,
210 s32 processor_id, u32 stack_top, int stack_size) {
211
212 _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
213 "CreateThread priority=%d, outside of allowable range!", priority)
214
215 Thread* t = new Thread;
216
217 handle = Kernel::g_object_pool.Create(t);
218
219 g_thread_queue.push_back(handle);
220 g_thread_ready_queue.prepare(priority);
221
222 t->status = THREADSTATUS_DORMANT;
223 t->entry_point = entry_point;
224 t->stack_top = stack_top;
225 t->stack_size = stack_size;
226 t->initial_priority = t->current_priority = priority;
227 t->processor_id = processor_id;
228 t->wait_type = WAITTYPE_NONE;
229
230 strncpy(t->name, name, Kernel::MAX_NAME_LENGTH);
231 t->name[Kernel::MAX_NAME_LENGTH] = '\0';
232
233 return t;
234}
235
236/// Creates a new thread - wrapper for external user
237Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
238 u32 stack_top, int stack_size) {
239 if (name == NULL) {
240 ERROR_LOG(KERNEL, "CreateThread(): NULL name");
241 return -1;
242 }
243 if ((u32)stack_size < 0x200) {
244 ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name,
245 stack_size);
246 return -1;
247 }
248 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
249 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
250 WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X",
251 name, priority, new_priority);
252 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
253 // validity of this
254 priority = new_priority;
255 }
256 if (!Memory::GetPointer(entry_point)) {
257 ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point);
258 return -1;
259 }
260 Handle handle;
261 Thread* t = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
262 stack_size);
263
264 ResetThread(t, arg, 0);
265
266 HLE::EatCycles(32000);
267
268 // This won't schedule to the new thread, but it may to one woken from eating cycles.
269 // Technically, this should not eat all at once, and reschedule in the middle, but that's hard.
270 HLE::ReSchedule("thread created");
271
272 CallThread(t);
273
274 return handle;
275}
276
277/// Sets up the primary application thread
278Handle SetupMainThread(s32 priority, int stack_size) {
279 Handle handle;
280
281 // Initialize new "main" thread
282 Thread* t = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
283 THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
284
285 ResetThread(t, 0, 0);
286
287 // If running another thread already, set it to "ready" state
288 Thread* cur = GetCurrentThread();
289 if (cur && cur->IsRunning()) {
290 ChangeReadyState(cur, true);
291 }
292
293 // Run new "main" thread
294 SetCurrentThread(t);
295 t->status = THREADSTATUS_RUNNING;
296 LoadContext(t->context);
297
298 return handle;
299}
300
301/// Reschedules to the next available thread (call after current thread is suspended)
302void Reschedule() {
303 Thread* prev = GetCurrentThread();
304 Thread* next = NextThread();
305 if (next > 0) {
306 SwitchContext(next);
307
308 // Hack - automatically change previous thread (which would have been in "wait" state) to
309 // "ready" state, so that we can immediately resume to it when new thread yields. FixMe to
310 // actually wait for whatever event it is supposed to be waiting on.
311 ChangeReadyState(prev, true);
312 }
313}
314
315////////////////////////////////////////////////////////////////////////////////////////////////////
316
317void ThreadingInit() {
318}
319
320void ThreadingShutdown() {
321}
322
323} // namespace