Skip to content

runBlocking(Dispatchers.IO) inside NanoHTTPD request handlers risks thread starvation #23

Description

@callumee

runBlocking(Dispatchers.IO) inside NanoHTTPD request handlers risks thread starvation

Severity: High
File: feature/file-transfer/src/main/java/com/wanbaohe/file_transfer/server/FileTransferServer.kt:144,327

Two methods use runBlocking to bridge coroutines to the synchronous NanoHTTPD handler:

fun getChatHistoryByChannel(channelId: String): List<ChatMessage> {
    return runBlocking(Dispatchers.IO) {
        chatDao.getMessagesByChannel(channelId).map { it.toChatMessage() }
    }
}

fun listChatSessions(): List<ChatSession> {
    return runBlocking(Dispatchers.IO) {
        chatDao.listSessionSummaries().map { ... }
    }
}

NanoHTTPD already dispatches each request on its own thread pool. Wrapping Room queries in runBlocking(Dispatchers.IO) creates a nested blocking call: the NanoHTTPD thread blocks waiting for a coroutine that itself blocks an IO-dispatcher thread. Under concurrent load (multiple browser tabs requesting history simultaneously), this can exhaust the Dispatchers.IO thread pool (default: 64 threads) and deadlock.

Why it matters

If several browser clients load the chat history page simultaneously, or if a Room migration is in progress, the blocking chain can starve the IO dispatcher and cause the entire server to hang. These methods should either use runBlocking without specifying Dispatchers.IO (since the caller is already off the main thread), or the server should be migrated to Ktor/cio which handles async natively.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions