summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
authorGravatar bunnei2014-05-26 20:52:00 -0400
committerGravatar bunnei2014-05-26 20:52:00 -0400
commit6448c2f30062c085330ff26a4812c9a91c7b492c (patch)
tree386e32cf3ec053491fb8dfd8459a1c92553241d9 /src/core/hle/kernel
parentMerge pull request #4 from archshift/patch-1 (diff)
parentservice: fixed typo that MSVC did not catch as an error (diff)
downloadyuzu-6448c2f30062c085330ff26a4812c9a91c7b492c.tar.gz
yuzu-6448c2f30062c085330ff26a4812c9a91c7b492c.tar.xz
yuzu-6448c2f30062c085330ff26a4812c9a91c7b492c.zip
Merge pull request #9 from bunnei/master
Add initial kernel HLE, includes thread creation and context switching
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/kernel.cpp158
-rw-r--r--src/core/hle/kernel/kernel.h154
-rw-r--r--src/core/hle/kernel/mutex.cpp132
-rw-r--r--src/core/hle/kernel/mutex.h26
-rw-r--r--src/core/hle/kernel/thread.cpp323
-rw-r--r--src/core/hle/kernel/thread.h74
6 files changed, 867 insertions, 0 deletions
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
new file mode 100644
index 000000000..de80de893
--- /dev/null
+++ b/src/core/hle/kernel/kernel.cpp
@@ -0,0 +1,158 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string.h>
8
9#include "common/common.h"
10
11#include "core/core.h"
12#include "core/hle/kernel/kernel.h"
13#include "core/hle/kernel/thread.h"
14
15namespace Kernel {
16
17ObjectPool g_object_pool;
18
19ObjectPool::ObjectPool() {
20 memset(occupied, 0, sizeof(bool) * MAX_COUNT);
21 next_id = INITIAL_NEXT_ID;
22}
23
24Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) {
25 if (range_top > MAX_COUNT) {
26 range_top = MAX_COUNT;
27 }
28 if (next_id >= range_bottom && next_id < range_top) {
29 range_bottom = next_id++;
30 }
31 for (int i = range_bottom; i < range_top; i++) {
32 if (!occupied[i]) {
33 occupied[i] = true;
34 pool[i] = obj;
35 pool[i]->handle = i + HANDLE_OFFSET;
36 return i + HANDLE_OFFSET;
37 }
38 }
39 ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use.");
40 return 0;
41}
42
43bool ObjectPool::IsValid(Handle handle) {
44 int index = handle - HANDLE_OFFSET;
45 if (index < 0)
46 return false;
47 if (index >= MAX_COUNT)
48 return false;
49
50 return occupied[index];
51}
52
53void ObjectPool::Clear() {
54 for (int i = 0; i < MAX_COUNT; i++) {
55 //brutally clear everything, no validation
56 if (occupied[i])
57 delete pool[i];
58 occupied[i] = false;
59 }
60 memset(pool, 0, sizeof(Object*)*MAX_COUNT);
61 next_id = INITIAL_NEXT_ID;
62}
63
64Object* &ObjectPool::operator [](Handle handle)
65{
66 _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ");
67 return pool[handle - HANDLE_OFFSET];
68}
69
70void ObjectPool::List() {
71 for (int i = 0; i < MAX_COUNT; i++) {
72 if (occupied[i]) {
73 if (pool[i]) {
74 INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName(),
75 pool[i]->GetName());
76 }
77 }
78 }
79}
80
81int ObjectPool::GetCount() {
82 int count = 0;
83 for (int i = 0; i < MAX_COUNT; i++) {
84 if (occupied[i])
85 count++;
86 }
87 return count;
88}
89
90Object* ObjectPool::CreateByIDType(int type) {
91 // Used for save states. This is ugly, but what other way is there?
92 switch (type) {
93 //case SCE_KERNEL_TMID_Alarm:
94 // return __KernelAlarmObject();
95 //case SCE_KERNEL_TMID_EventFlag:
96 // return __KernelEventFlagObject();
97 //case SCE_KERNEL_TMID_Mbox:
98 // return __KernelMbxObject();
99 //case SCE_KERNEL_TMID_Fpl:
100 // return __KernelMemoryFPLObject();
101 //case SCE_KERNEL_TMID_Vpl:
102 // return __KernelMemoryVPLObject();
103 //case PPSSPP_KERNEL_TMID_PMB:
104 // return __KernelMemoryPMBObject();
105 //case PPSSPP_KERNEL_TMID_Module:
106 // return __KernelModuleObject();
107 //case SCE_KERNEL_TMID_Mpipe:
108 // return __KernelMsgPipeObject();
109 //case SCE_KERNEL_TMID_Mutex:
110 // return __KernelMutexObject();
111 //case SCE_KERNEL_TMID_LwMutex:
112 // return __KernelLwMutexObject();
113 //case SCE_KERNEL_TMID_Semaphore:
114 // return __KernelSemaphoreObject();
115 //case SCE_KERNEL_TMID_Callback:
116 // return __KernelCallbackObject();
117 //case SCE_KERNEL_TMID_Thread:
118 // return __KernelThreadObject();
119 //case SCE_KERNEL_TMID_VTimer:
120 // return __KernelVTimerObject();
121 //case SCE_KERNEL_TMID_Tlspl:
122 // return __KernelTlsplObject();
123 //case PPSSPP_KERNEL_TMID_File:
124 // return __KernelFileNodeObject();
125 //case PPSSPP_KERNEL_TMID_DirList:
126 // return __KernelDirListingObject();
127
128 default:
129 ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type);
130 return NULL;
131 }
132}
133
134void Init() {
135 Kernel::ThreadingInit();
136}
137
138void Shutdown() {
139 Kernel::ThreadingShutdown();
140}
141
142/**
143 * Loads executable stored at specified address
144 * @entry_point Entry point in memory of loaded executable
145 * @return True on success, otherwise false
146 */
147bool LoadExec(u32 entry_point) {
148 Init();
149
150 Core::g_app_core->SetPC(entry_point);
151
152 // 0x30 is the typical main thread priority I've seen used so far
153 Handle thread = Kernel::SetupMainThread(0x30);
154
155 return true;
156}
157
158} // namespace
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
new file mode 100644
index 000000000..7cd79c2c4
--- /dev/null
+++ b/src/core/hle/kernel/kernel.h
@@ -0,0 +1,154 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common.h"
8
9typedef u32 Handle;
10typedef s32 Result;
11
12namespace Kernel {
13
14enum class HandleType : u32 {
15 Unknown = 0,
16 Port = 1,
17 Service = 2,
18 Event = 3,
19 Mutex = 4,
20 SharedMemory = 5,
21 Redirection = 6,
22 Thread = 7,
23 Process = 8,
24 Arbiter = 9,
25 File = 10,
26 Semaphore = 11,
27};
28
29enum {
30 MAX_NAME_LENGTH = 0x100,
31 DEFAULT_STACK_SIZE = 0x4000,
32};
33
34class ObjectPool;
35
36class Object : NonCopyable {
37 friend class ObjectPool;
38 u32 handle;
39public:
40 virtual ~Object() {}
41 Handle GetHandle() const { return handle; }
42 virtual const char *GetTypeName() { return "[BAD KERNEL OBJECT TYPE]"; }
43 virtual const char *GetName() { return "[UNKNOWN KERNEL OBJECT]"; }
44 virtual Kernel::HandleType GetHandleType() const = 0;
45};
46
47class ObjectPool : NonCopyable {
48public:
49 ObjectPool();
50 ~ObjectPool() {}
51
52 // Allocates a handle within the range and inserts the object into the map.
53 Handle Create(Object* obj, int range_bottom=INITIAL_NEXT_ID, int range_top=0x7FFFFFFF);
54
55 static Object* CreateByIDType(int type);
56
57 template <class T>
58 u32 Destroy(Handle handle) {
59 u32 error;
60 if (Get<T>(handle, error)) {
61 occupied[handle - HANDLE_OFFSET] = false;
62 delete pool[handle - HANDLE_OFFSET];
63 }
64 return error;
65 };
66
67 bool IsValid(Handle handle);
68
69 template <class T>
70 T* Get(Handle handle, u32& outError) {
71 if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) {
72 // Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP
73 if (handle != 0 && (u32)handle != 0x80020001) {
74 WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
75 }
76 outError = 0;//T::GetMissingErrorCode();
77 return 0;
78 } else {
79 // Previously we had a dynamic_cast here, but since RTTI was disabled traditionally,
80 // it just acted as a static case and everything worked. This means that we will never
81 // see the Wrong type object error below, but we'll just have to live with that danger.
82 T* t = static_cast<T*>(pool[handle - HANDLE_OFFSET]);
83 if (t == 0 || t->GetHandleType() != T::GetStaticHandleType()) {
84 WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle);
85 outError = 0;//T::GetMissingErrorCode();
86 return 0;
87 }
88 outError = 0;//SCE_KERNEL_ERROR_OK;
89 return t;
90 }
91 }
92
93 // ONLY use this when you know the handle is valid.
94 template <class T>
95 T *GetFast(Handle handle) {
96 const Handle realHandle = handle - HANDLE_OFFSET;
97 _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]);
98 return static_cast<T*>(pool[realHandle]);
99 }
100
101 template <class T, typename ArgT>
102 void Iterate(bool func(T*, ArgT), ArgT arg) {
103 int type = T::GetStaticIDType();
104 for (int i = 0; i < MAX_COUNT; i++)
105 {
106 if (!occupied[i])
107 continue;
108 T* t = static_cast<T*>(pool[i]);
109 if (t->GetIDType() == type) {
110 if (!func(t, arg))
111 break;
112 }
113 }
114 }
115
116 bool GetIDType(Handle handle, HandleType* type) const {
117 if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) ||
118 !occupied[handle - HANDLE_OFFSET]) {
119 ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle);
120 return false;
121 }
122 Object* t = pool[handle - HANDLE_OFFSET];
123 *type = t->GetHandleType();
124 return true;
125 }
126
127 Object* &operator [](Handle handle);
128 void List();
129 void Clear();
130 int GetCount();
131
132private:
133
134 enum {
135 MAX_COUNT = 0x1000,
136 HANDLE_OFFSET = 0x100,
137 INITIAL_NEXT_ID = 0x10,
138 };
139
140 Object* pool[MAX_COUNT];
141 bool occupied[MAX_COUNT];
142 int next_id;
143};
144
145extern ObjectPool g_object_pool;
146
147/**
148 * Loads executable stored at specified address
149 * @entry_point Entry point in memory of loaded executable
150 * @return True on success, otherwise false
151 */
152bool LoadExec(u32 entry_point);
153
154} // namespace
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
new file mode 100644
index 000000000..019efbc78
--- /dev/null
+++ b/src/core/hle/kernel/mutex.cpp
@@ -0,0 +1,132 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <map>
6#include <vector>
7
8#include "common/common.h"
9
10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/thread.h"
12
13namespace Kernel {
14
15class Mutex : public Object {
16public:
17 const char* GetTypeName() { return "Mutex"; }
18
19 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; }
20 Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Mutex; }
21
22 bool initial_locked; ///< Initial lock state when mutex was created
23 bool locked; ///< Current locked state
24 Handle lock_thread; ///< Handle to thread that currently has mutex
25 std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
26};
27
28////////////////////////////////////////////////////////////////////////////////////////////////////
29
30typedef std::multimap<Handle, Handle> MutexMap;
31static MutexMap g_mutex_held_locks;
32
33void MutexAcquireLock(Mutex* mutex, Handle thread) {
34 g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle()));
35 mutex->lock_thread = thread;
36}
37
38void MutexAcquireLock(Mutex* mutex) {
39 Handle thread = GetCurrentThreadHandle();
40 MutexAcquireLock(mutex, thread);
41}
42
43void MutexEraseLock(Mutex* mutex) {
44 Handle handle = mutex->GetHandle();
45 auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread);
46 for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) {
47 if ((*iter).second == handle) {
48 g_mutex_held_locks.erase(iter);
49 break;
50 }
51 }
52 mutex->lock_thread = -1;
53}
54
55bool LockMutex(Mutex* mutex) {
56 // Mutex alread locked?
57 if (mutex->locked) {
58 return false;
59 }
60 MutexAcquireLock(mutex);
61 return true;
62}
63
64bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
65 MutexAcquireLock(mutex, thread);
66 Kernel::ResumeThreadFromWait(thread);
67 return true;
68}
69
70bool ReleaseMutex(Mutex* mutex) {
71 MutexEraseLock(mutex);
72 bool woke_threads = false;
73 auto iter = mutex->waiting_threads.begin();
74
75 // Find the next waiting thread for the mutex...
76 while (!woke_threads && !mutex->waiting_threads.empty()) {
77 woke_threads |= ReleaseMutexForThread(mutex, *iter);
78 mutex->waiting_threads.erase(iter);
79 }
80 // Reset mutex lock thread handle, nothing is waiting
81 if (!woke_threads) {
82 mutex->locked = false;
83 mutex->lock_thread = -1;
84 }
85 return woke_threads;
86}
87
88/**
89 * Releases a mutex
90 * @param handle Handle to mutex to release
91 */
92Result ReleaseMutex(Handle handle) {
93 Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle);
94 if (!ReleaseMutex(mutex)) {
95 return -1;
96 }
97 return 0;
98}
99
100/**
101 * Creates a mutex
102 * @param handle Reference to handle for the newly created mutex
103 * @param initial_locked Specifies if the mutex should be locked initially
104 */
105Mutex* CreateMutex(Handle& handle, bool initial_locked) {
106 Mutex* mutex = new Mutex;
107 handle = Kernel::g_object_pool.Create(mutex);
108
109 mutex->locked = mutex->initial_locked = initial_locked;
110
111 // Acquire mutex with current thread if initialized as locked...
112 if (mutex->locked) {
113 MutexAcquireLock(mutex);
114
115 // Otherwise, reset lock thread handle
116 } else {
117 mutex->lock_thread = -1;
118 }
119 return mutex;
120}
121
122/**
123 * Creates a mutex
124 * @param initial_locked Specifies if the mutex should be locked initially
125 */
126Handle CreateMutex(bool initial_locked) {
127 Handle handle;
128 Mutex* mutex = CreateMutex(handle, initial_locked);
129 return handle;
130}
131
132} // namespace
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
new file mode 100644
index 000000000..871e2e562
--- /dev/null
+++ b/src/core/hle/kernel/mutex.h
@@ -0,0 +1,26 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9#include "core/hle/kernel/kernel.h"
10
11namespace Kernel {
12
13/**
14 * Releases a mutex
15 * @param handle Handle to mutex to release
16 */
17Result ReleaseMutex(Handle handle);
18
19/**
20 * Creates a mutex
21 * @param handle Reference to handle for the newly created mutex
22 * @param initial_locked Specifies if the mutex should be locked initially
23 */
24Handle CreateMutex(bool initial_locked);
25
26} // namespace
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
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
new file mode 100644
index 000000000..9628f165d
--- /dev/null
+++ b/src/core/hle/kernel/thread.h
@@ -0,0 +1,74 @@
1// Copyright 2014 Citra Emulator Project / PPSSPP Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8#include "core/hle/kernel/kernel.h"
9
10enum ThreadPriority {
11 THREADPRIO_HIGHEST = 0, ///< Highest thread priority
12 THREADPRIO_DEFAULT = 16, ///< Default thread priority for userland apps
13 THREADPRIO_LOW = 31, ///< Low range of thread priority for userland apps
14 THREADPRIO_LOWEST = 63, ///< Thread priority max checked by svcCreateThread
15};
16
17enum ThreadProcessorId {
18 THREADPROCESSORID_0 = 0xFFFFFFFE, ///< Enables core appcode
19 THREADPROCESSORID_1 = 0xFFFFFFFD, ///< Enables core syscore
20 THREADPROCESSORID_ALL = 0xFFFFFFFC, ///< Enables both cores
21};
22
23enum ThreadStatus {
24 THREADSTATUS_RUNNING = 1,
25 THREADSTATUS_READY = 2,
26 THREADSTATUS_WAIT = 4,
27 THREADSTATUS_SUSPEND = 8,
28 THREADSTATUS_DORMANT = 16,
29 THREADSTATUS_DEAD = 32,
30 THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND
31};
32
33enum WaitType {
34 WAITTYPE_NONE,
35 WAITTYPE_SLEEP,
36 WAITTYPE_SEMA,
37 WAITTYPE_EVENTFLAG,
38 WAITTYPE_THREADEND,
39 WAITTYPE_VBLANK,
40 WAITTYPE_MUTEX,
41 WAITTYPE_SYNCH,
42};
43
44namespace Kernel {
45
46/// Creates a new thread - wrapper for external user
47Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
48 u32 stack_top, int stack_size=Kernel::DEFAULT_STACK_SIZE);
49
50/// Sets up the primary application thread
51Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE);
52
53/// Reschedules to the next available thread (call after current thread is suspended)
54void Reschedule();
55
56/// Puts the current thread in the wait state for the given type
57void WaitCurrentThread(WaitType wait_type);
58
59/// Resumes a thread from waiting by marking it as "ready"
60void ResumeThreadFromWait(Handle handle);
61
62/// Gets the current thread handle
63Handle GetCurrentThreadHandle();
64
65/// Put current thread in a wait state - on WaitSynchronization
66void WaitThread_Synchronization();
67
68/// Initialize threading
69void ThreadingInit();
70
71/// Shutdown threading
72void ThreadingShutdown();
73
74} // namespace