All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.tascalate.concurrent.CompletableTask Maven / Gradle / Ivy

Go to download

Implementation of blocking (IO-Bound) cancellable java.util.concurrent.CompletionStage and related extensions to java.util.concurrent.ExecutorService-s

There is a newer version: 0.9.8
Show newest version
/**
 * 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; } }