// Author: Sandeep Belgavi. Controlled waits distinguish future state from worker lifetime.
import java.util.concurrent.*;

public class TimeoutDemo {
    public static void main(String[] args) throws Exception {
        var executor = Executors.newFixedThreadPool(2);
        var release = new CountDownLatch(1);
        var started = new CountDownLatch(1);
        var done = new CountDownLatch(1);
        try {
            var stage = CompletableFuture.supplyAsync(() -> {
                started.countDown();
                try { release.await(); return "finished"; }
                catch (InterruptedException e) { Thread.currentThread().interrupt(); return "interrupted"; }
                finally { done.countDown(); }
            }, executor);
            if (!started.await(5, TimeUnit.SECONDS)) throw new AssertionError("Not started");
            try { stage.orTimeout(1, TimeUnit.MILLISECONDS).join(); }
            catch (CompletionException e) {
                if (!(e.getCause() instanceof TimeoutException)) throw e;
            }
            if (!stage.isCompletedExceptionally() || done.getCount() != 1)
                throw new AssertionError("Timeout did not leave worker running");
            System.out.println("stage_timed_out=true worker_still_waiting=true");
            release.countDown();
            if (!done.await(5, TimeUnit.SECONDS)) throw new AssertionError("Cleanup stalled");

            var workerStarted = new CountDownLatch(1);
            var workerStopped = new CountDownLatch(1);
            Future<?> handle = executor.submit(() -> {
                workerStarted.countDown();
                try { new CountDownLatch(1).await(); }
                catch (InterruptedException e) { Thread.currentThread().interrupt(); }
                finally { workerStopped.countDown(); }
            });
            if (!workerStarted.await(5, TimeUnit.SECONDS)) throw new AssertionError("Not started");
            if (!handle.cancel(true)) throw new AssertionError("Cancellation not accepted");
            if (!workerStopped.await(5, TimeUnit.SECONDS)) throw new AssertionError("Worker ignored interrupt");
            System.out.println("owned_worker_cancelled=true cooperative_cleanup_observed=true");
        } finally {
            release.countDown(); executor.shutdownNow();
            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) throw new AssertionError("Executor still alive");
        }
    }
}
