aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/hle/kernel
diff options
context:
space:
mode:
authorGravatar bunnei <ericbunnie@gmail.com>2014-06-05 22:35:36 -0400
committerGravatar bunnei <ericbunnie@gmail.com>2014-06-13 09:51:02 -0400
commitf5c7c1543434e25a215286e6db5e71c055ba48cf (patch)
tree488a3fd0c01051453c6f8ccc4867f6b6ea3f2843 /src/core/hle/kernel
parenta002abf1711a53430d3002e81de8221ea24766ee (diff)
Kernel: Added real support for thread and event blocking
- SVC: Added ExitThread support - SVC: Added SignalEvent support - Thread: Added WAITTYPE_EVENT for waiting threads for event signals - Thread: Added support for blocking on other threads to finish (e.g. Thread::Join) - Thread: Added debug function for printing current threads ready for execution - Thread: Removed hack/broken thread ready state code from Kernel::Reschedule - Mutex: Moved WaitCurrentThread from SVC to Mutex::WaitSynchronization - Event: Added support for blocking threads on event signalling Kernel: Added missing algorithm #include for use of std::find on non-Windows platforms.
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r--src/core/hle/kernel/event.cpp71
-rw-r--r--src/core/hle/kernel/event.h7
-rw-r--r--src/core/hle/kernel/mutex.cpp5
-rw-r--r--src/core/hle/kernel/thread.cpp121
-rw-r--r--src/core/hle/kernel/thread.h9
5 files changed, 165 insertions, 48 deletions
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp
index 70e50115..787e9f5f 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/event.cpp
@@ -3,12 +3,14 @@
// 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 {
@@ -20,12 +22,13 @@ public:
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
+ ResetType intitial_reset_type; ///< ResetType specified at Event initialization
+ ResetType reset_type; ///< Current ResetType
- bool locked; ///< Current locked state
- bool permanent_locked; ///< Hack - to set event permanent state (for easy passthrough)
- std::string name; ///< Name of event (optional)
+ 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)
/**
* Synchronize kernel object
@@ -44,8 +47,14 @@ public:
* @return Result of operation, 0 on success, otherwise error code
*/
Result WaitSynchronization(bool* wait) {
- // TODO(bunnei): ImplementMe
*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;
}
@@ -54,6 +63,22 @@ public:
};
/**
+ * 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);
+ if (!evt) {
+ ERROR_LOG(KERNEL, "called with unknown handle=0x%08X", handle);
+ return -1;
+ }
+ 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
@@ -72,18 +97,32 @@ 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
+ * Signals an event
+ * @param handle Handle to event to signal
* @return Result of operation, 0 on success, otherwise error code
*/
-Result SetPermanentLock(Handle handle, const bool permanent_locked) {
+Result SignalEvent(const Handle handle) {
Event* evt = g_object_pool.GetFast<Event>(handle);
if (!evt) {
ERROR_LOG(KERNEL, "called with unknown handle=0x%08X", handle);
return -1;
}
- evt->permanent_locked = permanent_locked;
+ // 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;
}
@@ -93,7 +132,15 @@ Result SetPermanentLock(Handle handle, const bool permanent_locked) {
* @return Result of operation, 0 on success, otherwise error code
*/
Result ClearEvent(Handle handle) {
- return SetEventLocked(handle, true);
+ Event* evt = g_object_pool.GetFast<Event>(handle);
+ if (!evt) {
+ ERROR_LOG(KERNEL, "called with unknown handle=0x%08X", handle);
+ return -1;
+ }
+ if (!evt->permanent_locked) {
+ evt->locked = true;
+ }
+ return 0;
}
/**
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h
index eed09f0e..3527b01f 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/event.h
@@ -28,6 +28,13 @@ Result SetEventLocked(const Handle handle, const bool locked);
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
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index 7e60fbfe..133c4307 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -46,6 +46,11 @@ public:
Result WaitSynchronization(bool* wait) {
// TODO(bunnei): ImplementMe
*wait = locked;
+
+ if (locked) {
+ Kernel::WaitCurrentThread(WAITTYPE_MUTEX);
+ }
+
return 0;
}
};
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index c84fdf91..d372df70 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>
@@ -52,7 +53,14 @@ public:
* @return Result of operation, 0 on success, otherwise error code
*/
Result WaitSynchronization(bool* wait) {
- // TODO(bunnei): ImplementMe
+ 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;
}
@@ -69,6 +77,9 @@ public:
s32 processor_id;
WaitType wait_type;
+ Handle wait_handle;
+
+ std::vector<Handle> waiting_threads;
char name[Kernel::MAX_NAME_LENGTH + 1];
};
@@ -82,7 +93,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;
@@ -114,15 +124,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.cpu_registers[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
@@ -142,6 +152,43 @@ void ChangeReadyState(Thread* t, bool ready) {
}
}
+/// Verify that a thread has not been released from waiting
+inline bool VerifyWait(const Handle& thread, WaitType type, Handle handle) {
+ Handle wait_id = 0;
+ Thread *t = g_object_pool.GetFast<Thread>(thread);
+ if (t) {
+ if (type == t->wait_type && handle == t->wait_handle) {
+ return true;
+ }
+ } else {
+ ERROR_LOG(KERNEL, "thread 0x%08X does not exist", thread);
+ }
+ return false;
+}
+
+/// Stops the current thread
+void StopThread(Handle thread, const char* reason) {
+ u32 error;
+ Thread *t = g_object_pool.Get<Thread>(thread, error);
+ if (t) {
+ ChangeReadyState(t, false);
+ t->status = THREADSTATUS_DORMANT;
+ for (size_t i = 0; i < t->waiting_threads.size(); ++i) {
+ const Handle waiting_thread = t->waiting_threads[i];
+ if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, thread)) {
+ ResumeThreadFromWait(waiting_thread);
+ }
+ }
+ t->waiting_threads.clear();
+
+ // Stopped threads are never waiting.
+ t->wait_type = WAITTYPE_NONE;
+ t->wait_handle = 0;
+ } else {
+ ERROR_LOG(KERNEL, "thread 0x%08X does not exist", thread);
+ }
+}
+
/// Changes a threads state
void ChangeThreadState(Thread* t, ThreadStatus new_status) {
if (!t || t->status == new_status) {
@@ -152,7 +199,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");
}
}
}
@@ -207,9 +254,10 @@ Thread* NextThread() {
}
/// Puts the current thread in the wait state for the given type
-void WaitCurrentThread(WaitType wait_type) {
+void WaitCurrentThread(WaitType wait_type, Handle wait_handle) {
Thread* t = GetCurrentThread();
t->wait_type = wait_type;
+ t->wait_handle = wait_handle;
ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND)));
}
@@ -225,6 +273,22 @@ void ResumeThreadFromWait(Handle handle) {
}
}
+/// 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);
+ }
+ }
+}
+
/// Creates a new thread
Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority,
s32 processor_id, u32 stack_top, int stack_size) {
@@ -233,12 +297,12 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
"CreateThread priority=%d, outside of allowable range!", priority)
Thread* t = new Thread;
-
+
handle = Kernel::g_object_pool.Create(t);
-
+
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;
@@ -246,16 +310,18 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio
t->initial_priority = t->current_priority = priority;
t->processor_id = processor_id;
t->wait_type = WAITTYPE_NONE;
-
+ t->wait_handle = 0;
+
strncpy(t->name, name, Kernel::MAX_NAME_LENGTH);
t->name[Kernel::MAX_NAME_LENGTH] = '\0';
-
+
return t;
}
/// 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");
return -1;
@@ -289,7 +355,7 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3
// 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");
+ //HLE::Reschedule(__func__);
return handle;
}
@@ -363,35 +429,24 @@ Handle SetupMainThread(s32 priority, int stack_size) {
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);
- } else {
- INFO_LOG(KERNEL, "no ready threads, staying on 0x%08X", prev->GetHandle());
-
- // Hack - no other threads are available, so decrement current PC to the last instruction,
- // and then resume current thread. This should always be called on a blocking instruction
- // (e.g. svcWaitSynchronization), and the result should be that the instruction is repeated
- // until it no longer blocks.
-
- // TODO(bunnei): A better solution: Have the CPU switch to an idle thread
-
- ThreadContext ctx;
- SaveContext(ctx);
- ctx.pc -= 4;
- LoadContext(ctx);
- 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 094c8d43..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,6 +62,9 @@ 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();