summaryrefslogtreecommitdiff
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
authorGravatar bunnei2014-06-14 12:13:16 -0400
committerGravatar bunnei2014-06-14 12:13:16 -0400
commit004df767953a949817da89bddcd5d1379240f769 (patch)
treeb2d54928dcbf3cb4dde0cd5d3277afe7999b7bd9 /src/core/hle/kernel
parentGPU debugger: Const correctness and build fix. (diff)
parentKernel: Removed unnecessary "#pragma once". (diff)
downloadyuzu-004df767953a949817da89bddcd5d1379240f769.tar.gz
yuzu-004df767953a949817da89bddcd5d1379240f769.tar.xz
yuzu-004df767953a949817da89bddcd5d1379240f769.zip
Merge branch 'threading' of https://github.com/bunnei/citra
Conflicts: src/core/hle/function_wrappers.h src/core/hle/service/gsp.cpp
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/event.cpp159
-rw-r--r--src/core/hle/kernel/event.h52
-rw-r--r--src/core/hle/kernel/kernel.cpp11
-rw-r--r--src/core/hle/kernel/kernel.h33
-rw-r--r--src/core/hle/kernel/mutex.cpp50
-rw-r--r--src/core/hle/kernel/mutex.h6
-rw-r--r--src/core/hle/kernel/thread.cpp223
-rw-r--r--src/core/hle/kernel/thread.h15
8 files changed, 477 insertions, 72 deletions
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
new file mode 100644
index 000000000..127c0cfc6
--- /dev/null
+++ b/src/core/hle/kernel/event.cpp
@@ -0,0 +1,159 @@
1// Copyright 2014 Citra Emulator Project
2// Licensed under GPLv2
3// Refer to the license.txt file included.
4
5#include <map>
6#include <algorithm>
7#include <vector>
8
9#include "common/common.h"
10
11#include "core/hle/kernel/kernel.h"
12#include "core/hle/kernel/event.h"
13#include "core/hle/kernel/thread.h"
14
15namespace Kernel {
16
17class Event : public Object {
18public:
19 const char* GetTypeName() const { return "Event"; }
20 const char* GetName() const { return name.c_str(); }
21
22 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Event; }
23 Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Event; }
24
25 ResetType intitial_reset_type; ///< ResetType specified at Event initialization
26 ResetType reset_type; ///< Current ResetType
27
28 bool locked; ///< Event signal wait
29 bool permanent_locked; ///< Hack - to set event permanent state (for easy passthrough)
30 std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event
31 std::string name; ///< Name of event (optional)
32
33 /**
34 * Wait for kernel object to synchronize
35 * @param wait Boolean wait set if current thread should wait as a result of sync operation
36 * @return Result of operation, 0 on success, otherwise error code
37 */
38 Result WaitSynchronization(bool* wait) {
39 *wait = locked;
40 if (locked) {
41 Handle thread = GetCurrentThreadHandle();
42 if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
43 waiting_threads.push_back(thread);
44 }
45 Kernel::WaitCurrentThread(WAITTYPE_EVENT);
46 }
47 if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
48 locked = true;
49 }
50 return 0;
51 }
52};
53
54/**
55 * Hackish function to set an events permanent lock state, used to pass through synch blocks
56 * @param handle Handle to event to change
57 * @param permanent_locked Boolean permanent locked value to set event
58 * @return Result of operation, 0 on success, otherwise error code
59 */
60Result SetPermanentLock(Handle handle, const bool permanent_locked) {
61 Event* evt = g_object_pool.GetFast<Event>(handle);
62 _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
63
64 evt->permanent_locked = permanent_locked;
65 return 0;
66}
67
68/**
69 * Changes whether an event is locked or not
70 * @param handle Handle to event to change
71 * @param locked Boolean locked value to set event
72 * @return Result of operation, 0 on success, otherwise error code
73 */
74Result SetEventLocked(const Handle handle, const bool locked) {
75 Event* evt = g_object_pool.GetFast<Event>(handle);
76 _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
77
78 if (!evt->permanent_locked) {
79 evt->locked = locked;
80 }
81 return 0;
82}
83
84/**
85 * Signals an event
86 * @param handle Handle to event to signal
87 * @return Result of operation, 0 on success, otherwise error code
88 */
89Result SignalEvent(const Handle handle) {
90 Event* evt = g_object_pool.GetFast<Event>(handle);
91 _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
92
93 // Resume threads waiting for event to signal
94 bool event_caught = false;
95 for (size_t i = 0; i < evt->waiting_threads.size(); ++i) {
96 ResumeThreadFromWait( evt->waiting_threads[i]);
97
98 // If any thread is signalled awake by this event, assume the event was "caught" and reset
99 // the event. This will result in the next thread waiting on the event to block. Otherwise,
100 // the event will not be reset, and the next thread to call WaitSynchronization on it will
101 // not block. Not sure if this is correct behavior, but it seems to work.
102 event_caught = true;
103 }
104 evt->waiting_threads.clear();
105
106 if (!evt->permanent_locked) {
107 evt->locked = event_caught;
108 }
109 return 0;
110}
111
112/**
113 * Clears an event
114 * @param handle Handle to event to clear
115 * @return Result of operation, 0 on success, otherwise error code
116 */
117Result ClearEvent(Handle handle) {
118 Event* evt = g_object_pool.GetFast<Event>(handle);
119 _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
120
121 if (!evt->permanent_locked) {
122 evt->locked = true;
123 }
124 return 0;
125}
126
127/**
128 * Creates an event
129 * @param handle Reference to handle for the newly created mutex
130 * @param reset_type ResetType describing how to create event
131 * @param name Optional name of event
132 * @return Newly created Event object
133 */
134Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) {
135 Event* evt = new Event;
136
137 handle = Kernel::g_object_pool.Create(evt);
138
139 evt->locked = true;
140 evt->permanent_locked = false;
141 evt->reset_type = evt->intitial_reset_type = reset_type;
142 evt->name = name;
143
144 return evt;
145}
146
147/**
148 * Creates an event
149 * @param reset_type ResetType describing how to create event
150 * @param name Optional name of event
151 * @return Handle to newly created Event object
152 */
153Handle CreateEvent(const ResetType reset_type, const std::string& name) {
154 Handle handle;
155 Event* evt = CreateEvent(handle, reset_type, name);
156 return handle;
157}
158
159} // namespace
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
new file mode 100644
index 000000000..c39b33180
--- /dev/null
+++ b/src/core/hle/kernel/event.h
@@ -0,0 +1,52 @@
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#include "core/hle/svc.h"
11
12namespace Kernel {
13
14/**
15 * Changes whether an event is locked or not
16 * @param handle Handle to event to change
17 * @param locked Boolean locked value to set event
18 * @return Result of operation, 0 on success, otherwise error code
19 */
20Result SetEventLocked(const Handle handle, const bool locked);
21
22/**
23 * Hackish function to set an events permanent lock state, used to pass through synch blocks
24 * @param handle Handle to event to change
25 * @param permanent_locked Boolean permanent locked value to set event
26 * @return Result of operation, 0 on success, otherwise error code
27 */
28Result SetPermanentLock(Handle handle, const bool permanent_locked);
29
30/**
31 * Signals an event
32 * @param handle Handle to event to signal
33 * @return Result of operation, 0 on success, otherwise error code
34 */
35Result SignalEvent(const Handle handle);
36
37/**
38 * Clears an event
39 * @param handle Handle to event to clear
40 * @return Result of operation, 0 on success, otherwise error code
41 */
42Result ClearEvent(Handle handle);
43
44/**
45 * Creates an event
46 * @param reset_type ResetType describing how to create event
47 * @param name Optional name of event
48 * @return Handle to newly created Event object
49 */
50Handle CreateEvent(const ResetType reset_type, const std::string& name="Unknown");
51
52} // namespace
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index de80de893..cda183add 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -2,8 +2,6 @@
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#pragma once
6
7#include <string.h> 5#include <string.h>
8 6
9#include "common/common.h" 7#include "common/common.h"
@@ -14,6 +12,7 @@
14 12
15namespace Kernel { 13namespace Kernel {
16 14
15Handle g_main_thread = 0;
17ObjectPool g_object_pool; 16ObjectPool g_object_pool;
18 17
19ObjectPool::ObjectPool() { 18ObjectPool::ObjectPool() {
@@ -127,16 +126,20 @@ Object* ObjectPool::CreateByIDType(int type) {
127 126
128 default: 127 default:
129 ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type); 128 ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type);
130 return NULL; 129 return nullptr;
131 } 130 }
132} 131}
133 132
133/// Initialize the kernel
134void Init() { 134void Init() {
135 Kernel::ThreadingInit(); 135 Kernel::ThreadingInit();
136} 136}
137 137
138/// Shutdown the kernel
138void Shutdown() { 139void Shutdown() {
139 Kernel::ThreadingShutdown(); 140 Kernel::ThreadingShutdown();
141
142 g_object_pool.Clear(); // Free all kernel objects
140} 143}
141 144
142/** 145/**
@@ -150,7 +153,7 @@ bool LoadExec(u32 entry_point) {
150 Core::g_app_core->SetPC(entry_point); 153 Core::g_app_core->SetPC(entry_point);
151 154
152 // 0x30 is the typical main thread priority I've seen used so far 155 // 0x30 is the typical main thread priority I've seen used so far
153 Handle thread = Kernel::SetupMainThread(0x30); 156 g_main_thread = Kernel::SetupMainThread(0x30);
154 157
155 return true; 158 return true;
156} 159}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 7cd79c2c4..3f15da0ac 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -11,6 +11,11 @@ typedef s32 Result;
11 11
12namespace Kernel { 12namespace Kernel {
13 13
14enum KernelHandle {
15 CurrentThread = 0xFFFF8000,
16 CurrentProcess = 0xFFFF8001,
17};
18
14enum class HandleType : u32 { 19enum class HandleType : u32 {
15 Unknown = 0, 20 Unknown = 0,
16 Port = 1, 21 Port = 1,
@@ -39,9 +44,26 @@ class Object : NonCopyable {
39public: 44public:
40 virtual ~Object() {} 45 virtual ~Object() {}
41 Handle GetHandle() const { return handle; } 46 Handle GetHandle() const { return handle; }
42 virtual const char *GetTypeName() { return "[BAD KERNEL OBJECT TYPE]"; } 47 virtual const char* GetTypeName() const { return "[BAD KERNEL OBJECT TYPE]"; }
43 virtual const char *GetName() { return "[UNKNOWN KERNEL OBJECT]"; } 48 virtual const char* GetName() const { return "[UNKNOWN KERNEL OBJECT]"; }
44 virtual Kernel::HandleType GetHandleType() const = 0; 49 virtual Kernel::HandleType GetHandleType() const = 0;
50
51 /**
52 * Synchronize kernel object
53 * @param wait Boolean wait set if current thread should wait as a result of sync operation
54 * @return Result of operation, 0 on success, otherwise error code
55 */
56 virtual Result SyncRequest(bool* wait) {
57 ERROR_LOG(KERNEL, "(UNIMPLEMENTED)");
58 return -1;
59 }
60
61 /**
62 * Wait for kernel object to synchronize
63 * @param wait Boolean wait set if current thread should wait as a result of sync operation
64 * @return Result of operation, 0 on success, otherwise error code
65 */
66 virtual Result WaitSynchronization(bool* wait) = 0;
45}; 67};
46 68
47class ObjectPool : NonCopyable { 69class ObjectPool : NonCopyable {
@@ -143,6 +165,13 @@ private:
143}; 165};
144 166
145extern ObjectPool g_object_pool; 167extern ObjectPool g_object_pool;
168extern Handle g_main_thread;
169
170/// Initialize the kernel
171void Init();
172
173/// Shutdown the kernel
174void Shutdown();
146 175
147/** 176/**
148 * Loads executable stored at specified address 177 * Loads executable stored at specified address
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 019efbc78..1ccf1eb73 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -8,21 +8,51 @@
8#include "common/common.h" 8#include "common/common.h"
9 9
10#include "core/hle/kernel/kernel.h" 10#include "core/hle/kernel/kernel.h"
11#include "core/hle/kernel/mutex.h"
11#include "core/hle/kernel/thread.h" 12#include "core/hle/kernel/thread.h"
12 13
13namespace Kernel { 14namespace Kernel {
14 15
15class Mutex : public Object { 16class Mutex : public Object {
16public: 17public:
17 const char* GetTypeName() { return "Mutex"; } 18 const char* GetTypeName() const { return "Mutex"; }
19 const char* GetName() const { return name.c_str(); }
18 20
19 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; } 21 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; }
20 Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Mutex; } 22 Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Mutex; }
21 23
22 bool initial_locked; ///< Initial lock state when mutex was created 24 bool initial_locked; ///< Initial lock state when mutex was created
23 bool locked; ///< Current locked state 25 bool locked; ///< Current locked state
24 Handle lock_thread; ///< Handle to thread that currently has mutex 26 Handle lock_thread; ///< Handle to thread that currently has mutex
25 std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex 27 std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
28 std::string name; ///< Name of mutex (optional)
29
30 /**
31 * Synchronize kernel object
32 * @param wait Boolean wait set if current thread should wait as a result of sync operation
33 * @return Result of operation, 0 on success, otherwise error code
34 */
35 Result SyncRequest(bool* wait) {
36 // TODO(bunnei): ImplementMe
37 locked = true;
38 return 0;
39 }
40
41 /**
42 * Wait for kernel object to synchronize
43 * @param wait Boolean wait set if current thread should wait as a result of sync operation
44 * @return Result of operation, 0 on success, otherwise error code
45 */
46 Result WaitSynchronization(bool* wait) {
47 // TODO(bunnei): ImplementMe
48 *wait = locked;
49
50 if (locked) {
51 Kernel::WaitCurrentThread(WAITTYPE_MUTEX);
52 }
53
54 return 0;
55 }
26}; 56};
27 57
28//////////////////////////////////////////////////////////////////////////////////////////////////// 58////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -70,10 +100,10 @@ bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
70bool ReleaseMutex(Mutex* mutex) { 100bool ReleaseMutex(Mutex* mutex) {
71 MutexEraseLock(mutex); 101 MutexEraseLock(mutex);
72 bool woke_threads = false; 102 bool woke_threads = false;
73 auto iter = mutex->waiting_threads.begin();
74 103
75 // Find the next waiting thread for the mutex... 104 // Find the next waiting thread for the mutex...
76 while (!woke_threads && !mutex->waiting_threads.empty()) { 105 while (!woke_threads && !mutex->waiting_threads.empty()) {
106 std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
77 woke_threads |= ReleaseMutexForThread(mutex, *iter); 107 woke_threads |= ReleaseMutexForThread(mutex, *iter);
78 mutex->waiting_threads.erase(iter); 108 mutex->waiting_threads.erase(iter);
79 } 109 }
@@ -91,6 +121,9 @@ bool ReleaseMutex(Mutex* mutex) {
91 */ 121 */
92Result ReleaseMutex(Handle handle) { 122Result ReleaseMutex(Handle handle) {
93 Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle); 123 Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle);
124
125 _assert_msg_(KERNEL, (mutex != nullptr), "ReleaseMutex tried to release a nullptr mutex!");
126
94 if (!ReleaseMutex(mutex)) { 127 if (!ReleaseMutex(mutex)) {
95 return -1; 128 return -1;
96 } 129 }
@@ -101,12 +134,15 @@ Result ReleaseMutex(Handle handle) {
101 * Creates a mutex 134 * Creates a mutex
102 * @param handle Reference to handle for the newly created mutex 135 * @param handle Reference to handle for the newly created mutex
103 * @param initial_locked Specifies if the mutex should be locked initially 136 * @param initial_locked Specifies if the mutex should be locked initially
137 * @param name Optional name of mutex
138 * @return Pointer to new Mutex object
104 */ 139 */
105Mutex* CreateMutex(Handle& handle, bool initial_locked) { 140Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) {
106 Mutex* mutex = new Mutex; 141 Mutex* mutex = new Mutex;
107 handle = Kernel::g_object_pool.Create(mutex); 142 handle = Kernel::g_object_pool.Create(mutex);
108 143
109 mutex->locked = mutex->initial_locked = initial_locked; 144 mutex->locked = mutex->initial_locked = initial_locked;
145 mutex->name = name;
110 146
111 // Acquire mutex with current thread if initialized as locked... 147 // Acquire mutex with current thread if initialized as locked...
112 if (mutex->locked) { 148 if (mutex->locked) {
@@ -122,10 +158,12 @@ Mutex* CreateMutex(Handle& handle, bool initial_locked) {
122/** 158/**
123 * Creates a mutex 159 * Creates a mutex
124 * @param initial_locked Specifies if the mutex should be locked initially 160 * @param initial_locked Specifies if the mutex should be locked initially
161 * @param name Optional name of mutex
162 * @return Handle to newly created object
125 */ 163 */
126Handle CreateMutex(bool initial_locked) { 164Handle CreateMutex(bool initial_locked, const std::string& name) {
127 Handle handle; 165 Handle handle;
128 Mutex* mutex = CreateMutex(handle, initial_locked); 166 Mutex* mutex = CreateMutex(handle, initial_locked, name);
129 return handle; 167 return handle;
130} 168}
131 169
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 871e2e562..7d7b5137e 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -13,14 +13,16 @@ namespace Kernel {
13/** 13/**
14 * Releases a mutex 14 * Releases a mutex
15 * @param handle Handle to mutex to release 15 * @param handle Handle to mutex to release
16 * @return Result of operation, 0 on success, otherwise error code
16 */ 17 */
17Result ReleaseMutex(Handle handle); 18Result ReleaseMutex(Handle handle);
18 19
19/** 20/**
20 * Creates a mutex 21 * 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 22 * @param initial_locked Specifies if the mutex should be locked initially
23 * @param name Optional name of mutex
24 * @return Handle to newly created object
23 */ 25 */
24Handle CreateMutex(bool initial_locked); 26Handle CreateMutex(bool initial_locked, const std::string& name="Unknown");
25 27
26} // namespace 28} // namespace
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index bf4c8353c..ab5a5559e 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -5,6 +5,7 @@
5#include <stdio.h> 5#include <stdio.h>
6 6
7#include <list> 7#include <list>
8#include <algorithm>
8#include <vector> 9#include <vector>
9#include <map> 10#include <map>
10#include <string> 11#include <string>
@@ -24,10 +25,10 @@ namespace Kernel {
24class Thread : public Kernel::Object { 25class Thread : public Kernel::Object {
25public: 26public:
26 27
27 const char* GetName() { return name; } 28 const char* GetName() const { return name; }
28 const char* GetTypeName() { return "Thread"; } 29 const char* GetTypeName() const { return "Thread"; }
29 30
30 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; } 31 static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; }
31 Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Thread; } 32 Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Thread; }
32 33
33 inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; } 34 inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
@@ -36,6 +37,23 @@ public:
36 inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } 37 inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
37 inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } 38 inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
38 39
40 /**
41 * Wait for kernel object to synchronize
42 * @param wait Boolean wait set if current thread should wait as a result of sync operation
43 * @return Result of operation, 0 on success, otherwise error code
44 */
45 Result WaitSynchronization(bool* wait) {
46 if (status != THREADSTATUS_DORMANT) {
47 Handle thread = GetCurrentThreadHandle();
48 if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
49 waiting_threads.push_back(thread);
50 }
51 WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle());
52 *wait = true;
53 }
54 return 0;
55 }
56
39 ThreadContext context; 57 ThreadContext context;
40 58
41 u32 status; 59 u32 status;
@@ -49,6 +67,9 @@ public:
49 s32 processor_id; 67 s32 processor_id;
50 68
51 WaitType wait_type; 69 WaitType wait_type;
70 Handle wait_handle;
71
72 std::vector<Handle> waiting_threads;
52 73
53 char name[Kernel::MAX_NAME_LENGTH + 1]; 74 char name[Kernel::MAX_NAME_LENGTH + 1];
54}; 75};
@@ -62,7 +83,6 @@ Common::ThreadQueueList<Handle> g_thread_ready_queue;
62Handle g_current_thread_handle; 83Handle g_current_thread_handle;
63Thread* g_current_thread; 84Thread* g_current_thread;
64 85
65
66/// Gets the current thread 86/// Gets the current thread
67inline Thread* GetCurrentThread() { 87inline Thread* GetCurrentThread() {
68 return g_current_thread; 88 return g_current_thread;
@@ -94,15 +114,15 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
94 memset(&t->context, 0, sizeof(ThreadContext)); 114 memset(&t->context, 0, sizeof(ThreadContext));
95 115
96 t->context.cpu_registers[0] = arg; 116 t->context.cpu_registers[0] = arg;
97 t->context.pc = t->entry_point; 117 t->context.pc = t->context.reg_15 = t->entry_point;
98 t->context.sp = t->stack_top; 118 t->context.sp = t->stack_top;
99 t->context.cpsr = 0x1F; // Usermode 119 t->context.cpsr = 0x1F; // Usermode
100 120
101 if (t->current_priority < lowest_priority) { 121 if (t->current_priority < lowest_priority) {
102 t->current_priority = t->initial_priority; 122 t->current_priority = t->initial_priority;
103 } 123 }
104
105 t->wait_type = WAITTYPE_NONE; 124 t->wait_type = WAITTYPE_NONE;
125 t->wait_handle = 0;
106} 126}
107 127
108/// Change a thread to "ready" state 128/// Change a thread to "ready" state
@@ -122,6 +142,37 @@ void ChangeReadyState(Thread* t, bool ready) {
122 } 142 }
123} 143}
124 144
145/// Verify that a thread has not been released from waiting
146inline bool VerifyWait(const Handle& handle, WaitType type, Handle wait_handle) {
147 Thread* thread = g_object_pool.GetFast<Thread>(handle);
148 _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
149
150 if (type != thread->wait_type || wait_handle != thread->wait_handle)
151 return false;
152
153 return true;
154}
155
156/// Stops the current thread
157void StopThread(Handle handle, const char* reason) {
158 Thread* thread = g_object_pool.GetFast<Thread>(handle);
159 _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
160
161 ChangeReadyState(thread, false);
162 thread->status = THREADSTATUS_DORMANT;
163 for (size_t i = 0; i < thread->waiting_threads.size(); ++i) {
164 const Handle waiting_thread = thread->waiting_threads[i];
165 if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) {
166 ResumeThreadFromWait(waiting_thread);
167 }
168 }
169 thread->waiting_threads.clear();
170
171 // Stopped threads are never waiting.
172 thread->wait_type = WAITTYPE_NONE;
173 thread->wait_handle = 0;
174}
175
125/// Changes a threads state 176/// Changes a threads state
126void ChangeThreadState(Thread* t, ThreadStatus new_status) { 177void ChangeThreadState(Thread* t, ThreadStatus new_status) {
127 if (!t || t->status == new_status) { 178 if (!t || t->status == new_status) {
@@ -132,7 +183,7 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
132 183
133 if (new_status == THREADSTATUS_WAIT) { 184 if (new_status == THREADSTATUS_WAIT) {
134 if (t->wait_type == WAITTYPE_NONE) { 185 if (t->wait_type == WAITTYPE_NONE) {
135 printf("ERROR: Waittype none not allowed here\n"); 186 ERROR_LOG(KERNEL, "Waittype none not allowed");
136 } 187 }
137 } 188 }
138} 189}
@@ -166,7 +217,7 @@ void SwitchContext(Thread* t) {
166 t->wait_type = WAITTYPE_NONE; 217 t->wait_type = WAITTYPE_NONE;
167 LoadContext(t->context); 218 LoadContext(t->context);
168 } else { 219 } else {
169 SetCurrentThread(NULL); 220 SetCurrentThread(nullptr);
170 } 221 }
171} 222}
172 223
@@ -181,26 +232,43 @@ Thread* NextThread() {
181 next = g_thread_ready_queue.pop_first(); 232 next = g_thread_ready_queue.pop_first();
182 } 233 }
183 if (next == 0) { 234 if (next == 0) {
184 return NULL; 235 return nullptr;
185 } 236 }
186 return Kernel::g_object_pool.GetFast<Thread>(next); 237 return Kernel::g_object_pool.GetFast<Thread>(next);
187} 238}
188 239
189/// Puts the current thread in the wait state for the given type 240/// Puts the current thread in the wait state for the given type
190void WaitCurrentThread(WaitType wait_type) { 241void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
191 Thread* t = GetCurrentThread(); 242 Thread* thread = GetCurrentThread();
192 t->wait_type = wait_type; 243 thread->wait_type = wait_type;
193 ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND))); 244 thread->wait_handle = wait_handle;
245 ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
194} 246}
195 247
196/// Resumes a thread from waiting by marking it as "ready" 248/// Resumes a thread from waiting by marking it as "ready"
197void ResumeThreadFromWait(Handle handle) { 249void ResumeThreadFromWait(Handle handle) {
198 u32 error; 250 u32 error;
199 Thread* t = Kernel::g_object_pool.Get<Thread>(handle, error); 251 Thread* thread = Kernel::g_object_pool.Get<Thread>(handle, error);
200 if (t) { 252 if (thread) {
201 t->status &= ~THREADSTATUS_WAIT; 253 thread->status &= ~THREADSTATUS_WAIT;
202 if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { 254 if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
203 ChangeReadyState(t, true); 255 ChangeReadyState(thread, true);
256 }
257 }
258}
259
260/// Prints the thread queue for debugging purposes
261void DebugThreadQueue() {
262 Thread* thread = GetCurrentThread();
263 if (!thread) {
264 return;
265 }
266 INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
267 for (u32 i = 0; i < g_thread_queue.size(); i++) {
268 Handle handle = g_thread_queue[i];
269 s32 priority = g_thread_ready_queue.contains(handle);
270 if (priority != -1) {
271 INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle);
204 } 272 }
205 } 273 }
206} 274}
@@ -212,32 +280,34 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
212 _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), 280 _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
213 "CreateThread priority=%d, outside of allowable range!", priority) 281 "CreateThread priority=%d, outside of allowable range!", priority)
214 282
215 Thread* t = new Thread; 283 Thread* thread = new Thread;
216 284
217 handle = Kernel::g_object_pool.Create(t); 285 handle = Kernel::g_object_pool.Create(thread);
218 286
219 g_thread_queue.push_back(handle); 287 g_thread_queue.push_back(handle);
220 g_thread_ready_queue.prepare(priority); 288 g_thread_ready_queue.prepare(priority);
221 289
222 t->status = THREADSTATUS_DORMANT; 290 thread->status = THREADSTATUS_DORMANT;
223 t->entry_point = entry_point; 291 thread->entry_point = entry_point;
224 t->stack_top = stack_top; 292 thread->stack_top = stack_top;
225 t->stack_size = stack_size; 293 thread->stack_size = stack_size;
226 t->initial_priority = t->current_priority = priority; 294 thread->initial_priority = thread->current_priority = priority;
227 t->processor_id = processor_id; 295 thread->processor_id = processor_id;
228 t->wait_type = WAITTYPE_NONE; 296 thread->wait_type = WAITTYPE_NONE;
229 297 thread->wait_handle = 0;
230 strncpy(t->name, name, Kernel::MAX_NAME_LENGTH); 298
231 t->name[Kernel::MAX_NAME_LENGTH] = '\0'; 299 strncpy(thread->name, name, Kernel::MAX_NAME_LENGTH);
232 300 thread->name[Kernel::MAX_NAME_LENGTH] = '\0';
233 return t; 301
302 return thread;
234} 303}
235 304
236/// Creates a new thread - wrapper for external user 305/// Creates a new thread - wrapper for external user
237Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id, 306Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
238 u32 stack_top, int stack_size) { 307 u32 stack_top, int stack_size) {
239 if (name == NULL) { 308
240 ERROR_LOG(KERNEL, "CreateThread(): NULL name"); 309 if (name == nullptr) {
310 ERROR_LOG(KERNEL, "CreateThread(): nullptr name");
241 return -1; 311 return -1;
242 } 312 }
243 if ((u32)stack_size < 0x200) { 313 if ((u32)stack_size < 0x200) {
@@ -258,20 +328,56 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
258 return -1; 328 return -1;
259 } 329 }
260 Handle handle; 330 Handle handle;
261 Thread* t = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, 331 Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
262 stack_size); 332 stack_size);
263 333
264 ResetThread(t, arg, 0); 334 ResetThread(thread, arg, 0);
335 CallThread(thread);
336
337 return handle;
338}
265 339
266 HLE::EatCycles(32000); 340/// Get the priority of the thread specified by handle
341u32 GetThreadPriority(const Handle handle) {
342 Thread* thread = g_object_pool.GetFast<Thread>(handle);
343 _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
344 return thread->current_priority;
345}
267 346
268 // This won't schedule to the new thread, but it may to one woken from eating cycles. 347/// Set the priority of the thread specified by handle
269 // Technically, this should not eat all at once, and reschedule in the middle, but that's hard. 348Result SetThreadPriority(Handle handle, s32 priority) {
270 HLE::ReSchedule("thread created"); 349 Thread* thread = nullptr;
350 if (!handle) {
351 thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
352 } else {
353 thread = g_object_pool.GetFast<Thread>(handle);
354 }
355 _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
271 356
272 CallThread(t); 357 // If priority is invalid, clamp to valid range
273 358 if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
274 return handle; 359 s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
360 WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority);
361 // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
362 // validity of this
363 priority = new_priority;
364 }
365
366 // Change thread priority
367 s32 old = thread->current_priority;
368 g_thread_ready_queue.remove(old, handle);
369 thread->current_priority = priority;
370 g_thread_ready_queue.prepare(thread->current_priority);
371
372 // Change thread status to "ready" and push to ready queue
373 if (thread->IsRunning()) {
374 thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
375 }
376 if (thread->IsReady()) {
377 g_thread_ready_queue.push_back(thread->current_priority, handle);
378 }
379
380 return 0;
275} 381}
276 382
277/// Sets up the primary application thread 383/// Sets up the primary application thread
@@ -279,10 +385,10 @@ Handle SetupMainThread(s32 priority, int stack_size) {
279 Handle handle; 385 Handle handle;
280 386
281 // Initialize new "main" thread 387 // Initialize new "main" thread
282 Thread* t = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, 388 Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
283 THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); 389 THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
284 390
285 ResetThread(t, 0, 0); 391 ResetThread(thread, 0, 0);
286 392
287 // If running another thread already, set it to "ready" state 393 // If running another thread already, set it to "ready" state
288 Thread* cur = GetCurrentThread(); 394 Thread* cur = GetCurrentThread();
@@ -291,24 +397,31 @@ Handle SetupMainThread(s32 priority, int stack_size) {
291 } 397 }
292 398
293 // Run new "main" thread 399 // Run new "main" thread
294 SetCurrentThread(t); 400 SetCurrentThread(thread);
295 t->status = THREADSTATUS_RUNNING; 401 thread->status = THREADSTATUS_RUNNING;
296 LoadContext(t->context); 402 LoadContext(thread->context);
297 403
298 return handle; 404 return handle;
299} 405}
300 406
407
301/// Reschedules to the next available thread (call after current thread is suspended) 408/// Reschedules to the next available thread (call after current thread is suspended)
302void Reschedule() { 409void Reschedule() {
303 Thread* prev = GetCurrentThread(); 410 Thread* prev = GetCurrentThread();
304 Thread* next = NextThread(); 411 Thread* next = NextThread();
412 HLE::g_reschedule = false;
305 if (next > 0) { 413 if (next > 0) {
414 INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
415
306 SwitchContext(next); 416 SwitchContext(next);
307 417
308 // Hack - automatically change previous thread (which would have been in "wait" state) to 418 // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep
309 // "ready" state, so that we can immediately resume to it when new thread yields. FixMe to 419 // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again.
310 // actually wait for whatever event it is supposed to be waiting on. 420 // This results in the current thread yielding on a VBLANK once, and then it will be
311 ChangeReadyState(prev, true); 421 // immediately placed back in the queue for execution.
422 if (prev->wait_type == WAITTYPE_VBLANK) {
423 ResumeThreadFromWait(prev->GetHandle());
424 }
312 } 425 }
313} 426}
314 427
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 9628f165d..04914ba90 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -34,7 +34,7 @@ enum WaitType {
34 WAITTYPE_NONE, 34 WAITTYPE_NONE,
35 WAITTYPE_SLEEP, 35 WAITTYPE_SLEEP,
36 WAITTYPE_SEMA, 36 WAITTYPE_SEMA,
37 WAITTYPE_EVENTFLAG, 37 WAITTYPE_EVENT,
38 WAITTYPE_THREADEND, 38 WAITTYPE_THREADEND,
39 WAITTYPE_VBLANK, 39 WAITTYPE_VBLANK,
40 WAITTYPE_MUTEX, 40 WAITTYPE_MUTEX,
@@ -53,8 +53,8 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE);
53/// Reschedules to the next available thread (call after current thread is suspended) 53/// Reschedules to the next available thread (call after current thread is suspended)
54void Reschedule(); 54void Reschedule();
55 55
56/// Puts the current thread in the wait state for the given type 56/// Stops the current thread
57void WaitCurrentThread(WaitType wait_type); 57void StopThread(Handle thread, const char* reason);
58 58
59/// Resumes a thread from waiting by marking it as "ready" 59/// Resumes a thread from waiting by marking it as "ready"
60void ResumeThreadFromWait(Handle handle); 60void ResumeThreadFromWait(Handle handle);
@@ -62,9 +62,18 @@ void ResumeThreadFromWait(Handle handle);
62/// Gets the current thread handle 62/// Gets the current thread handle
63Handle GetCurrentThreadHandle(); 63Handle GetCurrentThreadHandle();
64 64
65/// Puts the current thread in the wait state for the given type
66void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
67
65/// Put current thread in a wait state - on WaitSynchronization 68/// Put current thread in a wait state - on WaitSynchronization
66void WaitThread_Synchronization(); 69void WaitThread_Synchronization();
67 70
71/// Get the priority of the thread specified by handle
72u32 GetThreadPriority(const Handle handle);
73
74/// Set the priority of the thread specified by handle
75Result SetThreadPriority(Handle handle, s32 priority);
76
68/// Initialize threading 77/// Initialize threading
69void ThreadingInit(); 78void ThreadingInit();
70 79