mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2024-12-24 17:01:48 +01:00
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.
This commit is contained in:
parent
a002abf171
commit
f5c7c15434
@ -3,12 +3,14 @@
|
|||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "common/common.h"
|
#include "common/common.h"
|
||||||
|
|
||||||
#include "core/hle/kernel/kernel.h"
|
#include "core/hle/kernel/kernel.h"
|
||||||
#include "core/hle/kernel/event.h"
|
#include "core/hle/kernel/event.h"
|
||||||
|
#include "core/hle/kernel/thread.h"
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
@ -20,12 +22,13 @@ public:
|
|||||||
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Event; }
|
static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Event; }
|
||||||
Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Event; }
|
Kernel::HandleType GetHandleType() const { return Kernel::HandleType::Event; }
|
||||||
|
|
||||||
ResetType intitial_reset_type; ///< ResetType specified at Event initialization
|
ResetType intitial_reset_type; ///< ResetType specified at Event initialization
|
||||||
ResetType reset_type; ///< Current ResetType
|
ResetType reset_type; ///< Current ResetType
|
||||||
|
|
||||||
bool locked; ///< Current locked state
|
bool locked; ///< Event signal wait
|
||||||
bool permanent_locked; ///< Hack - to set event permanent state (for easy passthrough)
|
bool permanent_locked; ///< Hack - to set event permanent state (for easy passthrough)
|
||||||
std::string name; ///< Name of event (optional)
|
std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event
|
||||||
|
std::string name; ///< Name of event (optional)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronize kernel object
|
* Synchronize kernel object
|
||||||
@ -44,8 +47,14 @@ public:
|
|||||||
* @return Result of operation, 0 on success, otherwise error code
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
*/
|
*/
|
||||||
Result WaitSynchronization(bool* wait) {
|
Result WaitSynchronization(bool* wait) {
|
||||||
// TODO(bunnei): ImplementMe
|
|
||||||
*wait = locked;
|
*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) {
|
if (reset_type != RESETTYPE_STICKY && !permanent_locked) {
|
||||||
locked = true;
|
locked = true;
|
||||||
}
|
}
|
||||||
@ -53,6 +62,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
|
* Changes whether an event is locked or not
|
||||||
* @param handle Handle to event to change
|
* @param handle Handle to event to change
|
||||||
@ -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
|
* Signals an event
|
||||||
* @param handle Handle to event to change
|
* @param handle Handle to event to signal
|
||||||
* @param permanent_locked Boolean permanent locked value to set event
|
|
||||||
* @return Result of operation, 0 on success, otherwise error code
|
* @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);
|
Event* evt = g_object_pool.GetFast<Event>(handle);
|
||||||
if (!evt) {
|
if (!evt) {
|
||||||
ERROR_LOG(KERNEL, "called with unknown handle=0x%08X", handle);
|
ERROR_LOG(KERNEL, "called with unknown handle=0x%08X", handle);
|
||||||
return -1;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +132,15 @@ Result SetPermanentLock(Handle handle, const bool permanent_locked) {
|
|||||||
* @return Result of operation, 0 on success, otherwise error code
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
*/
|
*/
|
||||||
Result ClearEvent(Handle handle) {
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -27,6 +27,13 @@ Result SetEventLocked(const Handle handle, const bool locked);
|
|||||||
*/
|
*/
|
||||||
Result SetPermanentLock(Handle handle, const bool permanent_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
|
* Clears an event
|
||||||
* @param handle Handle to event to clear
|
* @param handle Handle to event to clear
|
||||||
|
@ -46,6 +46,11 @@ public:
|
|||||||
Result WaitSynchronization(bool* wait) {
|
Result WaitSynchronization(bool* wait) {
|
||||||
// TODO(bunnei): ImplementMe
|
// TODO(bunnei): ImplementMe
|
||||||
*wait = locked;
|
*wait = locked;
|
||||||
|
|
||||||
|
if (locked) {
|
||||||
|
Kernel::WaitCurrentThread(WAITTYPE_MUTEX);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -52,7 +53,14 @@ public:
|
|||||||
* @return Result of operation, 0 on success, otherwise error code
|
* @return Result of operation, 0 on success, otherwise error code
|
||||||
*/
|
*/
|
||||||
Result WaitSynchronization(bool* wait) {
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +77,9 @@ public:
|
|||||||
s32 processor_id;
|
s32 processor_id;
|
||||||
|
|
||||||
WaitType wait_type;
|
WaitType wait_type;
|
||||||
|
Handle wait_handle;
|
||||||
|
|
||||||
|
std::vector<Handle> waiting_threads;
|
||||||
|
|
||||||
char name[Kernel::MAX_NAME_LENGTH + 1];
|
char name[Kernel::MAX_NAME_LENGTH + 1];
|
||||||
};
|
};
|
||||||
@ -82,7 +93,6 @@ Common::ThreadQueueList<Handle> g_thread_ready_queue;
|
|||||||
Handle g_current_thread_handle;
|
Handle g_current_thread_handle;
|
||||||
Thread* g_current_thread;
|
Thread* g_current_thread;
|
||||||
|
|
||||||
|
|
||||||
/// Gets the current thread
|
/// Gets the current thread
|
||||||
inline Thread* GetCurrentThread() {
|
inline Thread* GetCurrentThread() {
|
||||||
return g_current_thread;
|
return g_current_thread;
|
||||||
@ -114,15 +124,15 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) {
|
|||||||
memset(&t->context, 0, sizeof(ThreadContext));
|
memset(&t->context, 0, sizeof(ThreadContext));
|
||||||
|
|
||||||
t->context.cpu_registers[0] = arg;
|
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.sp = t->stack_top;
|
||||||
t->context.cpsr = 0x1F; // Usermode
|
t->context.cpsr = 0x1F; // Usermode
|
||||||
|
|
||||||
if (t->current_priority < lowest_priority) {
|
if (t->current_priority < lowest_priority) {
|
||||||
t->current_priority = t->initial_priority;
|
t->current_priority = t->initial_priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
t->wait_type = WAITTYPE_NONE;
|
t->wait_type = WAITTYPE_NONE;
|
||||||
|
t->wait_handle = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change a thread to "ready" state
|
/// 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
|
/// Changes a threads state
|
||||||
void ChangeThreadState(Thread* t, ThreadStatus new_status) {
|
void ChangeThreadState(Thread* t, ThreadStatus new_status) {
|
||||||
if (!t || t->status == 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 (new_status == THREADSTATUS_WAIT) {
|
||||||
if (t->wait_type == WAITTYPE_NONE) {
|
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
|
/// 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();
|
Thread* t = GetCurrentThread();
|
||||||
t->wait_type = wait_type;
|
t->wait_type = wait_type;
|
||||||
|
t->wait_handle = wait_handle;
|
||||||
ChangeThreadState(t, ThreadStatus(THREADSTATUS_WAIT | (t->status & THREADSTATUS_SUSPEND)));
|
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
|
/// Creates a new thread
|
||||||
Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority,
|
Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority,
|
||||||
s32 processor_id, u32 stack_top, int stack_size) {
|
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)
|
"CreateThread priority=%d, outside of allowable range!", priority)
|
||||||
|
|
||||||
Thread* t = new Thread;
|
Thread* t = new Thread;
|
||||||
|
|
||||||
handle = Kernel::g_object_pool.Create(t);
|
handle = Kernel::g_object_pool.Create(t);
|
||||||
|
|
||||||
g_thread_queue.push_back(handle);
|
g_thread_queue.push_back(handle);
|
||||||
g_thread_ready_queue.prepare(priority);
|
g_thread_ready_queue.prepare(priority);
|
||||||
|
|
||||||
t->status = THREADSTATUS_DORMANT;
|
t->status = THREADSTATUS_DORMANT;
|
||||||
t->entry_point = entry_point;
|
t->entry_point = entry_point;
|
||||||
t->stack_top = stack_top;
|
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->initial_priority = t->current_priority = priority;
|
||||||
t->processor_id = processor_id;
|
t->processor_id = processor_id;
|
||||||
t->wait_type = WAITTYPE_NONE;
|
t->wait_type = WAITTYPE_NONE;
|
||||||
|
t->wait_handle = 0;
|
||||||
|
|
||||||
strncpy(t->name, name, Kernel::MAX_NAME_LENGTH);
|
strncpy(t->name, name, Kernel::MAX_NAME_LENGTH);
|
||||||
t->name[Kernel::MAX_NAME_LENGTH] = '\0';
|
t->name[Kernel::MAX_NAME_LENGTH] = '\0';
|
||||||
|
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new thread - wrapper for external user
|
/// Creates a new thread - wrapper for external user
|
||||||
Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
|
Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s32 processor_id,
|
||||||
u32 stack_top, int stack_size) {
|
u32 stack_top, int stack_size) {
|
||||||
|
|
||||||
if (name == NULL) {
|
if (name == NULL) {
|
||||||
ERROR_LOG(KERNEL, "CreateThread(): NULL name");
|
ERROR_LOG(KERNEL, "CreateThread(): NULL name");
|
||||||
return -1;
|
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.
|
// 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.
|
// 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;
|
return handle;
|
||||||
}
|
}
|
||||||
@ -363,35 +429,24 @@ Handle SetupMainThread(s32 priority, int stack_size) {
|
|||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Reschedules to the next available thread (call after current thread is suspended)
|
/// Reschedules to the next available thread (call after current thread is suspended)
|
||||||
void Reschedule() {
|
void Reschedule() {
|
||||||
Thread* prev = GetCurrentThread();
|
Thread* prev = GetCurrentThread();
|
||||||
Thread* next = NextThread();
|
Thread* next = NextThread();
|
||||||
|
HLE::g_reschedule = false;
|
||||||
if (next > 0) {
|
if (next > 0) {
|
||||||
INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
|
INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
|
||||||
|
|
||||||
SwitchContext(next);
|
SwitchContext(next);
|
||||||
|
|
||||||
// Hack - automatically change previous thread (which would have been in "wait" state) to
|
// Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep
|
||||||
// "ready" state, so that we can immediately resume to it when new thread yields. FixMe to
|
// by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again.
|
||||||
// actually wait for whatever event it is supposed to be waiting on.
|
// This results in the current thread yielding on a VBLANK once, and then it will be
|
||||||
|
// immediately placed back in the queue for execution.
|
||||||
ChangeReadyState(prev, true);
|
if (prev->wait_type == WAITTYPE_VBLANK) {
|
||||||
} else {
|
ResumeThreadFromWait(prev->GetHandle());
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ enum WaitType {
|
|||||||
WAITTYPE_NONE,
|
WAITTYPE_NONE,
|
||||||
WAITTYPE_SLEEP,
|
WAITTYPE_SLEEP,
|
||||||
WAITTYPE_SEMA,
|
WAITTYPE_SEMA,
|
||||||
WAITTYPE_EVENTFLAG,
|
WAITTYPE_EVENT,
|
||||||
WAITTYPE_THREADEND,
|
WAITTYPE_THREADEND,
|
||||||
WAITTYPE_VBLANK,
|
WAITTYPE_VBLANK,
|
||||||
WAITTYPE_MUTEX,
|
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)
|
/// Reschedules to the next available thread (call after current thread is suspended)
|
||||||
void Reschedule();
|
void Reschedule();
|
||||||
|
|
||||||
/// Puts the current thread in the wait state for the given type
|
/// Stops the current thread
|
||||||
void WaitCurrentThread(WaitType wait_type);
|
void StopThread(Handle thread, const char* reason);
|
||||||
|
|
||||||
/// Resumes a thread from waiting by marking it as "ready"
|
/// Resumes a thread from waiting by marking it as "ready"
|
||||||
void ResumeThreadFromWait(Handle handle);
|
void ResumeThreadFromWait(Handle handle);
|
||||||
@ -62,6 +62,9 @@ void ResumeThreadFromWait(Handle handle);
|
|||||||
/// Gets the current thread handle
|
/// Gets the current thread handle
|
||||||
Handle GetCurrentThreadHandle();
|
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
|
/// Put current thread in a wait state - on WaitSynchronization
|
||||||
void WaitThread_Synchronization();
|
void WaitThread_Synchronization();
|
||||||
|
|
||||||
|
@ -93,8 +93,8 @@ Result SendSyncRequest(Handle handle) {
|
|||||||
bool wait = false;
|
bool wait = false;
|
||||||
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
|
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
|
||||||
|
|
||||||
DEBUG_LOG(SVC, "called handle=0x%08X", handle);
|
|
||||||
_assert_msg_(KERNEL, object, "called, but kernel object is NULL!");
|
_assert_msg_(KERNEL, object, "called, but kernel object is NULL!");
|
||||||
|
DEBUG_LOG(SVC, "called handle=0x%08X(%s)", handle, object->GetTypeName());
|
||||||
|
|
||||||
Result res = object->SyncRequest(&wait);
|
Result res = object->SyncRequest(&wait);
|
||||||
if (wait) {
|
if (wait) {
|
||||||
@ -115,29 +115,21 @@ Result CloseHandle(Handle handle) {
|
|||||||
Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
|
Result WaitSynchronization1(Handle handle, s64 nano_seconds) {
|
||||||
// TODO(bunnei): Do something with nano_seconds, currently ignoring this
|
// TODO(bunnei): Do something with nano_seconds, currently ignoring this
|
||||||
bool wait = false;
|
bool wait = false;
|
||||||
|
bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
|
||||||
|
|
||||||
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
|
Kernel::Object* object = Kernel::g_object_pool.GetFast<Kernel::Object>(handle);
|
||||||
|
|
||||||
DEBUG_LOG(SVC, "called handle=0x%08X, nanoseconds=%d", handle,
|
DEBUG_LOG(SVC, "called handle=0x%08X(%s:%s), nanoseconds=%d", handle, object->GetTypeName(),
|
||||||
nano_seconds);
|
object->GetName(), nano_seconds);
|
||||||
|
|
||||||
_assert_msg_(KERNEL, object, "called, but kernel object is NULL!");
|
_assert_msg_(KERNEL, object, "called, but kernel object is NULL!");
|
||||||
|
|
||||||
Result res = object->WaitSynchronization(&wait);
|
Result res = object->WaitSynchronization(&wait);
|
||||||
|
|
||||||
|
// Check for next thread to schedule
|
||||||
if (wait) {
|
if (wait) {
|
||||||
// Set current thread to wait state if handle was not unlocked
|
|
||||||
Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
|
|
||||||
|
|
||||||
// Check for next thread to schedule
|
|
||||||
HLE::Reschedule(__func__);
|
HLE::Reschedule(__func__);
|
||||||
|
return 0;
|
||||||
// Context switch - Function blocked, is not actually returning (will be "called" again)
|
|
||||||
|
|
||||||
// TODO(bunnei): This saves handle to R0 so that it's correctly reloaded on context switch
|
|
||||||
// (otherwise R0 will be set to whatever is returned, and handle will be invalid when this
|
|
||||||
// thread is resumed). There is probably a better way of keeping track of state so that we
|
|
||||||
// don't necessarily have to do this.
|
|
||||||
return (Result)PARAM(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
@ -150,6 +142,7 @@ Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wa
|
|||||||
s32* out = (s32*)_out;
|
s32* out = (s32*)_out;
|
||||||
Handle* handles = (Handle*)_handles;
|
Handle* handles = (Handle*)_handles;
|
||||||
bool unlock_all = true;
|
bool unlock_all = true;
|
||||||
|
bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated
|
||||||
|
|
||||||
DEBUG_LOG(SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%d",
|
DEBUG_LOG(SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%d",
|
||||||
handle_count, (wait_all ? "true" : "false"), nano_seconds);
|
handle_count, (wait_all ? "true" : "false"), nano_seconds);
|
||||||
@ -162,7 +155,8 @@ Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wa
|
|||||||
_assert_msg_(KERNEL, object, "called handle=0x%08X, but kernel object "
|
_assert_msg_(KERNEL, object, "called handle=0x%08X, but kernel object "
|
||||||
"is NULL!", handles[i]);
|
"is NULL!", handles[i]);
|
||||||
|
|
||||||
DEBUG_LOG(SVC, "\thandle[%d] = 0x%08X", i, handles[i]);
|
DEBUG_LOG(SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], object->GetTypeName(),
|
||||||
|
object->GetName());
|
||||||
|
|
||||||
Result res = object->WaitSynchronization(&wait);
|
Result res = object->WaitSynchronization(&wait);
|
||||||
|
|
||||||
@ -179,19 +173,10 @@ Result WaitSynchronizationN(void* _out, void* _handles, u32 handle_count, u32 wa
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set current thread to wait state if not all handles were unlocked
|
|
||||||
Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct?
|
|
||||||
|
|
||||||
// Check for next thread to schedule
|
// Check for next thread to schedule
|
||||||
HLE::Reschedule(__func__);
|
HLE::Reschedule(__func__);
|
||||||
|
|
||||||
// Context switch - Function blocked, is not actually returning (will be "called" again)
|
return 0;
|
||||||
|
|
||||||
// TODO(bunnei): This saves handle to R0 so that it's correctly reloaded on context switch
|
|
||||||
// (otherwise R0 will be set to whatever is returned, and handle will be invalid when this
|
|
||||||
// thread is resumed). There is probably a better way of keeping track of state so that we
|
|
||||||
// don't necessarily have to do this.
|
|
||||||
return (Result)PARAM(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an address arbiter (to allocate access to shared resources)
|
/// Create an address arbiter (to allocate access to shared resources)
|
||||||
@ -258,6 +243,17 @@ Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 p
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called when a thread exits
|
||||||
|
u32 ExitThread() {
|
||||||
|
Handle thread = Kernel::GetCurrentThreadHandle();
|
||||||
|
|
||||||
|
DEBUG_LOG(SVC, "called, pc=0x%08X", Core::g_app_core->GetPC()); // PC = 0x0010545C
|
||||||
|
|
||||||
|
Kernel::StopThread(thread, __func__);
|
||||||
|
HLE::Reschedule(__func__);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// Gets the priority for the specified thread
|
/// Gets the priority for the specified thread
|
||||||
Result GetThreadPriority(void* _priority, Handle handle) {
|
Result GetThreadPriority(void* _priority, Handle handle) {
|
||||||
s32* priority = (s32*)_priority;
|
s32* priority = (s32*)_priority;
|
||||||
@ -326,6 +322,13 @@ Result DuplicateHandle(void* _out, Handle handle) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Signals an event
|
||||||
|
Result SignalEvent(Handle evt) {
|
||||||
|
Result res = Kernel::SignalEvent(evt);
|
||||||
|
DEBUG_LOG(SVC, "called event=0x%08X", evt);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/// Clears an event
|
/// Clears an event
|
||||||
Result ClearEvent(Handle evt) {
|
Result ClearEvent(Handle evt) {
|
||||||
Result res = Kernel::ClearEvent(evt);
|
Result res = Kernel::ClearEvent(evt);
|
||||||
@ -348,7 +351,7 @@ const HLE::FunctionDef SVC_Table[] = {
|
|||||||
{0x06, NULL, "GetProcessIdealProcessor"},
|
{0x06, NULL, "GetProcessIdealProcessor"},
|
||||||
{0x07, NULL, "SetProcessIdealProcessor"},
|
{0x07, NULL, "SetProcessIdealProcessor"},
|
||||||
{0x08, WrapI_UUUUU<CreateThread>, "CreateThread"},
|
{0x08, WrapI_UUUUU<CreateThread>, "CreateThread"},
|
||||||
{0x09, NULL, "ExitThread"},
|
{0x09, WrapU_V<ExitThread>, "ExitThread"},
|
||||||
{0x0A, WrapV_S64<SleepThread>, "SleepThread"},
|
{0x0A, WrapV_S64<SleepThread>, "SleepThread"},
|
||||||
{0x0B, WrapI_VU<GetThreadPriority>, "GetThreadPriority"},
|
{0x0B, WrapI_VU<GetThreadPriority>, "GetThreadPriority"},
|
||||||
{0x0C, WrapI_UI<SetThreadPriority>, "SetThreadPriority"},
|
{0x0C, WrapI_UI<SetThreadPriority>, "SetThreadPriority"},
|
||||||
@ -363,7 +366,7 @@ const HLE::FunctionDef SVC_Table[] = {
|
|||||||
{0x15, NULL, "CreateSemaphore"},
|
{0x15, NULL, "CreateSemaphore"},
|
||||||
{0x16, NULL, "ReleaseSemaphore"},
|
{0x16, NULL, "ReleaseSemaphore"},
|
||||||
{0x17, WrapI_VU<CreateEvent>, "CreateEvent"},
|
{0x17, WrapI_VU<CreateEvent>, "CreateEvent"},
|
||||||
{0x18, NULL, "SignalEvent"},
|
{0x18, WrapI_U<SignalEvent>, "SignalEvent"},
|
||||||
{0x19, WrapI_U<ClearEvent>, "ClearEvent"},
|
{0x19, WrapI_U<ClearEvent>, "ClearEvent"},
|
||||||
{0x1A, NULL, "CreateTimer"},
|
{0x1A, NULL, "CreateTimer"},
|
||||||
{0x1B, NULL, "SetTimer"},
|
{0x1B, NULL, "SetTimer"},
|
||||||
|
Loading…
Reference in New Issue
Block a user