diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 54481f7f1..5608418c3 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -57,6 +57,33 @@ void ServerSession::Acquire(Thread* thread) {
     pending_requesting_threads.pop_back();
 }
 
+ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
+    auto& domain_message_header = context.GetDomainMessageHeader();
+    if (domain_message_header) {
+        // If there is a DomainMessageHeader, then this is CommandType "Request"
+        const u32 object_id{context.GetDomainMessageHeader()->object_id};
+        switch (domain_message_header->command) {
+        case IPC::DomainMessageHeader::CommandType::SendMessage:
+            return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
+
+        case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
+            LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id);
+
+            domain_request_handlers[object_id - 1] = nullptr;
+
+            IPC::ResponseBuilder rb{context, 2};
+            rb.Push(RESULT_SUCCESS);
+            return RESULT_SUCCESS;
+        }
+        }
+
+        LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value());
+        ASSERT(false);
+    }
+
+    return RESULT_SUCCESS;
+}
+
 ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
     // The ServerSession received a sync request, this means that there's new data available
     // from its ClientSession, so wake up any threads that may be waiting on a svcReplyAndReceive or
@@ -67,46 +94,39 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
     context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process,
                                               Kernel::g_handle_table);
 
-    // If the session has been converted to a domain, handle the doomain request
+    ResultCode result = RESULT_SUCCESS;
+    // If the session has been converted to a domain, handle the domain request
     if (IsDomain()) {
-        auto& domain_message_header = context.GetDomainMessageHeader();
-        if (domain_message_header) {
-            // If there is a DomainMessageHeader, then this is CommandType "Request"
-            const u32 object_id{context.GetDomainMessageHeader()->object_id};
-            switch (domain_message_header->command) {
-            case IPC::DomainMessageHeader::CommandType::SendMessage:
-                return domain_request_handlers[object_id - 1]->HandleSyncRequest(context);
-
-            case IPC::DomainMessageHeader::CommandType::CloseVirtualHandle: {
-                LOG_DEBUG(IPC, "CloseVirtualHandle, object_id=0x%08X", object_id);
-
-                domain_request_handlers[object_id - 1] = nullptr;
-
-                IPC::ResponseBuilder rb{context, 2};
-                rb.Push(RESULT_SUCCESS);
-                return RESULT_SUCCESS;
-            }
-            }
-
-            LOG_CRITICAL(IPC, "Unknown domain command=%d", domain_message_header->command.Value());
-            ASSERT(false);
-        }
+        result = HandleDomainSyncRequest(context);
         // If there is no domain header, the regular session handler is used
+    } else if (hle_handler != nullptr) {
+        // If this ServerSession has an associated HLE handler, forward the request to it.
+        result = hle_handler->HandleSyncRequest(context);
     }
 
-    // If this ServerSession has an associated HLE handler, forward the request to it.
-    ResultCode result{RESULT_SUCCESS};
-    if (hle_handler != nullptr) {
-        // Attempt to translate the incoming request's command buffer.
-        ResultCode translate_result = TranslateHLERequest(this);
-        if (translate_result.IsError())
-            return translate_result;
+    if (thread->status == THREADSTATUS_RUNNING) {
+        // Put the thread to sleep until the server replies, it will be awoken in
+        // svcReplyAndReceive for LLE servers.
+        thread->status = THREADSTATUS_WAIT_IPC;
 
-        result = hle_handler->HandleSyncRequest(context);
-    } else {
-        // Add the thread to the list of threads that have issued a sync request with this
-        // server.
-        pending_requesting_threads.push_back(std::move(thread));
+        if (hle_handler != nullptr) {
+            // For HLE services, we put the request threads to sleep for a short duration to
+            // simulate IPC overhead, but only if the HLE handler didn't put the thread to sleep for
+            // other reasons like an async callback. The IPC overhead is needed to prevent
+            // starvation when a thread only does sync requests to HLE services while a
+            // lower-priority thread is waiting to run.
+
+            // This delay was approximated in a homebrew application by measuring the average time
+            // it takes for svcSendSyncRequest to return when performing the SetLcdForceBlack IPC
+            // request to the GSP:GPU service in a n3DS with firmware 11.6. The measured values have
+            // a high variance and vary between models.
+            static constexpr u64 IPCDelayNanoseconds = 39000;
+            thread->WakeAfterDelay(IPCDelayNanoseconds);
+        } else {
+            // Add the thread to the list of threads that have issued a sync request with this
+            // server.
+            pending_requesting_threads.push_back(std::move(thread));
+        }
     }
 
     // If this ServerSession does not have an HLE implementation, just wake up the threads waiting
@@ -140,9 +160,4 @@ ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& n
 
     return std::make_tuple(std::move(server_session), std::move(client_session));
 }
-
-ResultCode TranslateHLERequest(ServerSession* server_session) {
-    // TODO(Subv): Implement this function once multiple concurrent processes are supported.
-    return RESULT_SUCCESS;
-}
 } // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index 144692106..2da807042 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -21,6 +21,7 @@ class ServerSession;
 class Session;
 class SessionRequestHandler;
 class Thread;
+class HLERequestContext;
 
 /**
  * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS
@@ -116,17 +117,12 @@ private:
      */
     static ResultVal<SharedPtr<ServerSession>> Create(std::string name = "Unknown");
 
+    /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
+    /// object handle.
+    ResultCode HandleDomainSyncRequest(Kernel::HLERequestContext& context);
+
     /// When set to True, converts the session to a domain at the end of the command
     bool convert_to_domain{};
 };
 
-/**
- * Performs command buffer translation for an HLE IPC request.
- * The command buffer from the ServerSession thread's TLS is copied into a
- * buffer and all descriptors in the buffer are processed.
- * TODO(Subv): Implement this function, currently we do not support multiple processes running at
- * once, but once that is implemented we'll need to properly translate all descriptors
- * in the command buffer.
- */
-ResultCode TranslateHLERequest(ServerSession* server_session);
 } // namespace Kernel
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 1a33cc6cb..130b669a0 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -284,6 +284,7 @@ void Thread::ResumeFromWait() {
     case THREADSTATUS_WAIT_SYNCH_ANY:
     case THREADSTATUS_WAIT_ARB:
     case THREADSTATUS_WAIT_SLEEP:
+    case THREADSTATUS_WAIT_IPC:
         break;
 
     case THREADSTATUS_READY:
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 0a1ada27d..bbffaf4cf 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -40,6 +40,7 @@ enum ThreadStatus {
     THREADSTATUS_READY,          ///< Ready to run
     THREADSTATUS_WAIT_ARB,       ///< Waiting on an address arbiter
     THREADSTATUS_WAIT_SLEEP,     ///< Waiting due to a SleepThread SVC
+    THREADSTATUS_WAIT_IPC,       ///< Waiting for the reply from an IPC request
     THREADSTATUS_WAIT_SYNCH_ANY, ///< Waiting due to WaitSynch1 or WaitSynchN with wait_all = false
     THREADSTATUS_WAIT_SYNCH_ALL, ///< Waiting due to WaitSynchronizationN with wait_all = true
     THREADSTATUS_DORMANT,        ///< Created but not yet made ready