Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cli/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ kt_jvm_test(
runtime_deps = [":cli-test-lib"],
)

kt_jvm_test(
name = "ProcessStdinHangRegressionTest",
test_class = "com.bazel_diff.process.ProcessStdinHangRegressionTest",
runtime_deps = [":cli-test-lib"],
)

kt_jvm_test(
name = "E2ETest",
timeout = "long",
Expand Down
6 changes: 6 additions & 0 deletions cli/src/main/kotlin/com/bazel_diff/process/Process.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ suspend fun process(
}
.start()

// Close the subprocess's stdin so reads from it see EOF immediately.
// Without this, subprocesses that read stdin on startup (e.g. aspect CLI's
// interactive first-run path, #256) block forever waiting for input we
// never send, and waitFor() then blocks forever too.
process.outputStream.close()

// Handles async consumptions before the blocking output handling.
if (stdout is Redirect.Consume) {
process.inputStream.lineFlow(stdout.consumer)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.bazel_diff.process

import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeoutOrNull
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull

/**
* Regression test for https://github.com/Tinder/bazel-diff/issues/256
* ("bug: bazel-diff freezes when aspect CLI is installed").
*
* Before the fix, [process] started subprocesses via `ProcessBuilder.start()`
* without redirecting or closing stdin. Java defaults stdin to `Redirect.PIPE`,
* so the subprocess received an open, never-closed stdin pipe. Any subprocess
* that reads from stdin (the aspect CLI's interactive first-run path, in #256)
* blocked indefinitely on `read()`, and `process.waitFor()` then blocked too —
* matching the `FUTEX_WAIT` strace the original reporter captured.
*
* The fix closes `process.outputStream` immediately after `start()` so the
* subprocess sees EOF on stdin and exits.
*/
@OptIn(ExperimentalCoroutinesApi::class)
class ProcessStdinHangRegressionTest {

@Test
fun `process does not hang when subprocess reads from stdin`() = runBlocking {
// `cat` with no args reads from stdin until EOF. Before the fix this hung
// forever; after the fix `cat` sees EOF immediately and exits 0.
val result =
withTimeoutOrNull(timeMillis = 5_000) {
process(
"cat",
stdout = Redirect.CAPTURE,
stderr = Redirect.SILENT,
)
}

assertNotNull(result, "process() deadlocked — subprocess stdin was not closed (regression of #256).")
assertEquals(0, result.resultCode)
}
}
Loading