/** * Returns a new {@link AsyncIterator} that wraps this instance which serializes the execution of all calls to * {@link #getNext()} (no two executions of {@link #getNext()} will ever execute at the same time; they will be * run in the order of invocation, but only after the previous one completes). * * @param executor An Executor to run async tasks on. * @return A new {@link AsyncIterator}. */ default AsyncIterator<T> asSequential(Executor executor) { SequentialProcessor processor = new SequentialProcessor(executor); return () -> { CompletableFuture<T> result = processor.add(AsyncIterator.this::getNext); result.thenAccept(r -> { if (r == null) { processor.close(); } }); return result; }; } }
final int count = 10000; @Cleanup val proc = new SequentialProcessor(executorService()); val running = new AtomicBoolean(false); val previousRun = new AtomicReference<CompletableFuture<Integer>>(); val thisRun = new CompletableFuture<Integer>(); val pr = previousRun.getAndSet(thisRun); results.add(proc.add(() -> { if (!running.compareAndSet(false, true)) { Assert.fail("Concurrent execution detected.");
/** * Tests the ability to cancel ongoing tasks when the processor is closed. */ @Test public void testClose() { @Cleanup val proc = new SequentialProcessor(executorService()); val toRun = new CompletableFuture<Integer>(); val result = proc.add(() -> toRun); proc.close(); AssertExtensions.assertThrows( "Task not cancelled.", result::join, ex -> ex instanceof ObjectClosedException); Assert.assertFalse("Not expecting inner blocker task to be done.", toRun.isDone()); } }