ソースを参照

Merge pull request #443 from Subv/sleep_thread

SVC: Fixed SleepThread
bunnei 11 年 前
コミット
cf1bbe8705
3 ファイル変更43 行追加8 行削除
  1. 32 8
      src/core/hle/kernel/thread.cpp
  2. 7 0
      src/core/hle/kernel/thread.h
  3. 4 0
      src/core/hle/svc.cpp

+ 32 - 8
src/core/hle/kernel/thread.cpp

@@ -308,6 +308,37 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_addres
     GetCurrentThread()->wait_address = wait_address;
 }
 
+/// Event type for the thread wake up event
+static int ThreadWakeupEventType = -1;
+
+/// Callback that will wake up the thread it was scheduled for
+static void ThreadWakeupCallback(u64 parameter, int cycles_late) {
+    Handle handle = static_cast<Handle>(parameter);
+    Thread* thread = Kernel::g_handle_table.Get<Thread>(handle);
+    if (thread == nullptr) {
+        LOG_ERROR(Kernel, "Thread doesn't exist %u", handle);
+        return;
+    }
+
+    Kernel::ResumeThreadFromWait(handle);
+}
+
+
+void WakeThreadAfterDelay(Handle handle, s64 nanoseconds) {
+    // Don't schedule a wakeup if the thread wants to wait forever
+    if (nanoseconds == -1)
+        return;
+
+    Thread* thread = Kernel::g_handle_table.Get<Thread>(handle);
+    if (thread == nullptr) {
+        LOG_ERROR(Kernel, "Thread doesn't exist %u", handle);
+        return;
+    }
+
+    u64 microseconds = nanoseconds / 1000;
+    CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, handle);
+}
+
 /// Resumes a thread from waiting by marking it as "ready"
 void ResumeThreadFromWait(Handle handle) {
     Thread* thread = Kernel::g_handle_table.Get<Thread>(handle);
@@ -499,14 +530,6 @@ void Reschedule() {
                 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());
 }
 
 bool IsIdleThread(Handle handle) {
@@ -533,6 +556,7 @@ ResultCode GetThreadId(u32* thread_id, Handle handle) {
 
 void ThreadingInit() {
     next_thread_id = INITIAL_THREAD_ID;
+    ThreadWakeupEventType = CoreTiming::RegisterEvent("ThreadWakeupCallback", ThreadWakeupCallback);
 }
 
 void ThreadingShutdown() {

+ 7 - 0
src/core/hle/kernel/thread.h

@@ -87,6 +87,13 @@ Handle GetCurrentThreadHandle();
  */
 void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle());
 
+/**
+ * Schedules an event to wake up the specified thread after the specified delay.
+ * @param handle The thread handle.
+ * @param nanoseconds The time this thread will be allowed to sleep for.
+ */
+void WakeThreadAfterDelay(Handle handle, s64 nanoseconds);
+
 /**
  * Puts the current thread in the wait state for the given type
  * @param wait_type Type of wait

+ 4 - 0
src/core/hle/svc.cpp

@@ -344,6 +344,10 @@ static void SleepThread(s64 nanoseconds) {
 
     // Sleep current thread and check for next thread to schedule
     Kernel::WaitCurrentThread(WAITTYPE_SLEEP);
+
+    // Create an event to wake the thread up after the specified nanosecond delay has passed
+    Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThreadHandle(), nanoseconds);
+
     HLE::Reschedule(__func__);
 }