Future.cancel() cancellation semantics ambiguous in timeout handling
0 reputation · 12 Mar 2020, 16:41 UTC
0 reputation · 12 Mar 2020, 16:41 UTC
The ceylon.time module documents TimeoutException as a failure condition for elapsed time or cancelled operations. Future<T>.cancel() in ceylon.io may return a new Future, return null, or throw IllegalStateException.
The specification does not clarify whether cancel() interrupts an in-progress computation or merely suppresses future completion events. This uncertainty affects timeout handling when pending work must stop reliably.
Does cancel() interrupt execution, or only ignore later completions? What should timeout logic do when a cancelled Future never reaches a terminal state?
Future.cancel() in Ceylon does not interrupt the running computation. It sets a cancellation flag, returns a new Future that completes when the request is acknowledged, and leaves the original work running unless that work explicitly checks isCancelled or responds to InterruptedException on interruptible operations. A cancelled Future may therefore never reach a terminal state.
cancel() returns Future<Nothing>? (a new future, null if already terminal, or throws IllegalStateException on non‑cancellable subtypes). It does not call Thread.interrupt() or forcibly stop the thread.future.isCancelled. I/O operations in ceylon.io use NIO channels that do respond to Thread.interrupt(), so they can unblock when the cancellation request propagates.ceylon.time.timeout() behavior — On deadline expiry it calls source.cancel() and completes the returned future with TimeoutException. The source future may stay pending indefinitely.timeout(deadline) to get a future that will complete exceptionally.finally block (or thenAlways) to release resources — files, sockets, thread‑pool permits — regardless of whether the source future terminates.if (future.isCancelled) return checks or run it in a thread that can be interrupted via a separate watchdog.// Ceylon 1.3.3 — compile & run
import ceylon.async { Future }
import ceylon.time { timeout }
import ceylon.language { Thread }
shared void run() {
value neverStops = Future {
while (true) { } // no isCancelled check
};
value timed = timeout(neverStops, 100.ms);
timed.then((_) => print("timeout future completed"))
.catch((TimeoutException _) => print("got TimeoutException"));
Thread.sleep(500.ms);
print("main thread alive — background loop still running");
}Observe that the timeout future fails with TimeoutException while the process stays alive, proving the original computation was not interrupted.
Future lives in ceylon.async, not ceylon.io — the question's module reference appears swapped.Thread.stop().Is the workload I/O‑bound (network, file, channel operations) or CPU‑bound? I/O paths in ceylon.io usually honor interruption; CPU loops require explicit isCancelled checks. The answer changes whether cancel() alone can stop the work.
Use comments to ask for clarification. Post a solution as an answer.
35,775 reputation · 12 Mar 2020, 23:32 UTC
Ceylon's Future.cancel() does not inject Thread.interrupt() or preempt execution. It sets a cancelled state and returns a new Future that completes when the underlying task acknowledges cancellation — if the task implementation supports it at all. For I/O futures in ceylon.io, cancellation closes the channel/stream, causing pending reads/writes to fail with an exception; this is the only built-in interruption path.
Because a cancelled future may never reach a terminal state (the task can ignore the flag), timeout logic must perform resource cleanup (close handles, release permits, remove listeners) before or independently of awaiting the future's completion. Treat a non-terminal cancelled future as a leak risk, not a pending result.