aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
authorGravatar bunnei <ericbunnie@gmail.com>2014-06-14 12:13:16 -0400
committerGravatar bunnei <ericbunnie@gmail.com>2014-06-14 12:13:16 -0400
commit004df767953a949817da89bddcd5d1379240f769 (patch)
treeb2d54928dcbf3cb4dde0cd5d3277afe7999b7bd9 /src/core/hle/kernel
parentc34ba380011921a9d984136381c3a65a1e2389d5 (diff)
parentb45a38f55794e47b0429a8667441a20433e19e42 (diff)
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 00000000..127c0cfc
--- /dev/null
+++ b/src/core/hle/kernel/event.cpp
@@ -0,0 +1,159 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#include <map>
+#include <algorithm>
+#include <vector>
+
+#include "common/common.h"
+
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/thread.h"
+
+namespace Kernel {
+
+class Event : public Object {
+public:
+ const char* GetTypeName() const { return "Event"; }
+ const char* GetName() const { return name.c_str(); }
+
+ static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Event; }
+ Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Event; }
+
+ ResetType intitial_reset_type; ///< ResetType specified at Event initialization
+ ResetType reset_type; ///< Current ResetType
+
+ bool locked; ///< Event signal wait
+ bool permanent_locked; ///< Hack - to set event permanent state (for easy passthrough)
+ std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event
+ std::string name; ///< Name of event (optional)
+
+ /**
+ * Wait for kernel object to synchronize
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ Result WaitSynchronization(bool* wait) {
+ *wait = locked;
+ if (locked) {
+ Handle thread = GetCurrentThreadHandle();
+ if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
+ waiting_threads.push_back(thread);
+ }
+ Kernel::WaitCurrentThread(WAITTYPE_EVENT);
+ }
+ if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
+ locked = true;
+ }
+ return 0;
+ }
+};
+
+/**
+ * Hackish function to set an events permanent lock state, used to pass through synch blocks
+ * @param handle Handle to event to change
+ * @param permanent_locked Boolean permanent locked value to set event
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result SetPermanentLock(Handle handle, const bool permanent_locked) {
+ Event* evt = g_object_pool.GetFast<Event>(handle);
+ _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+
+ evt->permanent_locked = permanent_locked;
+ return 0;
+}
+
+/**
+ * Changes whether an event is locked or not
+ * @param handle Handle to event to change
+ * @param locked Boolean locked value to set event
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result SetEventLocked(const Handle handle, const bool locked) {
+ Event* evt = g_object_pool.GetFast<Event>(handle);
+ _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+
+ if (!evt->permanent_locked) {
+ evt->locked = locked;
+ }
+ return 0;
+}
+
+/**
+ * Signals an event
+ * @param handle Handle to event to signal
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result SignalEvent(const Handle handle) {
+ Event* evt = g_object_pool.GetFast<Event>(handle);
+ _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+
+ // Resume threads waiting for event to signal
+ bool event_caught = false;
+ for (size_t i = 0; i < evt->waiting_threads.size(); ++i) {
+ ResumeThreadFromWait( evt->waiting_threads[i]);
+
+ // If any thread is signalled awake by this event, assume the event was "caught" and reset
+ // the event. This will result in the next thread waiting on the event to block. Otherwise,
+ // the event will not be reset, and the next thread to call WaitSynchronization on it will
+ // not block. Not sure if this is correct behavior, but it seems to work.
+ event_caught = true;
+ }
+ evt->waiting_threads.clear();
+
+ if (!evt->permanent_locked) {
+ evt->locked = event_caught;
+ }
+ return 0;
+}
+
+/**
+ * Clears an event
+ * @param handle Handle to event to clear
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result ClearEvent(Handle handle) {
+ Event* evt = g_object_pool.GetFast<Event>(handle);
+ _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!");
+
+ if (!evt->permanent_locked) {
+ evt->locked = true;
+ }
+ return 0;
+}
+
+/**
+ * Creates an event
+ * @param handle Reference to handle for the newly created mutex
+ * @param reset_type ResetType describing how to create event
+ * @param name Optional name of event
+ * @return Newly created Event object
+ */
+Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) {
+ Event* evt = new Event;
+
+ handle = Kernel::g_object_pool.Create(evt);
+
+ evt->locked = true;
+ evt->permanent_locked = false;
+ evt->reset_type = evt->intitial_reset_type = reset_type;
+ evt->name = name;
+
+ return evt;
+}
+
+/**
+ * Creates an event
+ * @param reset_type ResetType describing how to create event
+ * @param name Optional name of event
+ * @return Handle to newly created Event object
+ */
+Handle CreateEvent(const ResetType reset_type, const std::string& name) {
+ Handle handle;
+ Event* evt = CreateEvent(handle, reset_type, name);
+ return handle;
+}
+
+} // namespace
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
new file mode 100644
index 00000000..c39b3318
--- /dev/null
+++ b/src/core/hle/kernel/event.h
@@ -0,0 +1,52 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/svc.h"
+
+namespace Kernel {
+
+/**
+ * Changes whether an event is locked or not
+ * @param handle Handle to event to change
+ * @param locked Boolean locked value to set event
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result SetEventLocked(const Handle handle, const bool locked);
+
+/**
+ * Hackish function to set an events permanent lock state, used to pass through synch blocks
+ * @param handle Handle to event to change
+ * @param permanent_locked Boolean permanent locked value to set event
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result SetPermanentLock(Handle handle, const bool permanent_locked);
+
+/**
+ * Signals an event
+ * @param handle Handle to event to signal
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result SignalEvent(const Handle handle);
+
+/**
+ * Clears an event
+ * @param handle Handle to event to clear
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+Result ClearEvent(Handle handle);
+
+/**
+ * Creates an event
+ * @param reset_type ResetType describing how to create event
+ * @param name Optional name of event
+ * @return Handle to newly created Event object
+ */
+Handle CreateEvent(const ResetType reset_type, const std::string& name="Unknown");
+
+} // namespace
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index de80de89..cda183ad 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -2,8 +2,6 @@
// Licensed under GPLv2
// Refer to the license.txt file included.
-#pragma once
-
#include <string.h>
#include "common/common.h"
@@ -14,6 +12,7 @@
namespace Kernel {
+Handle g_main_thread = 0;
ObjectPool g_object_pool;
ObjectPool::ObjectPool() {
@@ -127,16 +126,20 @@ Object* ObjectPool::CreateByIDType(int type) {
default:
ERROR_LOG(COMMON, "Unable to load state: could not find object type %d.", type);
- return NULL;
+ return nullptr;
}
}
+/// Initialize the kernel
void Init() {
Kernel::ThreadingInit();
}
+/// Shutdown the kernel
void Shutdown() {
Kernel::ThreadingShutdown();
+
+ g_object_pool.Clear(); // Free all kernel objects
}
/**
@@ -150,7 +153,7 @@ bool LoadExec(u32 entry_point) {
Core::g_app_core->SetPC(entry_point);
// 0x30 is the typical main thread priority I've seen used so far
- Handle thread = Kernel::SetupMainThread(0x30);
+ g_main_thread = Kernel::SetupMainThread(0x30);
return true;
}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 7cd79c2c..3f15da0a 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -11,6 +11,11 @@ typedef s32 Result;
namespace Kernel {
+enum KernelHandle {
+ CurrentThread = 0xFFFF8000,
+ CurrentProcess = 0xFFFF8001,
+};
+
enum class HandleType : u32 {
Unknown = 0,
Port = 1,
@@ -39,9 +44,26 @@ class Object : NonCopyable {
public:
virtual ~Object() {}
Handle GetHandle() const { return handle; }
- virtual const char *GetTypeName() { return "[BAD KERNEL OBJECT TYPE]"; }
- virtual const char *GetName() { return "[UNKNOWN KERNEL OBJECT]"; }
+ virtual const char* GetTypeName() const { return "[BAD KERNEL OBJECT TYPE]"; }
+ virtual const char* GetName() const { return "[UNKNOWN KERNEL OBJECT]"; }
virtual Kernel::HandleType GetHandleType() const = 0;
+
+ /**
+ * Synchronize kernel object
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ virtual Result SyncRequest(bool* wait) {
+ ERROR_LOG(KERNEL, "(UNIMPLEMENTED)");
+ return -1;
+ }
+
+ /**
+ * Wait for kernel object to synchronize
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ virtual Result WaitSynchronization(bool* wait) = 0;
};
class ObjectPool : NonCopyable {
@@ -143,6 +165,13 @@ private:
};
extern ObjectPool g_object_pool;
+extern Handle g_main_thread;
+
+/// Initialize the kernel
+void Init();
+
+/// Shutdown the kernel
+void Shutdown();
/**
* Loads executable stored at specified address
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 019efbc7..1ccf1eb7 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -8,21 +8,51 @@
#include "common/common.h"
#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
class Mutex : public Object {
public:
- const char* GetTypeName() { return "Mutex"; }
+ const char* GetTypeName() const { return "Mutex"; }
+ const char* GetName() const { return name.c_str(); }
- static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; }
+ static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Mutex; }
Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Mutex; }
bool initial_locked; ///< Initial lock state when mutex was created
bool locked; ///< Current locked state
Handle lock_thread; ///< Handle to thread that currently has mutex
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex
+ std::string name; ///< Name of mutex (optional)
+
+ /**
+ * Synchronize kernel object
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ Result SyncRequest(bool* wait) {
+ // TODO(bunnei): ImplementMe
+ locked = true;
+ return 0;
+ }
+
+ /**
+ * Wait for kernel object to synchronize
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ Result WaitSynchronization(bool* wait) {
+ // TODO(bunnei): ImplementMe
+ *wait = locked;
+
+ if (locked) {
+ Kernel::WaitCurrentThread(WAITTYPE_MUTEX);
+ }
+
+ return 0;
+ }
};
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -70,10 +100,10 @@ bool ReleaseMutexForThread(Mutex* mutex, Handle thread) {
bool ReleaseMutex(Mutex* mutex) {
MutexEraseLock(mutex);
bool woke_threads = false;
- auto iter = mutex->waiting_threads.begin();
// Find the next waiting thread for the mutex...
while (!woke_threads && !mutex->waiting_threads.empty()) {
+ std::vector<Handle>::iterator iter = mutex->waiting_threads.begin();
woke_threads |= ReleaseMutexForThread(mutex, *iter);
mutex->waiting_threads.erase(iter);
}
@@ -91,6 +121,9 @@ bool ReleaseMutex(Mutex* mutex) {
*/
Result ReleaseMutex(Handle handle) {
Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle);
+
+ _assert_msg_(KERNEL, (mutex != nullptr), "ReleaseMutex tried to release a nullptr mutex!");
+
if (!ReleaseMutex(mutex)) {
return -1;
}
@@ -101,12 +134,15 @@ Result ReleaseMutex(Handle handle) {
* Creates a mutex
* @param handle Reference to handle for the newly created mutex
* @param initial_locked Specifies if the mutex should be locked initially
+ * @param name Optional name of mutex
+ * @return Pointer to new Mutex object
*/
-Mutex* CreateMutex(Handle& handle, bool initial_locked) {
+Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) {
Mutex* mutex = new Mutex;
handle = Kernel::g_object_pool.Create(mutex);
mutex->locked = mutex->initial_locked = initial_locked;
+ mutex->name = name;
// Acquire mutex with current thread if initialized as locked...
if (mutex->locked) {
@@ -122,10 +158,12 @@ Mutex* CreateMutex(Handle& handle, bool initial_locked) {
/**
* Creates a mutex
* @param initial_locked Specifies if the mutex should be locked initially
+ * @param name Optional name of mutex
+ * @return Handle to newly created object
*/
-Handle CreateMutex(bool initial_locked) {
+Handle CreateMutex(bool initial_locked, const std::string& name) {
Handle handle;
- Mutex* mutex = CreateMutex(handle, initial_locked);
+ Mutex* mutex = CreateMutex(handle, initial_locked, name);
return handle;
}
diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h
index 871e2e56..7d7b5137 100644
--- a/src/core/hle/kernel/mutex.h
+++ b/src/core/hle/kernel/mutex.h
@@ -13,14 +13,16 @@ namespace Kernel {
/**
* Releases a mutex
* @param handle Handle to mutex to release
+ * @return Result of operation, 0 on success, otherwise error code
*/
Result ReleaseMutex(Handle handle);
/**
* Creates a mutex
- * @param handle Reference to handle for the newly created mutex
* @param initial_locked Specifies if the mutex should be locked initially
+ * @param name Optional name of mutex
+ * @return Handle to newly created object
*/
-Handle CreateMutex(bool initial_locked);
+Handle CreateMutex(bool initial_locked, const std::string& name="Unknown");
} // namespace
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index bf4c8353..ab5a5559 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -5,6 +5,7 @@
#include <stdio.h>
#include <list>
+#include <algorithm>
#include <vector>
#include <map>
#include <string>
@@ -24,10 +25,10 @@ namespace Kernel {
class Thread : public Kernel::Object {
public:
- const char* GetName() { return name; }
- const char* GetTypeName() { return "Thread"; }
+ const char* GetName() const { return name; }
+ const char* GetTypeName() const { return "Thread"; }
- static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; }
+ static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Thread; }
Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Thread; }
inline bool IsRunning() const { return (status & THREADSTATUS_RUNNING) != 0; }
@@ -36,6 +37,23 @@ public:
inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; }
inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; }
+ /**
+ * Wait for kernel object to synchronize
+ * @param wait Boolean wait set if current thread should wait as a result of sync operation
+ * @return Result of operation, 0 on success, otherwise error code
+ */
+ Result WaitSynchronization(bool* wait) {
+ if (status != THREADSTATUS_DORMANT) {
+ Handle thread = GetCurrentThreadHandle();
+ if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) {
+ waiting_threads.push_back(thread);
+ }
+ WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle());
+ *wait = true;
+ }
+ return 0;
+ }
+
ThreadContext context;
u32 status;
@@ -49,6 +67,9 @@ public:
s32 processor_id;
WaitType wait_type;
+ Handle wait_handle;
+
+ std::vector<Handle> waiting_threads;
char name[Kernel::MAX_NAME_LENGTH + 1];
};
@@ -62,7 +83,6 @@ Common::ThreadQueueList<Handle> g_thread_ready_queue;
Handle g_current_thread_handle;
Thread* g_current_thread;
-
/// Gets the current thread
inline Thread* GetCurrentThread() {
return g_current_thread;
@@ -94,15 +114,15 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
memset(&t->context, 0, sizeof(ThreadContext));
t->context.cpu_registers[0] = arg;
- t->context.pc = t->entry_point;
+ t->context.pc = t->context.reg_15 = t->entry_point;
t->context.sp = t->stack_top;
t->context.cpsr = 0x1F; // Usermode
if (t->current_priority < lowest_priority) {
t->current_priority = t->initial_priority;
}
-
t->wait_type = WAITTYPE_NONE;
+ t->wait_handle = 0;
}
/// Change a thread to "ready" state
@@ -122,6 +142,37 @@ void ChangeReadyState(Thread* t, bool ready) {
}
}
+/// Verify that a thread has not been released from waiting
+inline bool VerifyWait(const Handle& handle, WaitType type, Handle wait_handle) {
+ Thread* thread = g_object_pool.GetFast<Thread>(handle);
+ _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
+
+ if (type != thread->wait_type || wait_handle != thread->wait_handle)
+ return false;
+
+ return true;
+}
+
+/// Stops the current thread
+void StopThread(Handle handle, const char* reason) {
+ Thread* thread = g_object_pool.GetFast<Thread>(handle);
+ _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
+
+ ChangeReadyState(thread, false);
+ thread->status = THREADSTATUS_DORMANT;
+ for (size_t i = 0; i < thread->waiting_threads.size(); ++i) {
+ const Handle waiting_thread = thread->waiting_threads[i];
+ if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) {
+ ResumeThreadFromWait(waiting_thread);
+ }
+ }
+ thread->waiting_threads.clear();
+
+ // Stopped threads are never waiting.
+ thread->wait_type = WAITTYPE_NONE;
+ thread->wait_handle = 0;
+}
+
/// Changes a threads state
void ChangeThreadState(Thread* t, ThreadStatus new_status) {
if (!t || t->status == new_status) {
@@ -132,7 +183,7 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) {
if (new_status == THREADSTATUS_WAIT) {
if (t->wait_type == WAITTYPE_NONE) {
- printf("ERROR: Waittype none not allowed here\n");
+ ERROR_LOG(KERNEL, "Waittype none not allowed");
}
}
}
@@ -166,7 +217,7 @@ void SwitchContext(Thread* t) {
t->wait_type = WAITTYPE_NONE;
LoadContext(t->context);
} else {
- SetCurrentThread(NULL);
+ SetCurrentThread(nullptr);
}
}
@@ -181,26 +232,43 @@ Thread* NextThread() {
next = g_thread_ready_queue.pop_first();
}
if (next == 0) {
- return NULL;
+ return nullptr;
}
return Kernel::g_object_pool.GetFast<Thread>(next);
}
/// Puts the current thread in the wait state for the given type
-void WaitCurrentThread(WaitType wait_type) {
- Thread* t = GetCurrentThread();
- t->wait_type = wait_type;
- ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND)));
+void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
+ Thread* thread = GetCurrentThread();
+ thread->wait_type = wait_type;
+ thread->wait_handle = wait_handle;
+ ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND)));
}
/// Resumes a thread from waiting by marking it as "ready"
void ResumeThreadFromWait(Handle handle) {
u32 error;
- Thread* t = Kernel::g_object_pool.Get<Thread>(handle, error);
- if (t) {
- t->status &= ~THREADSTATUS_WAIT;
- if (!(t->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
- ChangeReadyState(t, true);
+ Thread* thread = Kernel::g_object_pool.Get<Thread>(handle, error);
+ if (thread) {
+ thread->status &= ~THREADSTATUS_WAIT;
+ if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
+ ChangeReadyState(thread, true);
+ }
+ }
+}
+
+/// Prints the thread queue for debugging purposes
+void DebugThreadQueue() {
+ Thread* thread = GetCurrentThread();
+ if (!thread) {
+ return;
+ }
+ INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle());
+ for (u32 i = 0; i < g_thread_queue.size(); i++) {
+ Handle handle = g_thread_queue[i];
+ s32 priority = g_thread_ready_queue.contains(handle);
+ if (priority != -1) {
+ INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle);
}
}
}
@@ -212,32 +280,34 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
_assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST),
"CreateThread priority=%d, outside of allowable range!", priority)
- Thread* t = new Thread;
-
- handle = Kernel::g_object_pool.Create(t);
-
+ Thread* thread = new Thread;
+
+ handle = Kernel::g_object_pool.Create(thread);
+
g_thread_queue.push_back(handle);
g_thread_ready_queue.prepare(priority);
-
- t->status = THREADSTATUS_DORMANT;
- t->entry_point = entry_point;
- t->stack_top = stack_top;
- t->stack_size = stack_size;
- t->initial_priority = t->current_priority = priority;
- t->processor_id = processor_id;
- t->wait_type = WAITTYPE_NONE;
-
- strncpy(t->name, name, Kernel::MAX_NAME_LENGTH);
- t->name[Kernel::MAX_NAME_LENGTH] = '\0';
-
- return t;
+
+ thread->status = THREADSTATUS_DORMANT;
+ thread->entry_point = entry_point;
+ thread->stack_top = stack_top;
+ thread->stack_size = stack_size;
+ thread->initial_priority = thread->current_priority = priority;
+ thread->processor_id = processor_id;
+ thread->wait_type = WAITTYPE_NONE;
+ thread->wait_handle = 0;
+
+ strncpy(thread->name, name, Kernel::MAX_NAME_LENGTH);
+ thread->name[Kernel::MAX_NAME_LENGTH] = '\0';
+
+ return thread;
}
/// Creates a new thread - wrapper for external user
Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
u32 stack_top, int stack_size) {
- if (name == NULL) {
- ERROR_LOG(KERNEL, "CreateThread(): NULL name");
+
+ if (name == nullptr) {
+ ERROR_LOG(KERNEL, "CreateThread(): nullptr name");
return -1;
}
if ((u32)stack_size < 0x200) {
@@ -258,20 +328,56 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
return -1;
}
Handle handle;
- Thread* t = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
+ Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top,
stack_size);
- ResetThread(t, arg, 0);
+ ResetThread(thread, arg, 0);
+ CallThread(thread);
+
+ return handle;
+}
- HLE::EatCycles(32000);
+/// Get the priority of the thread specified by handle
+u32 GetThreadPriority(const Handle handle) {
+ Thread* thread = g_object_pool.GetFast<Thread>(handle);
+ _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
+ return thread->current_priority;
+}
- // This won't schedule to the new thread, but it may to one woken from eating cycles.
- // Technically, this should not eat all at once, and reschedule in the middle, but that's hard.
- HLE::ReSchedule("thread created");
+/// Set the priority of the thread specified by handle
+Result SetThreadPriority(Handle handle, s32 priority) {
+ Thread* thread = nullptr;
+ if (!handle) {
+ thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior?
+ } else {
+ thread = g_object_pool.GetFast<Thread>(handle);
+ }
+ _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!");
- CallThread(t);
-
- return handle;
+ // If priority is invalid, clamp to valid range
+ if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) {
+ s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST);
+ WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority);
+ // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm
+ // validity of this
+ priority = new_priority;
+ }
+
+ // Change thread priority
+ s32 old = thread->current_priority;
+ g_thread_ready_queue.remove(old, handle);
+ thread->current_priority = priority;
+ g_thread_ready_queue.prepare(thread->current_priority);
+
+ // Change thread status to "ready" and push to ready queue
+ if (thread->IsRunning()) {
+ thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY;
+ }
+ if (thread->IsReady()) {
+ g_thread_ready_queue.push_back(thread->current_priority, handle);
+ }
+
+ return 0;
}
/// Sets up the primary application thread
@@ -279,10 +385,10 @@ Handle SetupMainThread(s32 priority, int stack_size) {
Handle handle;
// Initialize new "main" thread
- Thread* t = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
+ Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority,
THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size);
- ResetThread(t, 0, 0);
+ ResetThread(thread, 0, 0);
// If running another thread already, set it to "ready" state
Thread* cur = GetCurrentThread();
@@ -291,24 +397,31 @@ Handle SetupMainThread(s32 priority, int stack_size) {
}
// Run new "main" thread
- SetCurrentThread(t);
- t->status = THREADSTATUS_RUNNING;
- LoadContext(t->context);
+ SetCurrentThread(thread);
+ thread->status = THREADSTATUS_RUNNING;
+ LoadContext(thread->context);
return handle;
}
+
/// Reschedules to the next available thread (call after current thread is suspended)
void Reschedule() {
Thread* prev = GetCurrentThread();
Thread* next = NextThread();
+ HLE::g_reschedule = false;
if (next > 0) {
+ INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
+
SwitchContext(next);
- // Hack - automatically change previous thread (which would have been in "wait" state) to
- // "ready" state, so that we can immediately resume to it when new thread yields. FixMe to
- // actually wait for whatever event it is supposed to be waiting on.
- ChangeReadyState(prev, true);
+ // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep
+ // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again.
+ // This results in the current thread yielding on a VBLANK once, and then it will be
+ // immediately placed back in the queue for execution.
+ if (prev->wait_type == WAITTYPE_VBLANK) {
+ ResumeThreadFromWait(prev->GetHandle());
+ }
}
}
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 9628f165..04914ba9 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -34,7 +34,7 @@ enum WaitType {
WAITTYPE_NONE,
WAITTYPE_SLEEP,
WAITTYPE_SEMA,
- WAITTYPE_EVENTFLAG,
+ WAITTYPE_EVENT,
WAITTYPE_THREADEND,
WAITTYPE_VBLANK,
WAITTYPE_MUTEX,
@@ -53,8 +53,8 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE);
/// Reschedules to the next available thread (call after current thread is suspended)
void Reschedule();
-/// Puts the current thread in the wait state for the given type
-void WaitCurrentThread(WaitType wait_type);
+/// Stops the current thread
+void StopThread(Handle thread, const char* reason);
/// Resumes a thread from waiting by marking it as "ready"
void ResumeThreadFromWait(Handle handle);
@@ -62,9 +62,18 @@ void ResumeThreadFromWait(Handle handle);
/// Gets the current thread handle
Handle GetCurrentThreadHandle();
+/// Puts the current thread in the wait state for the given type
+void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
+
/// Put current thread in a wait state - on WaitSynchronization
void WaitThread_Synchronization();
+/// Get the priority of the thread specified by handle
+u32 GetThreadPriority(const Handle handle);
+
+/// Set the priority of the thread specified by handle
+Result SetThreadPriority(Handle handle, s32 priority);
+
/// Initialize threading
void ThreadingInit();