use of dev.failsafe.Timeout in project failsafe by jhalterman.
the class TimeoutExecutor method applyAsync.
/**
* Schedules a separate timeout call that blocks and fails with {@link TimeoutExceededException} if the policy's
* timeout is exceeded.
* <p>
* This implementation sets up a race between a timeout being triggered and the execution completing. Whichever
* completes first will be the result that's recorded and used to complete the resulting promise.
*/
@Override
@SuppressWarnings("unchecked")
public Function<AsyncExecutionInternal<R>, CompletableFuture<ExecutionResult<R>>> applyAsync(Function<AsyncExecutionInternal<R>, CompletableFuture<ExecutionResult<R>>> innerFn, Scheduler scheduler, FailsafeFuture<R> future) {
return execution -> {
// Coordinates a race between the timeout and execution threads
AtomicReference<ExecutionResult<R>> resultRef = new AtomicReference<>();
AtomicReference<Future<R>> timeoutFutureRef = new AtomicReference<>();
CompletableFuture<ExecutionResult<R>> promise = new CompletableFuture<>();
// Guard against race with AsyncExecution.record, AsyncExecution.complete, future.complete or future.cancel
synchronized (future) {
// Schedule timeout if we are not done and not recording a result
if (!future.isDone() && !execution.isRecorded()) {
try {
Future<R> timeoutFuture = (Future<R>) Scheduler.DEFAULT.schedule(() -> {
// Guard against race with execution completion
ExecutionResult<R> cancelResult = ExecutionResult.exception(new TimeoutExceededException(policy));
if (resultRef.compareAndSet(null, cancelResult)) {
// Cancel and interrupt
execution.record(cancelResult);
execution.cancel(this);
future.cancelDependencies(this, config.canInterrupt(), cancelResult);
}
return null;
}, config.getTimeout().toNanos(), TimeUnit.NANOSECONDS);
timeoutFutureRef.set(timeoutFuture);
// Propagate outer cancellations to the Timeout future and its promise
future.setCancelFn(this, (mayInterrupt, cancelResult) -> {
timeoutFuture.cancel(mayInterrupt);
resultRef.compareAndSet(null, cancelResult);
});
} catch (Throwable t) {
// Hard scheduling failure
promise.completeExceptionally(t);
return promise;
}
}
}
// Propagate execution, cancel timeout future if not done, and postExecute result
innerFn.apply(execution).whenComplete((result, error) -> {
if (error != null) {
promise.completeExceptionally(error);
return;
}
// Fetch timeout result if any
if (!resultRef.compareAndSet(null, result))
result = resultRef.get();
if (result != null) {
// Cancel timeout task
Future<R> timeoutFuture = timeoutFutureRef.get();
if (timeoutFuture != null && !timeoutFuture.isDone())
timeoutFuture.cancel(false);
postExecuteAsync(execution, result, scheduler, future);
}
promise.complete(result);
});
return promise;
};
}
use of dev.failsafe.Timeout in project failsafe by jhalterman.
the class Issue260Test method test.
public void test() throws Throwable {
ExecutorService executor = Executors.newSingleThreadExecutor();
Timeout<Object> timeout = Timeout.builder(Duration.ofMillis(300)).withInterrupt().onFailure(e -> System.out.println("Interrupted")).build();
RetryPolicy<Object> rp = RetryPolicy.builder().onRetry(e -> System.out.println("Retrying")).onSuccess(e -> System.out.println("Success")).build();
Function<Integer, ContextualRunnable> task = (taskId) -> ctx -> {
System.out.println("Starting execution of task " + taskId);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
System.out.println("Interrupted task " + taskId);
throw e;
}
};
Future<?> f1 = Failsafe.with(rp, timeout).with(executor).runAsync(task.apply(1));
Future<?> f2 = Failsafe.with(rp, timeout).with(executor).runAsync(task.apply(2));
Future<?> f3 = Failsafe.with(rp, timeout).with(executor).runAsync(task.apply(3));
f1.get(1, TimeUnit.SECONDS);
f2.get(1, TimeUnit.SECONDS);
f3.get(1, TimeUnit.SECONDS);
}
use of dev.failsafe.Timeout in project failsafe by jhalterman.
the class TimeoutExecutor method apply.
/**
* Schedules a separate timeout call that fails with {@link TimeoutExceededException} if the policy's timeout is
* exceeded.
* <p>
* This implementation sets up a race between a timeout being triggered and the execution completing. Whichever
* completes first will be the result that's recorded.
*/
@Override
public Function<SyncExecutionInternal<R>, ExecutionResult<R>> apply(Function<SyncExecutionInternal<R>, ExecutionResult<R>> innerFn, Scheduler scheduler) {
return execution -> {
// Coordinates a result between the timeout and execution threads
AtomicReference<ExecutionResult<R>> result = new AtomicReference<>();
Future<?> timeoutFuture;
try {
// Schedule timeout check
timeoutFuture = Scheduler.DEFAULT.schedule(() -> {
// Guard against race with execution completion
ExecutionResult<R> cancelResult = ExecutionResult.exception(new TimeoutExceededException(policy));
if (result.compareAndSet(null, cancelResult)) {
// Cancel and interrupt
execution.record(cancelResult);
execution.cancel(this);
if (config.canInterrupt())
execution.interrupt();
}
return null;
}, config.getTimeout().toNanos(), TimeUnit.NANOSECONDS);
} catch (Throwable t) {
// Hard scheduling failure
return postExecute(execution, ExecutionResult.exception(t));
}
// Propagate execution, cancel timeout future if not done, and postExecute result
if (result.compareAndSet(null, innerFn.apply(execution)))
timeoutFuture.cancel(false);
return postExecute(execution, result.get());
};
}
Aggregations