Cancellation does NOT work well with CompletableFutures
CompletableFuture.cancel
CompletableFuture.cancel(boolean mayInterruptIfRunning). mayInterruptIfRunning: does Nothing.
[Cancellation does NOT work well with CompletableFutures](thorg://notes/u7ox6ra9bc6qdn3zoh7plfm) ```java /** * If not already completed, completes this CompletableFuture with * a {@link CancellationException}. Dependent CompletableFutures * that have not already completed will also complete * exceptionally, with a {@link CompletionException} caused by * this {@code CancellationException}. * * @param mayInterruptIfRunning this value has no effect in this * implementation because interrupts are not used to control * processing. * * @return {@code true} if this task is now cancelled */ public boolean cancel(boolean mayInterruptIfRunning) { ```After 'cancel' is called. Thread.sleep() in cancelled thread does NOT throw Interrupted exception.
Code
package com.glassthought.sandbox
import gt.sandbox.util.output.Out
import gt.sandbox.util.output.impl.OutSettings
import java.util.concurrent.CompletableFuture
val out = Out.standard(outSettings = OutSettings(printColorPerThread = true))
fun main() {
// Create a CompletableFuture that simulates a long-running task
val future = CompletableFuture.supplyAsync {
out.println("supplyAsync: Task started")
try {
out.println("Starting 5 second sleep")
Thread.sleep(5000) // Simulate a long-running task
out.println("Slept for 5 seconds, will sleep for just 100ms more")
Thread.sleep(100)
out.println("supplyAsync: Task completed")
"Task completed"
} catch (e: InterruptedException) {
out.println("Task was interrupted")
throw RuntimeException("Task interrupted", e)
}
}
Thread.sleep(100)
// Simulate cancellation after 2 seconds
Thread {
out.println("Starting a new thread to cancel the future...")
Thread.sleep(2000) // Wait 2 seconds before canceling
out.println("Cancelling the future...")
val cancelled = future.cancel(true)
out.println("Future cancelled: $cancelled")
}.start()
// Attempt to retrieve the result (blocks until completed or cancelled)
try {
val result = future.get()
out.println("Future result: $result")
} catch (e: Exception) {
out.println("Exception occurred: ${e.message}")
}
Thread.sleep(6000)
out.println("Main thread ends")
}
Command to reproduce:
gt.sandbox.checkout.commit e04a4d99c46dad07bed4 \
&& cd "${GT_SANDBOX_REPO}" \
&& cmd.run.announce "./gradlew run --quiet"
Recorded output of command:
[2024-11-22T16:45:52.824796Z][elapsed-since-start: 15ms][tname:ForkJoinPool.commonPool-worker-1/tid:20] supplyAsync: Task started
[2024-11-22T16:45:52.852971Z][elapsed-since-start: 33ms][tname:ForkJoinPool.commonPool-worker-1/tid:20] Starting 5 second sleep
[2024-11-22T16:45:52.928483Z][elapsed-since-start: 108ms][tname:Thread-0/tid:21] Starting a new thread to cancel the future...
[2024-11-22T16:45:54.931070Z][elapsed-since-start: 2111ms][tname:Thread-0/tid:21] Cancelling the future...
[2024-11-22T16:45:54.935170Z][elapsed-since-start: 2115ms][tname:main/tid:1] Exception occurred: null
[2024-11-22T16:45:54.938928Z][elapsed-since-start: 2118ms][tname:Thread-0/tid:21] Future cancelled: true
[2024-11-22T16:45:57.857226Z][elapsed-since-start: 5037ms][tname:ForkJoinPool.commonPool-worker-1/tid:20] Slept for 5 seconds, will sleep for just 100ms more
[2024-11-22T16:45:57.958440Z][elapsed-since-start: 5138ms][tname:ForkJoinPool.commonPool-worker-1/tid:20] supplyAsync: Task completed
[2024-11-22T16:46:00.940212Z][elapsed-since-start: 8120ms][tname:main/tid:1] Main thread ends
Backlinks