net.tascalate.concurrent.CompletableTask Maven / Gradle / Ivy
Show all versions of net.tascalate.concurrent Show documentation
/**
* Copyright 2015-2019 Valery Silaev (http://vsilaev.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.tascalate.concurrent;
import static net.tascalate.concurrent.SharedFunctions.selectSecond;
import java.time.Duration;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
import java.util.concurrent.RunnableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import net.tascalate.concurrent.decorators.ExecutorBoundPromise;
/**
*
* Concrete implementation of {@link Promise} interface for long-running blocking tasks
*
* @author vsilaev
*
* @param
* a type of the successfully executed task result
*/
public class CompletableTask extends AbstractCompletableTask implements RunnableFuture {
/**
* Creates a CompletableTask; for internal use only
* @param executor
* a default {@link Executor} to run functions passed to async composition methods
* @param callable
* a {@link Callable} that completes this task
*/
protected CompletableTask(Executor executor, Callable callable) {
super(executor, callable);
}
/**
* Executes wrapped {@link Callable}; don't use explicitly
*/
@Override
public void run() {
task.run();
}
/**
* Returns a resolved {@link Promise} with specified value; the promise is "bound" to the specified executor.
* I.e. any function passed to composition methods of Promise (like thenApplyAsync
* / thenAcceptAsync
/ whenCompleteAsync
etc.) will be executed using this executor
* unless executor is overridden via explicit composition method parameter. Moreover, any nested
* composition calls will use same executor, if it’s not redefined via explicit composition method parameter:
* {@code}CompletableTask
* .complete("Hello!", myExecutor)
* .thenApplyAsync(myMapper)
* .thenApplyAsync(myTransformer)
* .thenAcceptAsync(myConsumer)
* .thenRunAsync(myAction)
*
* All of myMapper
, myTransformer
, myConsumer
, myActtion
will be executed using myExecutor
*
* @param
* a type of the successfully executed task result
* @param value
* a task result
* @param defaultExecutor
* a default {@link Executor} to run functions passed to async composition methods
* (like thenApplyAsync
/ thenAcceptAsync
/ whenCompleteAsync
etc.)
* @return
* resolved {@link Promise} with a value passed; the promise is bound to the specified executor
*/
public static Promise complete(T value, Executor defaultExecutor) {
CompletableTask result = new CompletableTask(defaultExecutor, () -> value);
SAME_THREAD_EXECUTOR.execute(result);
return result;
}
/**
* Returns a resolved no-value {@link Promise} that is "bound" to the specified executor.
* I.e. any function passed to composition methods of Promise (like thenApplyAsync
* / thenAcceptAsync
/ whenCompleteAsync
etc.) will be executed using this executor
* unless executor is overridden via explicit composition method parameter. Moreover, any nested
* composition calls will use same executor, if it’s not redefined via explicit composition method parameter:
* {@code}CompletableTask
* .asyncOn(myExecutor)
* .thenApplyAsync(myValueGenerator)
* .thenAcceptAsync(myConsumer)
* .thenRunAsync(myAction)
*
* All of myValueGenerator
, myConsumer
, myActtion
will be executed using myExecutor
*
* @param executor
* a default {@link Executor} to run functions passed to async composition methods
* (like thenApplyAsync
/ thenAcceptAsync
/ whenCompleteAsync
etc.)
* @return
* resolved non-value {@link Promise} bound to the specified executor
*/
public static Promise asyncOn(Executor executor) {
return complete(null, executor);
}
/**
* Returns a resolved no-value {@link Promise} that is "bound" to the specified executor.
* I.e. any function passed to composition methods of Promise (like thenApplyAsync
* / thenAcceptAsync
/ whenCompleteAsync
etc.) will be executed using this executor
* unless executor is overridden via explicit composition method parameter. Moreover, any nested
* composition calls will use same executor, if it’s not redefined via explicit composition method parameter:
* {@code}CompletableTask
* .asyncOn(myExecutor)
* .thenApplyAsync(myValueGenerator)
* .thenAcceptAsync(myConsumer)
* .thenRunAsync(myAction)
*
* All of myValueGenerator
, myConsumer
, myActtion
will be executed using myExecutor
.
*
Moreover, if enforceDefaultAsync
is true, then default executor will be propagated to dependent promises
* even if corresponding transition was executed on another executor (via composition methods with explicit executor argument).
*
* @param executor
* a default {@link Executor} to run functions passed to async composition methods
* (like thenApplyAsync
/ thenAcceptAsync
/ whenCompleteAsync
etc.)
* @param enforceDefaultAsync
* if true then default executor will be propagated to dependent promises
* even if corresponding transition was executed on another executor
* @return
* resolved non-value {@link Promise} bound to the specified executor
*/
public static Promise asyncOn(Executor executor, boolean enforceDefaultAsync) {
Promise result = complete(null, executor);
if (enforceDefaultAsync) {
class DefaultAsyncDecorator extends ExecutorBoundPromise {
public DefaultAsyncDecorator(Promise delegate) {
super(delegate, executor);
}
@Override
protected Promise wrap(CompletionStage original) {
return new DefaultAsyncDecorator<>((Promise)original);
}
@Override
public Promise raw() {
// Return self to avoid unrolling further
return this;
}
}
return new DefaultAsyncDecorator<>(result);
} else {
return result;
}
}
/**
* Returns a new {@link Promise} that is asynchronously resolved by a task running in the given executor
* after it runs the given action.
* @param runnable
* the action to run before resolving the returned {@link Promise}
* @param executor
* the executor to use for asynchronous execution
* @return
* the new {@link Promise}
*/
public static Promise runAsync(Runnable runnable, Executor executor) {
return submit(() -> { runnable.run(); return null; }, executor);
}
/**
* Returns a new {@link Promise} that is asynchronously resolved by a task running in the given executor
* with the value obtained by calling the given {@link Supplier}.
* @param
* the function's return type
* @param supplier
* a function returning the value to be used to resolve the returned {@link Promise}
* @param executor
* the executor to use for asynchronous execution
* @return
* the new {@link Promise}
*/
public static Promise supplyAsync(Supplier supplier, Executor executor) {
return submit(supplier::get, executor);
}
/**
* Returns a new {@link Promise} that is asynchronously resolved by a task running in the given executor
* with the value obtained by calling the given {@link Callable}.
* @param
* the function's return type
* @param call
* a function returning the value to be used to resolve the returned {@link Promise}
* @param executor
* the executor to use for asynchronous execution
* @return
* the new {@link Promise}
*/
public static Promise submit(Callable call, Executor executor) {
CompletableTask result = new CompletableTask<>(executor, call);
executor.execute(result);
return result;
}
public static Promise waitFor(CompletionStage stage, Executor executor) {
return waitFor(stage, executor, false);
}
public static Promise waitFor(CompletionStage stage, Executor executor, boolean dependentStage) {
return asyncOn(executor)
.dependent()
.thenCombine(stage, selectSecond(), enlistParamOrNone(dependentStage))
.raw();
}
public static Promise delay(long timeout, TimeUnit unit, Executor executor) {
return delay(Timeouts.toDuration(timeout, unit), executor);
}
public static Promise delay(Duration duration, Executor executor) {
return asyncOn(executor)
.dependent()
.thenCombineAsync(Timeouts.delay(duration), selectSecond(), enlistParamOrNone(true))
.raw();
}
@Override
void fireTransition(Callable code) {
throw new UnsupportedOperationException();
}
/**
* Creates a nested sub-task for a composition method
* @param executor
* a default executor for async composition methods of nested sub-task
* @return
* an instance of {@link CompletableSubTask} bound to the specified executor
*/
@Override
protected AbstractCompletableTask createCompletionStage(Executor executor) {
return new CompletableSubTask(executor);
}
private static Set enlistParamOrNone(boolean enlistParam) {
return enlistParam ? PromiseOrigin.PARAM_ONLY : PromiseOrigin.NONE;
}
}