diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 58fb62e898..954bd09a09 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/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() {
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index dfe92d162a..e6961e279b 100644
--- a/src/core/hle/kernel/thread.h
+++ b/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
diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp
index c25409a9fe..4003e0ba9c 100644
--- a/src/core/hle/svc.cpp
+++ b/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__);
 }