citra-emu
/
citra
Archived
1
0
Fork 0

Thread: Wait current thread on svc_SleepThread

- Removed unused VBLANK sleep mode
- Added error log for bad context switch
- Renamed VerifyWait to CheckWaitType to be more clear
This commit is contained in:
bunnei 2014-12-20 02:32:19 -05:00
parent 2e5869c939
commit 4fcdbed9f6
3 changed files with 35 additions and 22 deletions

View File

@ -147,16 +147,19 @@ void ChangeReadyState(Thread* t, bool ready) {
}
}
/// Verify that a thread has not been released from waiting
static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) {
_dbg_assert_(Kernel, thread != nullptr);
return (type == thread->wait_type) && (wait_handle == thread->wait_handle) && (thread->IsWaiting());
/// Check if a thread is blocking on a specified wait type
static bool CheckWaitType(const Thread* thread, WaitType type) {
return (type == thread->wait_type) && (thread->IsWaiting());
}
/// Verify that a thread has not been released from waiting (with wait address)
static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) {
_dbg_assert_(Kernel, thread != nullptr);
return VerifyWait(thread, type, wait_handle) && (wait_address == thread->wait_address);
/// Check if a thread is blocking on a specified wait type with a specified handle
static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle) {
return CheckWaitType(thread, type) && (wait_handle == thread->wait_handle);
}
/// Check if a thread is blocking on a specified wait type with a specified handle and address
static bool CheckWaitType(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) {
return CheckWaitType(thread, type, wait_handle) && (wait_address == thread->wait_address);
}
/// Stops the current thread
@ -171,9 +174,9 @@ ResultCode StopThread(Handle handle, const char* reason) {
thread->status = THREADSTATUS_DORMANT;
for (Handle waiting_handle : thread->waiting_threads) {
Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle);
if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) {
if (CheckWaitType(waiting_thread, WAITTYPE_THREADEND, handle))
ResumeThreadFromWait(waiting_handle);
}
}
thread->waiting_threads.clear();
@ -209,7 +212,7 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) {
for (Handle handle : thread_queue) {
Thread* thread = g_object_pool.Get<Thread>(handle);
if (!VerifyWait(thread, WAITTYPE_ARB, arbiter, address))
if (!CheckWaitType(thread, WAITTYPE_ARB, arbiter, address))
continue;
if (thread == nullptr)
@ -234,7 +237,7 @@ void ArbitrateAllThreads(u32 arbiter, u32 address) {
for (Handle handle : thread_queue) {
Thread* thread = g_object_pool.Get<Thread>(handle);
if (VerifyWait(thread, WAITTYPE_ARB, arbiter, address))
if (CheckWaitType(thread, WAITTYPE_ARB, arbiter, address))
ResumeThreadFromWait(handle);
}
}
@ -305,6 +308,8 @@ void ResumeThreadFromWait(Handle handle) {
Thread* thread = Kernel::g_object_pool.Get<Thread>(handle);
if (thread) {
thread->status &= ~THREADSTATUS_WAIT;
thread->wait_handle = 0;
thread->wait_type = WAITTYPE_NONE;
if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) {
ChangeReadyState(thread, true);
}
@ -468,19 +473,27 @@ void Reschedule() {
Thread* prev = GetCurrentThread();
Thread* next = NextThread();
HLE::g_reschedule = false;
if (next > 0) {
if (next != nullptr) {
LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle());
SwitchContext(next);
} else {
LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle());
// 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());
for (Handle handle : thread_queue) {
Thread* thread = g_object_pool.Get<Thread>(handle);
LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X",
thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, thread->wait_handle);
}
}
// TODO(bunnei): Hack - There is no timing mechanism yet to wake up a thread if it has been put
// to sleep. So, we'll just immediately set it to "ready" again after an attempted context
// switch has occurred. This results in the current thread yielding on a sleep once, and then it
// will immediately be placed back in the queue for execution.
if (CheckWaitType(prev, WAITTYPE_SLEEP))
ResumeThreadFromWait(prev->GetHandle());
}
ResultCode GetThreadId(u32* thread_id, Handle handle) {

View File

@ -40,7 +40,6 @@ enum WaitType {
WAITTYPE_SEMA,
WAITTYPE_EVENT,
WAITTYPE_THREADEND,
WAITTYPE_VBLANK,
WAITTYPE_MUTEX,
WAITTYPE_SYNCH,
WAITTYPE_ARB,

View File

@ -352,7 +352,8 @@ static Result ClearEvent(Handle evt) {
static void SleepThread(s64 nanoseconds) {
LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds);
// Check for next thread to schedule
// Sleep current thread and check for next thread to schedule
Kernel::WaitCurrentThread(WAITTYPE_SLEEP);
HLE::Reschedule(__func__);
}